mysystem-cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/AGENTS.md +66 -0
  2. package/copy-templates.js +47 -0
  3. package/dist/commands/init.js +15 -2
  4. package/dist/utils/detector.js +84 -32
  5. package/package.json +2 -2
  6. package/src/commands/init.ts +15 -2
  7. package/src/utils/detector.ts +86 -29
  8. package/templates/docker/fastapi.Dockerfile +32 -0
  9. package/templates/docker/nextjs.Dockerfile +45 -0
  10. package/templates/docker/node.Dockerfile +32 -0
  11. package/templates/docker/react.Dockerfile +38 -0
  12. package/templates/github/deploy-ec2.yml +163 -0
  13. package/templates/github/deploy.yml +94 -0
  14. package/templates/github/destroy.yml +43 -0
  15. package/templates/terraform/alb.tf +69 -0
  16. package/templates/terraform/bootstrap-oidc.yaml +69 -0
  17. package/templates/terraform/budget.tf +40 -0
  18. package/templates/terraform/db.tf +46 -0
  19. package/templates/terraform/dns.tf +89 -0
  20. package/templates/terraform/ecs.tf +110 -0
  21. package/templates/terraform/outputs.tf +14 -0
  22. package/templates/terraform/provider.tf +21 -0
  23. package/templates/terraform/rds_proxy.tf +157 -0
  24. package/templates/terraform/redis.tf +69 -0
  25. package/templates/terraform/security.tf +156 -0
  26. package/templates/terraform/variables.tf +46 -0
  27. package/templates/terraform/vpc.tf +68 -0
  28. package/templates/terraform/waf.tf +97 -0
  29. package/templates/terraform-ec2/budget.tf +40 -0
  30. package/templates/terraform-ec2/dns.tf +85 -0
  31. package/templates/terraform-ec2/ec2.tf +124 -0
  32. package/templates/terraform-ec2/ecr.tf +10 -0
  33. package/templates/terraform-ec2/outputs.tf +19 -0
  34. package/templates/terraform-ec2/provider.tf +22 -0
  35. package/templates/terraform-ec2/variables.tf +58 -0
  36. package/templates/terraform-ec2/vpc.tf +50 -0
@@ -0,0 +1,163 @@
1
+ name: MySystem EC2 Deployment
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+
9
+ permissions:
10
+ id-token: write
11
+ contents: read
12
+
13
+ jobs:
14
+ deploy:
15
+ name: Deploy to AWS EC2 (Hobbyist Tier)
16
+ runs-on: ubuntu-latest
17
+
18
+ steps:
19
+ - name: Checkout Code
20
+ uses: actions/checkout@v4
21
+
22
+ # --- Step 1: Parse MySystem Config File ---
23
+ - name: Parse MySystem Config
24
+ id: config
25
+ run: |
26
+ echo "APP_NAME=$(jq -r .name mysystem.json)" >> $GITHUB_ENV
27
+ echo "AWS_REGION=$(jq -r .region mysystem.json)" >> $GITHUB_ENV
28
+ echo "CONTAINER_PORT=$(jq -r .port mysystem.json)" >> $GITHUB_ENV
29
+ echo "BILLING_EMAIL=$(jq -r .billingEmail mysystem.json)" >> $GITHUB_ENV
30
+ echo "ENABLE_CUSTOM_DOMAIN=$(jq -r .customDomain mysystem.json)" >> $GITHUB_ENV
31
+ echo "DOMAIN_NAME=$(jq -r .domainName mysystem.json)" >> $GITHUB_ENV
32
+ echo "DNS_PROVIDER=$(jq -r .dnsProvider mysystem.json)" >> $GITHUB_ENV
33
+
34
+ # --- Step 2: Authenticate with AWS via OIDC ---
35
+ - name: Configure AWS Credentials
36
+ uses: aws-actions/configure-aws-credentials@v4
37
+ with:
38
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
39
+ aws-region: ${{ env.AWS_REGION }}
40
+ audience: sts.amazonaws.com
41
+
42
+ # --- Step 3: Provision EC2 & ECR via Terraform ---
43
+ - name: Setup Terraform
44
+ uses: hashicorp/setup-terraform@v3
45
+ with:
46
+ terraform_version: 1.6.0
47
+
48
+ - name: Terraform Init & Apply
49
+ working-directory: ./terraform
50
+ env:
51
+ TF_VAR_aws_region: ${{ env.AWS_REGION }}
52
+ TF_VAR_app_name: ${{ env.APP_NAME }}
53
+ TF_VAR_container_port: ${{ env.CONTAINER_PORT }}
54
+ TF_VAR_billing_email: ${{ env.BILLING_EMAIL }}
55
+ TF_VAR_enable_custom_domain: ${{ env.ENABLE_CUSTOM_DOMAIN }}
56
+ TF_VAR_domain_name: ${{ env.DOMAIN_NAME }}
57
+ TF_VAR_dns_provider: ${{ env.DNS_PROVIDER }}
58
+ run: |
59
+ terraform init
60
+ terraform apply -auto-approve
61
+
62
+ # --- Step 4: Extract Terraform Outputs ---
63
+ - name: Get Outputs
64
+ id: tf_outputs
65
+ working-directory: ./terraform
66
+ run: |
67
+ echo "ECR_REPO=$(terraform output -raw ecr_repository_url)" >> $GITHUB_OUTPUT
68
+ echo "INSTANCE_ID=$(terraform output -raw instance_id)" >> $GITHUB_OUTPUT
69
+ echo "APP_URL=$(terraform output -raw application_url)" >> $GITHUB_OUTPUT
70
+
71
+ # --- Step 5: Login to Amazon ECR ---
72
+ - name: Login to Amazon ECR
73
+ id: login-ecr
74
+ uses: aws-actions/amazon-ecr-login@v2
75
+
76
+ # --- Step 6: Build and Push Docker Image ---
77
+ - name: Build, Tag, and Push Image to ECR
78
+ id: build-image
79
+ env:
80
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
81
+ ECR_REPOSITORY: ${{ steps.tf_outputs.outputs.ECR_REPO }}
82
+ IMAGE_TAG: ${{ github.sha }}
83
+ run: |
84
+ docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
85
+ docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
86
+ echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
87
+
88
+ # --- Step 7: Deploy to EC2 via AWS SSM ---
89
+ - name: Deploy to EC2 via AWS SSM
90
+ env:
91
+ INSTANCE_ID: ${{ steps.tf_outputs.outputs.INSTANCE_ID }}
92
+ IMAGE_URL: ${{ steps.build-image.outputs.image }}
93
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
94
+ AWS_DEFAULT_REGION: ${{ env.AWS_REGION }}
95
+ CONTAINER_PORT: ${{ env.CONTAINER_PORT }}
96
+ SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
97
+ run: |
98
+ # Generate a secure password for Postgres
99
+ DB_PASSWORD=$(openssl rand -base64 12)
100
+
101
+ # Write the deployment script
102
+ cat <<EOF > deploy.sh
103
+ #!/bin/bash
104
+ # 1. Login to ECR
105
+ aws ecr get-login-password --region ${AWS_DEFAULT_REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY}
106
+
107
+ # 2. Create app directory
108
+ mkdir -p /home/ec2-user/app
109
+
110
+ # 3. Create docker-compose.yml
111
+ cat <<'COMPOSE' > /home/ec2-user/app/docker-compose.yml
112
+ version: '3.8'
113
+ services:
114
+ web:
115
+ image: ${IMAGE_URL}
116
+ restart: always
117
+ ports:
118
+ - "80:${CONTAINER_PORT}"
119
+ environment:
120
+ - PORT=${CONTAINER_PORT}
121
+ - NODE_ENV=production
122
+ - DATABASE_URL=postgresql://mysystem_admin:${DB_PASSWORD}@db:5432/mysystem_db
123
+ - SENTRY_DSN=${SENTRY_DSN}
124
+ depends_on:
125
+ - db
126
+
127
+ db:
128
+ image: postgres:15-alpine
129
+ restart: always
130
+ environment:
131
+ - POSTGRES_USER=mysystem_admin
132
+ - POSTGRES_PASSWORD=${DB_PASSWORD}
133
+ - POSTGRES_DB=mysystem_db
134
+ volumes:
135
+ - pgdata:/var/lib/postgresql/data
136
+
137
+ volumes:
138
+ pgdata:
139
+ COMPOSE
140
+
141
+ # 4. Pull and run containers
142
+ cd /home/ec2-user/app
143
+ docker compose pull
144
+ docker compose up -d
145
+ EOF
146
+
147
+ # Base64 encode the script to avoid all command-line escaping issues
148
+ B64_SCRIPT=\$(base64 -w 0 deploy.sh)
149
+
150
+ # Send run command to AWS SSM
151
+ CMD_ID=\$(aws ssm send-command \
152
+ --instance-ids "${INSTANCE_ID}" \
153
+ --document-name "AWS-RunShellScript" \
154
+ --parameters "commands=[\"echo '\$B64_SCRIPT' | base64 -d > /tmp/deploy.sh\", \"chmod +x /tmp/deploy.sh\", \"/tmp/deploy.sh\"]" \
155
+ --query "Command.CommandId" \
156
+ --region "${AWS_DEFAULT_REGION}" \
157
+ --output text)
158
+
159
+ echo "Deployment initiated via SSM. Command ID: \$CMD_ID"
160
+
161
+ - name: Show App URL
162
+ run: |
163
+ echo "🎉 App successfully deployed to: ${{ steps.tf_outputs.outputs.APP_URL }}"
@@ -0,0 +1,94 @@
1
+ name: MySystem Deployment
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+
9
+ permissions:
10
+ id-token: write # Required for AWS OIDC authentication
11
+ contents: read
12
+
13
+ jobs:
14
+ deploy:
15
+ name: Deploy to AWS Fargate
16
+ runs-on: ubuntu-latest
17
+
18
+ steps:
19
+ - name: Checkout Code
20
+ uses: actions/checkout@v4
21
+
22
+ # --- Step 1: Authenticate with AWS via OIDC ---
23
+ - name: Configure AWS Credentials
24
+ uses: aws-actions/configure-aws-credentials@v4
25
+ with:
26
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
27
+ aws-region: us-east-1 # Will be customized by CLI on setup
28
+ audience: sts.amazonaws.com
29
+
30
+ # --- Step 2: Provision Infrastructure using Terraform ---
31
+ - name: Setup Terraform
32
+ uses: hashicorp/setup-terraform@v3
33
+ with:
34
+ terraform_version: 1.6.0
35
+
36
+ - name: Terraform Init & Apply
37
+ working-directory: ./terraform
38
+ run: |
39
+ terraform init
40
+ terraform apply -auto-approve
41
+
42
+ # --- Step 3: Extract Terraform Outputs ---
43
+ - name: Get Outputs
44
+ id: tf_outputs
45
+ working-directory: ./terraform
46
+ run: |
47
+ echo "ECR_REPO=$(terraform output -raw ecr_repository_url)" >> $GITHUB_OUTPUT
48
+ echo "APP_URL=$(terraform output -raw application_url)" >> $GITHUB_OUTPUT
49
+
50
+ # --- Step 4: Login to Amazon ECR ---
51
+ - name: Login to Amazon ECR
52
+ id: login-ecr
53
+ uses: aws-actions/amazon-ecr-login@v2
54
+
55
+ # --- Step 5: Build and Push Docker Image ---
56
+ - name: Build, Tag, and Push Image to ECR
57
+ id: build-image
58
+ env:
59
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
60
+ ECR_REPOSITORY: ${{ steps.tf_outputs.outputs.ECR_REPO }}
61
+ IMAGE_TAG: ${{ github.sha }}
62
+ run: |
63
+ # Build a local Docker image
64
+ docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
65
+ # Push it to ECR
66
+ docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
67
+ # Save image URL as output
68
+ echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
69
+
70
+ # --- Step 6: Deploy to ECS Fargate ---
71
+ # This pulls down the task definition template, updates it with the new image, and deploys it.
72
+ - name: Download Task Definition
73
+ run: |
74
+ aws ecs describe-task-definition --task-definition ${{ github.event.repository.name }} --query taskDefinition > task-definition.json
75
+
76
+ - name: Fill in the new image ID in the Amazon ECS task definition
77
+ id: render-task-def
78
+ uses: aws-actions/amazon-ecs-render-task-definition@v1
79
+ with:
80
+ task-definition: task-definition.json
81
+ container-name: ${{ github.event.repository.name }}
82
+ image: ${{ steps.build-image.outputs.image }}
83
+
84
+ - name: Deploy Amazon ECS task definition
85
+ uses: aws-actions/amazon-ecs-deploy-task-definition@v2
86
+ with:
87
+ task-definition: ${{ steps.render-task-def.outputs.task-definition }}
88
+ service: ${{ github.event.repository.name }}
89
+ cluster: ${{ github.event.repository.name }}-cluster
90
+ wait-for-service-stability: true
91
+
92
+ - name: Show App URL
93
+ run: |
94
+ echo "🎉 App successfully deployed to: ${{ steps.tf_outputs.outputs.APP_URL }}"
@@ -0,0 +1,43 @@
1
+ name: MySystem Teardown (Destroy)
2
+
3
+ on:
4
+ workflow_dispatch: # Allows manual trigger from the GitHub Actions UI
5
+
6
+ permissions:
7
+ id-token: write
8
+ contents: read
9
+
10
+ jobs:
11
+ destroy:
12
+ name: Teardown AWS Infrastructure
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout Code
17
+ uses: actions/checkout@v4
18
+
19
+ # --- Step 1: Authenticate with AWS via OIDC ---
20
+ - name: Configure AWS Credentials
21
+ uses: aws-actions/configure-aws-credentials@v4
22
+ with:
23
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
24
+ aws-region: us-east-1 # Will be customized by CLI on setup
25
+ audience: sts.amazonaws.com
26
+
27
+ # --- Step 2: Set up Terraform ---
28
+ - name: Setup Terraform
29
+ uses: hashicorp/setup-terraform@v3
30
+ with:
31
+ terraform_version: 1.6.0
32
+
33
+ # --- Step 3: Run Terraform Destroy ---
34
+ - name: Terraform Init & Destroy
35
+ working-directory: ./terraform
36
+ run: |
37
+ terraform init
38
+ terraform destroy -auto-approve
39
+
40
+ - name: Success Message
41
+ run: |
42
+ echo "🔥 All AWS resources for this application have been successfully destroyed."
43
+ echo "Your AWS billing for this project has been stopped."
@@ -0,0 +1,69 @@
1
+ resource "aws_lb" "main" {
2
+ name = "${var.app_name}-alb"
3
+ internal = false
4
+ load_balancer_type = "application"
5
+ security_groups = [aws_security_group.alb.id]
6
+ subnets = aws_subnet.public[*].id
7
+
8
+ tags = {
9
+ Name = "${var.app_name}-alb"
10
+ }
11
+ }
12
+
13
+ resource "aws_lb_target_group" "app" {
14
+ name = "${var.app_name}-tg"
15
+ port = var.container_port
16
+ protocol = "HTTP"
17
+ vpc_id = aws_vpc.main.id
18
+ target_type = "ip"
19
+
20
+ health_check {
21
+ healthy_threshold = 3
22
+ unhealthy_threshold = 3
23
+ timeout = 5
24
+ interval = 30
25
+ path = "/health" # Compliant with AGENTS.md rule
26
+ protocol = "HTTP"
27
+ matcher = "200"
28
+ }
29
+
30
+ tags = {
31
+ Name = "${var.app_name}-tg"
32
+ }
33
+ }
34
+
35
+ # HTTP Listener (Port 80)
36
+ resource "aws_lb_listener" "http" {
37
+ load_balancer_arn = aws_lb.main.arn
38
+ port = "80"
39
+ protocol = "HTTP"
40
+
41
+ default_action {
42
+ type = var.enable_custom_domain ? "redirect" : "forward"
43
+ target_group_arn = var.enable_custom_domain ? null : aws_lb_target_group.app.arn
44
+
45
+ dynamic "redirect" {
46
+ for_each = var.enable_custom_domain ? [1] : []
47
+ content {
48
+ port = "443"
49
+ protocol = "HTTPS"
50
+ status_code = "HTTP_301"
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ # HTTPS Listener (Port 443) - Created only if custom domain is enabled
57
+ resource "aws_lb_listener" "https" {
58
+ count = var.enable_custom_domain ? 1 : 0
59
+ load_balancer_arn = aws_lb.main.arn
60
+ port = "443"
61
+ protocol = "HTTPS"
62
+ ssl_policy = "ELBSecurityPolicy-2016-08"
63
+ certificate_arn = aws_acm_certificate.cert[0].arn
64
+
65
+ default_action {
66
+ type = "forward"
67
+ target_group_arn = aws_lb_target_group.app.arn
68
+ }
69
+ }
@@ -0,0 +1,69 @@
1
+ AWSTemplateFormatVersion: '2010-09-09'
2
+ Description: Bootstrap IAM Role for GitHub Actions OIDC connection to AWS (MySystem).
3
+
4
+ Parameters:
5
+ GitHubOrg:
6
+ Type: String
7
+ Description: Your GitHub Organization or Username (case-sensitive)
8
+ GitHubRepo:
9
+ Type: String
10
+ Description: Your GitHub Repository Name (case-sensitive)
11
+
12
+ Resources:
13
+ # OIDC Provider for GitHub (Created only if it doesn't exist, we assume it's created or we create it here)
14
+ # Note: Since there can only be one OIDC provider per account, we handle it. If it fails, users can use an existing one.
15
+ GithubOidcProvider:
16
+ Type: AWS::IAM::OIDCProvider
17
+ Properties:
18
+ Url: https://token.actions.githubusercontent.com
19
+ ClientIdList:
20
+ - sts.amazonaws.com
21
+ ThumbprintList:
22
+ - 6938fd4d98bab03faadb97b34396831e3780aea1 # GitHub Actions Thumbprint
23
+
24
+ # IAM Role that GitHub Actions will assume to deploy the app
25
+ MySystemDeployRole:
26
+ Type: AWS::IAM::Role
27
+ Properties:
28
+ RoleName: !Sub 'MySystemDeployRole-${GitHubRepo}'
29
+ Description: IAM Role assumed by GitHub Actions to deploy infrastructure and code.
30
+ AssumeRolePolicyDocument:
31
+ Version: '2012-10-17'
32
+ Statement:
33
+ - Effect: Allow
34
+ Principal:
35
+ Federated: !Ref GithubOidcProvider
36
+ Action: sts:AssumeRoleWithWebIdentity
37
+ Condition:
38
+ StringEquals:
39
+ token.actions.githubusercontent.com:aud: sts.amazonaws.com
40
+ StringLike:
41
+ token.actions.githubusercontent.com:sub: !Sub 'repo:${GitHubOrg}/${GitHubRepo}:*'
42
+ # Attach Policies to allow Terraform to provision VPC, ECS, RDS, ALB, ECR etc.
43
+ ManagedPolicyArns:
44
+ - arn:aws:iam::aws:policy/PowerUserAccess
45
+ Policies:
46
+ - PolicyName: MySystemAdditionalIAMPolicy
47
+ PolicyDocument:
48
+ Version: '2012-10-17'
49
+ Statement:
50
+ # PowerUserAccess does not allow managing IAM roles. Terraform needs to create ECS Task Execution Roles.
51
+ - Effect: Allow
52
+ Action:
53
+ - iam:CreateRole
54
+ - iam:DeleteRole
55
+ - iam:GetRole
56
+ - iam:PassRole
57
+ - iam:PutRolePolicy
58
+ - iam:DeleteRolePolicy
59
+ - iam:GetRolePolicy
60
+ - iam:AttachRolePolicy
61
+ - iam:DetachRolePolicy
62
+ Resource:
63
+ - !Sub 'arn:aws:iam::${AWS::AccountId}:role/*-ecs-execution-role'
64
+ - !Sub 'arn:aws:iam::${AWS::AccountId}:role/*-ecs-task-role'
65
+
66
+ Outputs:
67
+ RoleARN:
68
+ Description: The ARN of the IAM Role for GitHub Actions. Copy this to your GitHub Repository Secret `AWS_ROLE_ARN`.
69
+ Value: !GetAtt MySystemDeployRole.Arn
@@ -0,0 +1,40 @@
1
+ # AWS Cost Budget Alert
2
+ # Sends an email notification if the forecasted or actual AWS monthly spend exceeds the specified threshold.
3
+
4
+ variable "billing_email" {
5
+ type = string
6
+ description = "Email address to send AWS budget and billing alerts"
7
+ default = ""
8
+ }
9
+
10
+ variable "budget_limit" {
11
+ type = string
12
+ description = "Monthly budget limit in USD"
13
+ default = "20"
14
+ }
15
+
16
+ resource "aws_budgets_budget" "monthly_cost" {
17
+ count = var.billing_email != "" ? 1 : 0
18
+ name = "${var.app_name}-monthly-budget"
19
+ budget_type = "COST"
20
+ limit_amount = var.budget_limit
21
+ limit_unit = "USD"
22
+ time_period_start = "2026-01-01_00:00" # Arbitrary start date in the past/present
23
+ time_unit = "MONTHLY"
24
+
25
+ notification {
26
+ comparison_operator = "GREATER_THAN"
27
+ threshold = 80
28
+ threshold_type = "PERCENTAGE"
29
+ notification_type = "ACTUAL"
30
+ subscriber_email_addresses = [var.billing_email]
31
+ }
32
+
33
+ notification {
34
+ comparison_operator = "GREATER_THAN"
35
+ threshold = 100
36
+ threshold_type = "PERCENTAGE"
37
+ notification_type = "FORECASTED"
38
+ subscriber_email_addresses = [var.billing_email]
39
+ }
40
+ }
@@ -0,0 +1,46 @@
1
+ resource "random_password" "db_password" {
2
+ count = var.enable_database ? 1 : 0
3
+ length = 16
4
+ special = false
5
+ }
6
+
7
+ resource "aws_db_subnet_group" "main" {
8
+ count = var.enable_database ? 1 : 0
9
+ name = "${var.app_name}-db-subnet-group"
10
+ subnet_ids = aws_subnet.private[*].id
11
+
12
+ tags = {
13
+ Name = "${var.app_name}-db-subnet-group"
14
+ }
15
+ }
16
+
17
+ resource "aws_db_instance" "postgres" {
18
+ count = var.enable_database ? 1 : 0
19
+ identifier = "${var.app_name}-db"
20
+ allocated_storage = 20
21
+ max_allocated_storage = 100
22
+ engine = "postgres"
23
+ engine_version = "15"
24
+ instance_class = "db.t4g.micro" # cost-effective burstable Graviton instance
25
+ db_name = replace(var.app_name, "-", "_")
26
+ username = "mysystem_admin"
27
+ password = random_password.db_password[0].result
28
+ db_subnet_group_name = aws_db_subnet_group.main[0].name
29
+ vpc_security_group_ids = [aws_security_group.db[0].id]
30
+ skip_final_snapshot = true
31
+ publicly_accessible = false
32
+
33
+ tags = {
34
+ Name = "${var.app_name}-db-postgres"
35
+ }
36
+ }
37
+
38
+ # Save password in SSM Parameter Store so the developer can retrieve it if needed,
39
+ # and so ECS tasks can fetch it securely.
40
+ resource "aws_ssm_parameter" "db_url" {
41
+ count = var.enable_database ? 1 : 0
42
+ name = "/${var.app_name}/database_url"
43
+ type = "SecureString"
44
+ description = "Database URL for the application"
45
+ value = "postgresql://${aws_db_instance.postgres[0].username}:${random_password.db_password[0].result}@${var.enable_rds_proxy ? aws_db_proxy.postgres[0].endpoint : aws_db_instance.postgres[0].endpoint}/${aws_db_instance.postgres[0].db_name}"
46
+ }
@@ -0,0 +1,89 @@
1
+ # AWS ACM (SSL Certificate) and Custom Domain Routing
2
+ # Supports automated Route 53 setups or external DNS providers (GoDaddy, Cloudflare, Namecheap)
3
+
4
+ variable "enable_custom_domain" {
5
+ type = bool
6
+ description = "Enable custom domain and HTTPS SSL certificate"
7
+ default = false
8
+ }
9
+
10
+ variable "domain_name" {
11
+ type = string
12
+ description = "The custom domain name (e.g., app.example.com)"
13
+ default = ""
14
+ }
15
+
16
+ variable "dns_provider" {
17
+ type = string
18
+ description = "DNS provider for domain validation ('route53' or 'external')"
19
+ default = "external"
20
+ }
21
+
22
+ # 1. SSL/TLS Certificate via ACM
23
+ resource "aws_acm_certificate" "cert" {
24
+ count = var.enable_custom_domain ? 1 : 0
25
+ domain_name = var.domain_name
26
+ validation_method = "DNS"
27
+
28
+ lifecycle {
29
+ create_before_destroy = true
30
+ }
31
+
32
+ tags = {
33
+ Name = "${var.app_name}-cert"
34
+ }
35
+ }
36
+
37
+ # --- Route 53 DNS Configuration (Automated Setup) ---
38
+ data "aws_route53_zone" "primary" {
39
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
40
+ name = join(".", slice(split(".", var.domain_name), length(split(".", var.domain_name)) - 2, length(split(".", var.domain_name))))
41
+ private_zone = false
42
+ }
43
+
44
+ # Create DNS validation record in Route53
45
+ resource "aws_route53_record" "cert_validation" {
46
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
47
+ name = tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_name
48
+ type = tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_type
49
+ zone_id = data.aws_route53_zone.primary[0].zone_id
50
+ records = [tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_value]
51
+ ttl = 60
52
+ }
53
+
54
+ # Validate certificate in ACM (Wait for validation to complete)
55
+ resource "aws_acm_certificate_validation" "cert" {
56
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
57
+ certificate_arn = aws_acm_certificate.cert[0].arn
58
+ validation_record_fqdns = [aws_route53_record.cert_validation[0].fqdn]
59
+ }
60
+
61
+ # Create A record in Route53 pointing to the Load Balancer
62
+ resource "aws_route53_record" "app" {
63
+ count = var.enable_custom_domain && var.dns_provider == "route53" ? 1 : 0
64
+ zone_id = data.aws_route53_zone.primary[0].zone_id
65
+ name = var.domain_name
66
+ type = "A"
67
+
68
+ alias {
69
+ name = aws_lb.main.dns_name
70
+ zone_id = aws_lb.main.zone_id
71
+ evaluate_target_health = true
72
+ }
73
+ }
74
+
75
+ # --- Outputs for External DNS Setup (GoDaddy, Cloudflare, etc.) ---
76
+ output "dns_validation_name" {
77
+ value = var.enable_custom_domain ? tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_name : "None"
78
+ description = "CNAME Name to add to your DNS provider for SSL certificate validation"
79
+ }
80
+
81
+ output "dns_validation_value" {
82
+ value = var.enable_custom_domain ? tolist(aws_acm_certificate.cert[0].domain_validation_options)[0].resource_record_value : "None"
83
+ description = "CNAME Value/Alias to add to your DNS provider for SSL certificate validation"
84
+ }
85
+
86
+ output "app_cname_alias" {
87
+ value = aws_lb.main.dns_name
88
+ description = "Point your domain CNAME record to this target to route traffic to the app"
89
+ }