Setup ECS with Atmos
This setup guide walks you through deploying containerized applications to AWS ECS Fargate using Atmos for configuration orchestration and OpenTofu for infrastructure-as-code. The setup uses a self-contained approach where application infrastructure definitions and workflows reside in the application repository, while shared infrastructure is managed in the infra repository.
Overview
| Step | Action | Repository |
|---|---|---|
| 1. Verify platform infrastructure | Confirm VPC, ECS cluster, GitHub OIDC Provider, TFstate bucket app are deployed | infra |
| 2. Create application repository | Create repo from cloudposse-examples/app-on-ecs-v2 template | GitHub |
| 3. Provision ECR registry | Add ECR image repository for the app | infra |
| 4. Provision IAM roles | Create IAM roles for the app to assume | infra |
| 5. Update tfstate-bucket-app access | Grant IAM roles access to state bucket | infra |
| 6. Update profiles/github | Configure GitHub OIDC roles in app repo | app |
| 7. Configure GitHub repository | Add ECR_REGISTRY variable | GitHub UI |
| 8. Provision shared dependencies | Deploy app dependencies (databases, queues, etc.) | infra |
| 9. Configure dependencies | Set up remote state for infra components | app |
| 10. Configure container definitions | Define ECS task and container settings | app |
| 11. Test configuration | Run atmos terraform plan app -s preview | app |
1 Verify Platform Infrastructure
Before deploying an ECS application, verify that the following platform infrastructure components are already deployed in your target AWS accounts:
- VPC deployed in each environment (
vpccomponent) - ECS cluster deployed in each environment (
ecs/clustercomponent) - GitHub OIDC Provider deployed in each environment (
github-oidc-providercomponent) - TFstate bucket app deployed in each environment (
tfstate-bucket-appcomponent)
These components are typically deployed once and shared across multiple applications. See the ECS documentation for detailed setup instructions.
2 Create Application Repository
Create a new repository from the cloudposse-examples/app-on-ecs-v2 template.
- Navigate to cloudposse-examples/app-on-ecs-v2
- Click "Use this template" → "Create a new repository"
- Choose a name for the repository (e.g.,
acme/example-app) - Select "Private" for repository visibility
- Click "Create repository"
The template includes:
- Sample Go application with Dockerfile
- GitHub Actions workflows for CI/CD
- Terraform/OpenTofu components for ECS task definitions
- Atmos stack configurations for each environment
Since you created your repository from the app-on-ecs-v2 template, the configuration files shown in this guide already exist in your repo. You don't need to create them from scratch — just update the placeholder values (account IDs, role ARNs, bucket names, organization name) to match your infrastructure.
New to GitHub templates? See Creating a repository from a template.
3 Provision ECR Registry
Add an ECR image repository for the new application.
In your infra repository, update the ECR component configuration:
components:
terraform:
ecr:
vars:
images:
- acme/example-app
# Additional configuration as needed
atmos terraform deploy ecr -s core-use1-artifacts
4 Provision IAM Roles
Create IAM roles that the application will assume for deployments.
import:
- catalog/iam-role/defaults
components:
terraform:
example-app/iam-role:
metadata:
component: iam-role
inherits:
- iam-role/defaults
vars:
name: example-app
attributes:
- "terraform"
role_description: |
Role for GitHub Actions to access the GitOps resources, such as the S3 Bucket and DynamoDB Table.
github_oidc_provider_enabled: true
github_oidc_provider_arn: !terraform.state github-oidc-provider oidc_provider_arn
trusted_github_org: acme
trusted_github_repos:
- example-app
policy_statements:
AllowECRAccess:
effect: "Allow"
actions:
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
- "ecr:InitiateLayerUpload"
- "ecr:UploadLayerPart"
- "ecr:CompleteLayerUpload"
- "ecr:PutImage"
- "ecr:CreatePullThroughCacheRule"
- "ecr:BatchImportUpstreamImage"
- "ecr:CreateRepository"
resources:
- "*"
AllowAssumeRole:
effect: "Allow"
actions:
- "sts:AssumeRole"
- "sts:TagSession"
- "sts:SetSourceIdentity"
resources:
- "*"
AllowServicesAccess:
effect: "Allow"
actions:
- "s3:*"
- "iam:*"
- "ecs:*"
- "ecr:*"
- "logs:*"
- "ssm:*"
- "ec2:*"
- "elasticloadbalancing:*"
- "application-autoscaling:*"
- "cloudwatch:*"
- "kms:Decrypt"
resources:
- "*"
atmos terraform deploy example-app/iam-role -s plat-gbl-dev
atmos terraform deploy example-app/iam-role -s plat-gbl-staging
atmos terraform deploy example-app/iam-role -s plat-gbl-prod
5 Update tfstate-bucket-app Access
Grant the newly created IAM roles access to the Terraform state bucket.
Add the IAM role ARN references to your tfstate backend configuration:
components:
terraform:
tfstate-backend-apps:
vars:
privileged_principal_arns:
- !terraform.state example-app/iam-role .role_arn
atmos terraform deploy tfstate-backend-apps -s plat-gbl-dev
atmos terraform deploy tfstate-backend-apps -s plat-gbl-staging
atmos terraform deploy tfstate-backend-apps -s plat-gbl-prod
6 Update profiles/github
Configure the app repository with the IAM roles provisioned in the previous steps.
In your application repository, update the Atmos auth configuration with the provisioned IAM roles:
# ...
auth:
providers:
github-oidc:
kind: github/oidc
region: us-west-1
spec:
audience: sts.amazonaws.com
identities:
plat-dev/terraform:
kind: aws/assume-role
via:
provider: github-oidc
principal:
assume_role: arn:aws:iam::111111111111:role/acme-plat-gbl-dev-example-app-terraform
plat-staging/terraform:
kind: aws/assume-role
via:
provider: github-oidc
principal:
assume_role: arn:aws:iam::222222222222:role/acme-plat-gbl-dev-example-app-terraform
plat-prod/terraform:
kind: aws/assume-role
via:
provider: github-oidc
principal:
assume_role: arn:aws:iam::333333333333:role/acme-plat-gbl-dev-example-app-terraform
Get the role ARNs from the outputs of the IAM role deployments in step 5:
atmos terraform output example-app/iam-role -s plat-gbl-dev -- -raw role_arn
7 Configure GitHub Repository
Add repository ECR_REGISTRY variable required by the GitHub Actions workflows.
-
Navigate to your repository → Settings → Secrets and variables → Actions
-
Click the "Variables" tab
-
Click "New repository variable"
-
Add the following variable:
Name Value ECR_REGISTRYYour ECR registry URL (e.g., 444444444444.dkr.ecr.us-east-1.amazonaws.com)
If you have multiple application repositories, consider creating an organization-level variable for ECR_REGISTRY to avoid repetition.
8 Provision Shared Dependencies
Deploy application dependencies that have a different lifecycle than the application itself (e.g., databases, message queues, caches).
Skip this step if your application doesn't require external dependencies like databases or caches.
9 Configure Dependencies
Your application needs to reference infrastructure that was already deployed by your infrastructure repository — specifically the VPC and ECS cluster. These dependency files tell Atmos where to find the Terraform backend for those resources, allowing your application stacks to look up output values like VPC IDs, subnet IDs, and cluster ARNs.
These files don't deploy any infrastructure. They define how to read the remote state of existing infrastructure components.
In your application repository, create dependency stack files for each infrastructure component you need to reference:
components:
terraform:
vpc:
metadata:
component: vpc
type: abstract
terraform_workspace: "{{ .vars.tenant }}-{{ .vars.environment }}-{{ .vars.deps_stage }}"
backend_type: s3
backend:
s3:
bucket: "acme-core-gbl-root-tfstate"
region: "us-east-1"
encrypt: true
key: terraform.tfstate
acl: bucket-owner-full-control
assume_role:
role_arn: "arn:aws:iam::111111111111:role/acme-core-gbl-root-tfstate-ro"
components:
terraform:
ecs/cluster:
metadata:
component: ecs
type: abstract
terraform_workspace: "{{ .vars.tenant }}-{{ .vars.environment }}-{{ .vars.deps_stage }}"
backend_type: s3
backend:
s3:
bucket: "acme-core-gbl-root-tfstate"
region: "us-east-1"
encrypt: true
key: terraform.tfstate
acl: bucket-owner-full-control
assume_role:
role_arn: "arn:aws:iam::111111111111:role/acme-core-gbl-root-tfstate-ro"
And any other external dependencies like databases or caches.
10 Configure Container Definitions
Define the ECS task and container settings for your application.
components:
terraform:
app:
vars:
desired_count: 1
cpu: 256
memory: 512
container_definitions:
app:
image: !env APP_IMAGE
port_mappings:
- containerPort: 8080
hostPort: 8080
protocol: "tcp"
environment:
- name: "APP_ENV"
value: "production"
secrets:
- name: "DATABASE_URL"
valueFrom: !terraform.state rds .database_url
And/or update Terraform component (if needed). Modify the ECS task component in terraform/components/ecs-task/ to match your application requirements:
- Add additional IAM permissions
- Configure health check settings
- Set up log configuration
- Add sidecar containers
11 Test Configuration
Verify the Atmos configuration works correctly.
atmos terraform plan app -s preview
Check that:
- All remote state references resolve correctly
- Container definitions are valid
- IAM roles and permissions are configured
- No errors in the plan output
Quick Checklist
- Platform infrastructure verified (VPC, ECS cluster, GitHub OIDC Provider)
-
tfstate-bucket-appsprovisioned in dev/staging/prod - Application repository created from template
- ECR registry provisioned (
core-use1-artifacts) - IAM roles provisioned in dev/staging/prod
-
tfstate-backend-appsupdated with IAM role access -
profiles/github/atmos.yamlconfigured with IAM role ARNs - GitHub repo variable
ECR_REGISTRYconfigured - Shared dependencies provisioned (if applicable)
- Dependencies configured in
terraform/stacks/deps/ - Container definitions configured in
terraform/stacks/default/app.yaml -
atmos terraform plan app -s previewruns successfully
Next Steps
Now that your ECS application is configured, learn how to deploy through the CI/CD pipeline.
Trigger DeploymentsReferences
- app-on-ecs-v2: Example application repository
- Atmos: Configuration orchestration tool
- OpenTofu: Infrastructure-as-code tool
- GitHub OIDC with AWS: How to configure GitHub OIDC
- github-action-docker-build-push: Docker build action