From 60ad05e3eb9afe03f82f26096aa72a2ad46c6f27 Mon Sep 17 00:00:00 2001 From: rahul-infra Date: Tue, 25 Nov 2025 16:40:13 +0530 Subject: [PATCH 1/4] feat!: updated providers in acm module. formatted terraform file. terraform-docs: automated action Updated examples for same account and cross account. Added line enter. Updated readme for same account and cross account examplefile. terraform-docs: automated action updated root readme file. --- README.md | 5 +- examples/cross-account/.header.md | 67 +++++++ examples/cross-account/README.md | 170 ++++++++++++++++ examples/cross-account/main.tf | 181 ++++++++++++++++++ .../{complete => cross-account}/outputs.tf | 0 .../{complete => cross-account}/variables.tf | 2 + .../{complete => cross-account}/versions.tf | 0 .../{complete => same-account}/.header.md | 0 examples/{complete => same-account}/README.md | 5 + examples/{complete => same-account}/main.tf | 10 +- examples/same-account/outputs.tf | 79 ++++++++ examples/same-account/variables.tf | 140 ++++++++++++++ examples/same-account/versions.tf | 3 + main.tf | 16 -- modules/acm/README.md | 5 +- modules/acm/providers.tf | 4 +- variables.tf | 6 - versions.tf | 4 + 18 files changed, 664 insertions(+), 33 deletions(-) create mode 100644 examples/cross-account/.header.md create mode 100644 examples/cross-account/README.md create mode 100644 examples/cross-account/main.tf rename examples/{complete => cross-account}/outputs.tf (100%) rename examples/{complete => cross-account}/variables.tf (98%) rename examples/{complete => cross-account}/versions.tf (100%) rename examples/{complete => same-account}/.header.md (100%) rename examples/{complete => same-account}/README.md (97%) rename examples/{complete => same-account}/main.tf (96%) create mode 100644 examples/same-account/outputs.tf create mode 100644 examples/same-account/variables.tf create mode 100644 examples/same-account/versions.tf diff --git a/README.md b/README.md index ffc3d57..4791105 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ # terraform-aws-ecs-deployment Terraform module to deploy production-ready applications and services on an existing ECS infra. +This module supports both same-account and cross-account ACM → Route53 validation workflows. For same-account usage, simply map the providers as providers = { aws = aws, aws.cross_account_provider = aws } without any assume role. +For cross-account setups, you must create an IAM Role in the Route53 Hosted Zone account (Account B) that allows Account A (where ACM and application resources are created) to assume it. This role should grant permissions such as route53:ChangeResourceRecordSets, route53:ListHostedZonesByName, and route53:ListResourceRecordSets, along with a trust policy that permits Account A to assume the role. +When using cross-account mode, configure an alias provider with assume_role and pass it to the module as: +providers = { aws = aws, aws.cross_account_provider = aws.cross_account_provider }. ## Architecture Diagram @@ -51,7 +55,6 @@ Terraform module to deploy production-ready applications and services on an exis | [create\_s3\_bucket\_for\_alb\_logging](#input\_create\_s3\_bucket\_for\_alb\_logging) | (Optional) Creates S3 bucket for storing ALB Access and Connection Logs. | `bool` | `true` | no | | [default\_capacity\_providers\_strategies](#input\_default\_capacity\_providers\_strategies) | (Optional) Set of capacity provider strategies to use by default for the cluster. | `any` | `[]` | no | | [load\_balancer](#input\_load\_balancer) | Configuration for the Application Load Balancer. |
object({
name = optional(string)
internal = optional(bool, false)
subnets_ids = optional(list(string), [])
security_groups_ids = optional(list(string), [])
preserve_host_header = optional(bool)
enable_deletion_protection = optional(bool, false)
access_logs = optional(any, null)
connection_logs = optional(any, null)
target_groups = optional(any, {})
listeners = optional(any, {})
listener_rules = optional(any, {})
tags = optional(map(string), {})
})
| `{}` | no | -| [region](#input\_region) | (Optional) AWS region to create resources in. | `string` | `null` | no | | [route53\_assume\_role\_arn](#input\_route53\_assume\_role\_arn) | (Optional) ARN of the role to assume for Route53 operations. | `string` | `null` | no | | [s3\_bucket\_force\_destroy](#input\_s3\_bucket\_force\_destroy) | (Optional, Default:false) Boolean that indicates all objects (including any locked objects) should be deleted from the bucket when the bucket is destroyed so that the bucket can be destroyed without error. | `bool` | `false` | no | | [s3\_bucket\_name](#input\_s3\_bucket\_name) | (Optional, Forces new resource) Name of the bucket. | `string` | `null` | no | diff --git a/examples/cross-account/.header.md b/examples/cross-account/.header.md new file mode 100644 index 0000000..22e56fb --- /dev/null +++ b/examples/cross-account/.header.md @@ -0,0 +1,67 @@ +# ECS Deployment Complete + +Configuration in this directory creates: + +- ECS Service in a pre-configured ECS Cluster and corresponding ECS Capacity Providers +- Internet-facing Application Load Balancer to access the deployed services with S3 bucket for storing access and connection logs, and +- ACM to generate and validate an Amazon-issued certificate for a base domain + +## Example `tfvars` Configuration + +```tf +vpc_id = "vpc-0123456789abcdefg" +service_network_configuration_security_groups = ["sg-0123456789abcdefg"] +private_subnets = ["subnet-0123456789abcdefg", "subnet-0123456789abcdefg"] +public_subnets = ["subnet-0123456789abcdefg", "subnet-0123456789abcdefg"] +cluster_name = "your-cluster-name" +container_name = "your-container-name" +service_desired_count = 123 +container_image = "your-container-image:version" +container_port = 123 +container_cpu = 123 +container_memory = 123 +container_essential = true +container_port_mappings = [ + { + name = "your-port-mapping-name" + containerPort = 123 + hostPort = 123 + protocol = "your-port-mapping-protocol" + } +] +container_readonly_root_filesystem = false +asg_arn = "arn:aws:autoscaling:your-region:01234567890:autoScalingGroup:abcdefgh-ijkl-mnop-qrst-uvwxyz012345:autoScalingGroupName/your-autoscaling-group-name" +capacity_provider_name = "your-capacity-provider-name" +capacity_provider_managed_scaling = { + status = "ENABLED" + target_capacity = 123 + minimum_scaling_step_size = 123 + maximum_scaling_step_size = 123 +} +alb_name = "your-alb-name" +target_group_name = "your-alb-target-group-name" +target_group_protocol = "HTTP" +target_group_health_check = { + path = "/path/to/health/check" +} +listener_port = 123 +ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" +security_group_alb = "your-alb-sg-name" +s3_bucket_force_destroy = true +base_domain = "example.com" +domain_name = "your-service.example.com" +region = "us-east-1" +route53_assume_role_arn = "arn:aws:iam::123456789012:role/Route53CrossAccountRole" +``` + +## Usage + +To run this example, you will need to execute the commands: + +```bash +terraform init +terraform plan +terraform apply +``` + +Please note that this example may create resources that can incur monetary charges on your AWS bill. You can run `terraform destroy` when you no longer need the resources. diff --git a/examples/cross-account/README.md b/examples/cross-account/README.md new file mode 100644 index 0000000..4a809d3 --- /dev/null +++ b/examples/cross-account/README.md @@ -0,0 +1,170 @@ + +# ECS Deployment Complete + + + +Configuration in this directory creates: + +- ECS Service in a pre-configured ECS Cluster and corresponding ECS Capacity Providers +- Internet-facing Application Load Balancer to access the deployed services with S3 bucket for storing access and connection logs, and +- ACM to generate and validate an Amazon-issued certificate for a base domain + + +For cross-account Route53 validation, configure the alias provider with assume_role and pass it to the module as: +providers = { aws = aws, aws.cross_account_provider = aws.cross_account_provider }. +Ensure the cross_account_provider includes the assume_role block pointing to the Route53 account role. +provider "aws" { alias = "cross_account_provider" ... assume_role { role_arn = "" } } + +## Prerequisites + +**Create an IAM Role in the Hosted Zone Account (Account B)** +If you want to validate ACM certificates across accounts, ensure that an IAM role exists in Account B (Route53 Hosted Zone account) that grants cross-account access to manage Route53 DNS records. +This role must allow Account A (where ACM and your application resources are created) to assume it. +It should include permissions such as: + +route53:ChangeResourceRecordSets +route53:ListHostedZonesByName +route53:ListResourceRecordSets + +And a trust policy allowing Account A to assume the role. + + +## Example `tfvars` Configuration + +```tf +vpc_id = "vpc-0123456789abcdefg" +service_network_configuration_security_groups = ["sg-0123456789abcdefg"] +private_subnets = ["subnet-0123456789abcdefg", "subnet-0123456789abcdefg"] +public_subnets = ["subnet-0123456789abcdefg", "subnet-0123456789abcdefg"] + +cluster_name = "your-cluster-name" +container_name = "your-container-name" +service_desired_count = 123 +container_image = "your-container-image:version" +container_port = 123 +container_cpu = 123 +container_memory = 123 +container_essential = true +container_port_mappings = [ + { + name = "your-port-mapping-name" + containerPort = 123 + hostPort = 123 + protocol = "your-port-mapping-protocol" + } +] +container_readonly_root_filesystem = false + +asg_arn = "arn:aws:autoscaling:your-region:01234567890:autoScalingGroup:abcdefgh-ijkl-mnop-qrst-uvwxyz012345:autoScalingGroupName/your-autoscaling-group-name" +capacity_provider_name = "your-capacity-provider-name" +capacity_provider_managed_scaling = { + status = "ENABLED" + target_capacity = 123 + minimum_scaling_step_size = 123 + maximum_scaling_step_size = 123 +} + +alb_name = "your-alb-name" +target_group_name = "your-alb-target-group-name" +target_group_protocol = "HTTP" +target_group_health_check = { + path = "/path/to/health/check" +} +listener_port = 123 +ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06" +security_group_alb = "your-alb-sg-name" + +s3_bucket_force_destroy = true + +base_domain = "example.com" +domain_name = "your-service.example.com" +region = "us-east-1" +route53_assume_role_arn = "arn:aws:iam::123456789012:role/Route53CrossAccountRole" +``` + +## Usage + +To run this example, you will need to execute the commands: + +```bash +terraform init +terraform plan +terraform apply +``` + +Please note that this example may create resources that can incur monetary charges on your AWS bill. You can run `terraform destroy` when you no longer need the resources. + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.62.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [ecs\_deployment](#module\_ecs\_deployment) | ../../ | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_security_group.alb_allow_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_route53_zone.base_domain](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [alb\_name](#input\_alb\_name) | Name of the application load balancer | `string` | n/a | yes | +| [asg\_arn](#input\_asg\_arn) | ARN of the Auto Scaling group | `string` | n/a | yes | +| [base\_domain](#input\_base\_domain) | Base domain for ACM | `string` | n/a | yes | +| [capacity\_provider\_managed\_scaling](#input\_capacity\_provider\_managed\_scaling) | Managed scaling configuration for the Capacity Provider | `any` | n/a | yes | +| [capacity\_provider\_name](#input\_capacity\_provider\_name) | Name of the Capacity Provider | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | Name of the ECS cluster | `string` | n/a | yes | +| [container\_cpu](#input\_container\_cpu) | CPU units to allocate to the container | `number` | n/a | yes | +| [container\_essential](#input\_container\_essential) | Essential flag for the container | `bool` | n/a | yes | +| [container\_image](#input\_container\_image) | Image of the container | `string` | n/a | yes | +| [container\_memory](#input\_container\_memory) | Memory in MB to allocate to the container | `number` | n/a | yes | +| [container\_name](#input\_container\_name) | Name of the container | `string` | n/a | yes | +| [container\_port](#input\_container\_port) | Port on which the container will listen | `number` | n/a | yes | +| [container\_port\_mappings](#input\_container\_port\_mappings) | Port mappings for the container | `any` | n/a | yes | +| [container\_readonly\_root\_filesystem](#input\_container\_readonly\_root\_filesystem) | Whether the root filesystem is readonly for the container | `bool` | n/a | yes | +| [domain\_name](#input\_domain\_name) | Domain name for ACM | `string` | n/a | yes | +| [listener\_port](#input\_listener\_port) | Port for the ALB listener | `number` | n/a | yes | +| [private\_subnets](#input\_private\_subnets) | List of private subnet IDs | `list(string)` | n/a | yes | +| [public\_subnets](#input\_public\_subnets) | List of public subnet IDs | `list(string)` | n/a | yes | +| [s3\_bucket\_force\_destroy](#input\_s3\_bucket\_force\_destroy) | (Optional, Default:false) Boolean that indicates all objects (including any locked objects) should be deleted from the bucket when the bucket is destroyed so that the bucket can be destroyed without error. | `bool` | n/a | yes | +| [security\_group\_alb](#input\_security\_group\_alb) | Name of the security group for ALB | `string` | n/a | yes | +| [service\_desired\_count](#input\_service\_desired\_count) | Desired count for the ECS Service | `number` | n/a | yes | +| [service\_network\_configuration\_security\_groups](#input\_service\_network\_configuration\_security\_groups) | Security Groups for the ECS Service's Network Configuration | `list(string)` | n/a | yes | +| [ssl\_policy](#input\_ssl\_policy) | SSL policy for the ALB | `string` | n/a | yes | +| [target\_group\_health\_check](#input\_target\_group\_health\_check) | Health check configuration for the target group | `any` | n/a | yes | +| [target\_group\_name](#input\_target\_group\_name) | Name of the target group | `string` | n/a | yes | +| [target\_group\_protocol](#input\_target\_group\_protocol) | Protocol to use with the target group | `string` | n/a | yes | +| [vpc\_id](#input\_vpc\_id) | VPC ID where the resources will be deployed | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [acm\_amazon\_issued\_certificate\_arn](#output\_acm\_amazon\_issued\_certificate\_arn) | ARN of the ACM Amazon-issued certificate for the base domain | +| [alb\_allow\_all\_sg\_id](#output\_alb\_allow\_all\_sg\_id) | ID of the Security Group for Application Load Balancer to allow all traffic from any source | +| [alb\_arn](#output\_alb\_arn) | ARN of the Application Load Balancer ECS Service | +| [ecs\_capacity\_provider\_arn](#output\_ecs\_capacity\_provider\_arn) | ARN of the ECS Capacity Provider | +| [ecs\_capacity\_provider\_id](#output\_ecs\_capacity\_provider\_id) | Identifier of the ECS Capacity Provider | +| [ecs\_cluster\_capacity\_providers\_id](#output\_ecs\_cluster\_capacity\_providers\_id) | Identifier of the ECS Cluster Capacity Providers | +| [ecs\_service\_arn](#output\_ecs\_service\_arn) | ARN of the ECS Service | +| [listener\_arn](#output\_listener\_arn) | ARN of the ALB Listener forwarding to container instances | +| [listener\_id](#output\_listener\_id) | Identifier of the ALB Listener forwarding to container instances | +| [target\_group\_arn](#output\_target\_group\_arn) | ARN of the Target Group instances | +| [target\_group\_id](#output\_target\_group\_id) | Identifier of the Target Group instances | +| [task\_definition\_arn](#output\_task\_definition\_arn) | ARN of the ECS Task Definition | + diff --git a/examples/cross-account/main.tf b/examples/cross-account/main.tf new file mode 100644 index 0000000..b23f776 --- /dev/null +++ b/examples/cross-account/main.tf @@ -0,0 +1,181 @@ +provider "aws" { + region = var.region +} + +provider "aws" { + alias = "cross_account_provider" + region = var.region + + assume_role { + role_arn = var.route53_assume_role_arn + } +} + +locals { + task_definition_network_mode = "awsvpc" + capacity_provider_default_strategies_weight = 1 + capacity_provider_default_strategies_base = 0 + + acm_validation_method = "DNS" + + alb_internal = false + + target_group_key_name = "default-nginx" + target_group_target_type = "ip" + + listener_default_action_type = "forward" +} + +################################################################################ +# ECS Deployment Module +################################################################################ + +module "ecs_deployment" { + source = "../../" + + providers = { + aws = aws + aws.cross_account_provider = aws.cross_account_provider + } + + cluster_name = var.cluster_name + vpc_id = var.vpc_id + + # ECS + service = { + name = var.container_name + desired_count = var.service_desired_count + force_new_deployment = true + + network_configuration = { + security_groups = var.service_network_configuration_security_groups + subnets = var.private_subnets + } + + load_balancer = [ + { + target_group = local.target_group_key_name + container_name = var.container_name + container_port = var.container_port + } + ] + } + task_definition = { + family = var.container_name + network_mode = local.task_definition_network_mode + + cpu = var.container_cpu + memory = var.container_memory + + container_definitions = [ + { + name = var.container_name + image = var.container_image + cpu = var.container_cpu + memory = var.container_memory + essential = var.container_essential + portMappings = var.container_port_mappings + readonlyRootFilesystem = var.container_readonly_root_filesystem + } + ] + } + + # Capacity Provider + capacity_provider_default_auto_scaling_group_arn = var.asg_arn + capacity_providers = { + capacity_provider = { + name = var.capacity_provider_name + managed_termination_protection = "DISABLED" + managed_scaling = var.capacity_provider_managed_scaling + } + } + default_capacity_providers_strategies = [ + { + capacity_provider = "capacity_provider" + weight = local.capacity_provider_default_strategies_weight + base = local.capacity_provider_default_strategies_base + } + ] + + # Amazon Certificates Manager + create_acm = true + acm_certificates = { + base_domain = { + domain_name = var.domain_name + validation_method = local.acm_validation_method + record_zone_id = data.aws_route53_zone.base_domain.zone_id + } + } + # Cross-account role that ACM module will use for Route53 DNS record creation + route53_assume_role_arn = var.route53_assume_role_arn + + # Application Load Balancer + load_balancer = { + name = var.alb_name + internal = local.alb_internal + security_groups_ids = [aws_security_group.alb_allow_all.id] + subnets_ids = var.public_subnets + + target_groups = { + (local.target_group_key_name) = { + name = var.target_group_name + port = var.container_port + protocol = var.target_group_protocol + target_type = local.target_group_target_type + + health_check = var.target_group_health_check + } + } + + listeners = { + this = { + port = var.listener_port + protocol = "HTTPS" + certificate = "base_domain" + ssl_policy = var.ssl_policy + + default_action = [ + { + type = local.listener_default_action_type + target_group = local.target_group_key_name + } + ] + } + } + } + + # S3 Bucket + s3_bucket_force_destroy = var.s3_bucket_force_destroy +} + +################################################################################ +# ACM +################################################################################ + +data "aws_route53_zone" "base_domain" { + name = var.base_domain +} + +################################################################################ +# Security Groups +################################################################################ + +resource "aws_security_group" "alb_allow_all" { + name = var.security_group_alb + description = "Allow all ingress and egress traffic within Load Balancer" + vpc_id = var.vpc_id + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/examples/complete/outputs.tf b/examples/cross-account/outputs.tf similarity index 100% rename from examples/complete/outputs.tf rename to examples/cross-account/outputs.tf diff --git a/examples/complete/variables.tf b/examples/cross-account/variables.tf similarity index 98% rename from examples/complete/variables.tf rename to examples/cross-account/variables.tf index 3973043..4dd698a 100644 --- a/examples/complete/variables.tf +++ b/examples/cross-account/variables.tf @@ -136,9 +136,11 @@ variable "domain_name" { variable "region" { description = "AWS region to deploy resources" type = string + default = null } variable "route53_assume_role_arn" { description = "ARN of the cross-account role for Route53 DNS record creation" type = string + default = null } diff --git a/examples/complete/versions.tf b/examples/cross-account/versions.tf similarity index 100% rename from examples/complete/versions.tf rename to examples/cross-account/versions.tf diff --git a/examples/complete/.header.md b/examples/same-account/.header.md similarity index 100% rename from examples/complete/.header.md rename to examples/same-account/.header.md diff --git a/examples/complete/README.md b/examples/same-account/README.md similarity index 97% rename from examples/complete/README.md rename to examples/same-account/README.md index eef1104..99393d0 100644 --- a/examples/complete/README.md +++ b/examples/same-account/README.md @@ -7,6 +7,11 @@ Configuration in this directory creates: - Internet-facing Application Load Balancer to access the deployed services with S3 bucket for storing access and connection logs, and - ACM to generate and validate an Amazon-issued certificate for a base domain + +For same-account Route53 validation, map the alias provider to the main provider as: +providers = { aws = aws, aws.cross_account_provider = aws }. +No assume_role block is required in same-account mode. + ## Example `tfvars` Configuration ```tf diff --git a/examples/complete/main.tf b/examples/same-account/main.tf similarity index 96% rename from examples/complete/main.tf rename to examples/same-account/main.tf index 008ea49..37d6d66 100644 --- a/examples/complete/main.tf +++ b/examples/same-account/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "ap-south-1" + region = var.region } locals { @@ -25,6 +25,11 @@ locals { module "ecs_deployment" { source = "../../" + providers = { + aws = aws + aws.cross_account_provider = aws + } + cluster_name = var.cluster_name vpc_id = var.vpc_id @@ -93,9 +98,6 @@ module "ecs_deployment" { record_zone_id = data.aws_route53_zone.base_domain.zone_id } } - region = var.region - # Cross-account role that ACM module will use for Route53 DNS record creation - route53_assume_role_arn = var.route53_assume_role_arn # Application Load Balancer load_balancer = { diff --git a/examples/same-account/outputs.tf b/examples/same-account/outputs.tf new file mode 100644 index 0000000..7509467 --- /dev/null +++ b/examples/same-account/outputs.tf @@ -0,0 +1,79 @@ +################################################################################ +# ECS Deployment +################################################################################ + +output "ecs_service_arn" { + description = "ARN of the ECS Service" + value = module.ecs_deployment.ecs_service_arn +} + +output "task_definition_arn" { + description = "ARN of the ECS Task Definition" + value = module.ecs_deployment.ecs_task_definition_arn +} + +################################################################################ +# ECS Capacity Provider +################################################################################ + +output "ecs_capacity_provider_id" { + description = "Identifier of the ECS Capacity Provider" + value = module.ecs_deployment.capacity_provider_ids[0] +} + +output "ecs_capacity_provider_arn" { + description = "ARN of the ECS Capacity Provider" + value = module.ecs_deployment.capacity_provider_arns[0] +} + +output "ecs_cluster_capacity_providers_id" { + description = "Identifier of the ECS Cluster Capacity Providers" + value = module.ecs_deployment.capacity_provider_ecs_cluster_capacity_providers_id +} + +################################################################################ +# Application Load Balancer +################################################################################ + +output "alb_arn" { + description = "ARN of the Application Load Balancer ECS Service" + value = module.ecs_deployment.alb_arn +} + +output "target_group_id" { + description = "Identifier of the Target Group instances" + value = module.ecs_deployment.alb_target_groups_ids[local.target_group_key_name] +} + +output "target_group_arn" { + description = "ARN of the Target Group instances" + value = module.ecs_deployment.alb_target_groups_arns[local.target_group_key_name] +} + +output "listener_id" { + description = "Identifier of the ALB Listener forwarding to container instances" + value = module.ecs_deployment.alb_listeners_ids["this"] +} + +output "listener_arn" { + description = "ARN of the ALB Listener forwarding to container instances" + value = module.ecs_deployment.alb_listeners_arns["this"] +} + +################################################################################ +# ACM +################################################################################ + +output "acm_amazon_issued_certificate_arn" { + description = "ARN of the ACM Amazon-issued certificate for the base domain" + value = module.ecs_deployment.acm_certificates_arns["base_domain"] +} + +################################################################################ +# Security Groups +################################################################################ + +output "alb_allow_all_sg_id" { + description = "ID of the Security Group for Application Load Balancer to allow all traffic from any source" + value = aws_security_group.alb_allow_all.id +} diff --git a/examples/same-account/variables.tf b/examples/same-account/variables.tf new file mode 100644 index 0000000..800c346 --- /dev/null +++ b/examples/same-account/variables.tf @@ -0,0 +1,140 @@ +variable "vpc_id" { + description = "VPC ID where the resources will be deployed" + type = string +} + +variable "service_network_configuration_security_groups" { + description = "Security Groups for the ECS Service's Network Configuration" + type = list(string) +} + +variable "private_subnets" { + description = "List of private subnet IDs" + type = list(string) +} + +variable "public_subnets" { + description = "List of public subnet IDs" + type = list(string) +} + +variable "asg_arn" { + description = "ARN of the Auto Scaling group" + type = string +} + +variable "capacity_provider_name" { + description = "Name of the Capacity Provider" + type = string +} + +variable "capacity_provider_managed_scaling" { + description = "Managed scaling configuration for the Capacity Provider" + type = any +} + +variable "cluster_name" { + description = "Name of the ECS cluster" + type = string +} + +variable "container_name" { + description = "Name of the container" + type = string +} + +variable "service_desired_count" { + description = "Desired count for the ECS Service" + type = number +} + +variable "container_image" { + description = "Image of the container" + type = string +} + +variable "container_port" { + description = "Port on which the container will listen" + type = number +} + +variable "container_cpu" { + description = "CPU units to allocate to the container" + type = number +} + +variable "container_memory" { + description = "Memory in MB to allocate to the container" + type = number +} + +variable "container_essential" { + description = "Essential flag for the container" + type = bool +} + +variable "container_port_mappings" { + description = "Port mappings for the container" + type = any +} + +variable "container_readonly_root_filesystem" { + description = "Whether the root filesystem is readonly for the container" + type = bool +} + +variable "target_group_name" { + description = "Name of the target group" + type = string +} + +variable "target_group_protocol" { + description = "Protocol to use with the target group" + type = string +} + +variable "target_group_health_check" { + description = "Health check configuration for the target group" + type = any +} + +variable "alb_name" { + description = "Name of the application load balancer" + type = string +} + +variable "listener_port" { + description = "Port for the ALB listener" + type = number +} + +variable "ssl_policy" { + description = "SSL policy for the ALB" + type = string +} + +variable "security_group_alb" { + description = "Name of the security group for ALB" + type = string +} + +variable "s3_bucket_force_destroy" { + description = "(Optional, Default:false) Boolean that indicates all objects (including any locked objects) should be deleted from the bucket when the bucket is destroyed so that the bucket can be destroyed without error." + type = bool +} + +variable "base_domain" { + description = "Base domain for ACM" + type = string +} + +variable "domain_name" { + description = "Domain name for ACM" + type = string +} + +variable "region" { + description = "AWS region to deploy resources" + type = string + default = null +} diff --git a/examples/same-account/versions.tf b/examples/same-account/versions.tf new file mode 100644 index 0000000..40dc0a1 --- /dev/null +++ b/examples/same-account/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.6.0" +} diff --git a/main.tf b/main.tf index 9adc7a7..fff4392 100644 --- a/main.tf +++ b/main.tf @@ -243,22 +243,6 @@ resource "aws_ecs_task_definition" "this" { ################################################################################ # Amazon Certificates Manager Sub-module ################################################################################ -provider "aws" { - region = var.region -} - -# Cross-account provider for Route53 -provider "aws" { - alias = "cross_account_provider" - region = var.region - - dynamic "assume_role" { - for_each = var.route53_assume_role_arn != null ? [1] : [] - content { - role_arn = var.route53_assume_role_arn - } - } -} module "acm" { source = "./modules/acm" diff --git a/modules/acm/README.md b/modules/acm/README.md index 781ab9b..95ae221 100644 --- a/modules/acm/README.md +++ b/modules/acm/README.md @@ -24,14 +24,13 @@ This sub-module creates the Amazon-issued certificate for a given domain with `v | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.6.0 | -| [aws](#requirement\_aws) | ~> 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | ~> 6.0 | -| [aws.cross\_account\_provider](#provider\_aws.cross\_account\_provider) | ~> 6.0 | +| [aws](#provider\_aws) | n/a | +| [aws.cross\_account\_provider](#provider\_aws.cross\_account\_provider) | n/a | ## Modules diff --git a/modules/acm/providers.tf b/modules/acm/providers.tf index 8688425..1971e5f 100644 --- a/modules/acm/providers.tf +++ b/modules/acm/providers.tf @@ -1,10 +1,8 @@ terraform { required_providers { aws = { - source = "hashicorp/aws" - version = "~> 6.0" + source = "hashicorp/aws" configuration_aliases = [ - aws, aws.cross_account_provider ] } diff --git a/variables.tf b/variables.tf index 7e5ee9b..9f5581b 100644 --- a/variables.tf +++ b/variables.tf @@ -200,12 +200,6 @@ variable "acm_certificates" { default = {} } -variable "region" { - description = "(Optional) AWS region to create resources in." - type = string - default = null -} - variable "route53_assume_role_arn" { description = "(Optional) ARN of the role to assume for Route53 operations." type = string diff --git a/versions.tf b/versions.tf index bf72ee6..9cf042c 100644 --- a/versions.tf +++ b/versions.tf @@ -5,6 +5,10 @@ terraform { aws = { source = "hashicorp/aws" version = "~> 6.0" + + configuration_aliases = [ + aws.cross_account_provider + ] } } } From f928eba5820f2dc9c9ef3cda8c5c20c783bdc6df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Nov 2025 09:16:19 +0000 Subject: [PATCH 2/4] terraform-docs: automated action --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 4791105..f725d19 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,6 @@ # terraform-aws-ecs-deployment Terraform module to deploy production-ready applications and services on an existing ECS infra. -This module supports both same-account and cross-account ACM → Route53 validation workflows. For same-account usage, simply map the providers as providers = { aws = aws, aws.cross_account_provider = aws } without any assume role. -For cross-account setups, you must create an IAM Role in the Route53 Hosted Zone account (Account B) that allows Account A (where ACM and application resources are created) to assume it. This role should grant permissions such as route53:ChangeResourceRecordSets, route53:ListHostedZonesByName, and route53:ListResourceRecordSets, along with a trust policy that permits Account A to assume the role. -When using cross-account mode, configure an alias provider with assume_role and pass it to the module as: -providers = { aws = aws, aws.cross_account_provider = aws.cross_account_provider }. ## Architecture Diagram From 1e4a1da9ea679f29f0bc419d131c651b7688ff24 Mon Sep 17 00:00:00 2001 From: rahul-infra Date: Wed, 26 Nov 2025 16:16:22 +0530 Subject: [PATCH 3/4] Added terraform validate for examples in workflows. --- .github/workflows/terraform-checks.yaml | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/terraform-checks.yaml b/.github/workflows/terraform-checks.yaml index b8faa24..bb8f0ff 100644 --- a/.github/workflows/terraform-checks.yaml +++ b/.github/workflows/terraform-checks.yaml @@ -25,6 +25,34 @@ jobs: id: fmt run: terraform test + validateExamples: + name: Terraform Validate Example Files + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + example: + - examples/same-account + - examples/cross-account + defaults: + run: + working-directory: ${{ matrix.example }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "1.6.0" + + - name: Terraform Init + run: terraform init -input=false + + - name: Terraform Validate + run: terraform validate + collectInputs: name: Collect workflow inputs needs: test From 6f88da56f161b63593dec33e20f9320575a46144 Mon Sep 17 00:00:00 2001 From: rahul-infra Date: Wed, 26 Nov 2025 17:23:11 +0530 Subject: [PATCH 4/4] Made changes in github workflow --- .github/workflows/terraform-checks.yaml | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/.github/workflows/terraform-checks.yaml b/.github/workflows/terraform-checks.yaml index bb8f0ff..87cd30a 100644 --- a/.github/workflows/terraform-checks.yaml +++ b/.github/workflows/terraform-checks.yaml @@ -26,17 +26,8 @@ jobs: run: terraform test validateExamples: - name: Terraform Validate Example Files + name: Terraform Validate Examples runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - example: - - examples/same-account - - examples/cross-account - defaults: - run: - working-directory: ${{ matrix.example }} steps: - name: Checkout @@ -47,11 +38,13 @@ jobs: with: terraform_version: "1.6.0" - - name: Terraform Init - run: terraform init -input=false - - - name: Terraform Validate - run: terraform validate + - name: Validate all example folders + run: | + for dir in examples/*/; do + echo "Validating $dir" + terraform -chdir="$dir" init -input=false > /dev/null + terraform -chdir="$dir" validate + done collectInputs: name: Collect workflow inputs