Skip to main content
Latest Documentation
This is the latest documentation for the Cloud Posse Reference Architecture. To determine which version you're currently using, please see Version Identification.

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

StepActionRepository
1. Verify platform infrastructureConfirm VPC, ECS cluster, GitHub OIDC Provider, TFstate bucket app are deployedinfra
2. Create application repositoryCreate repo from cloudposse-examples/app-on-ecs-v2 templateGitHub
3. Provision ECR registryAdd ECR image repository for the appinfra
4. Provision IAM rolesCreate IAM roles for the app to assumeinfra
5. Update tfstate-bucket-app accessGrant IAM roles access to state bucketinfra
6. Update profiles/githubConfigure GitHub OIDC roles in app repoapp
7. Configure GitHub repositoryAdd ECR_REGISTRY variableGitHub UI
8. Provision shared dependenciesDeploy app dependencies (databases, queues, etc.)infra
9. Configure dependenciesSet up remote state for infra componentsapp
10. Configure container definitionsDefine ECS task and container settingsapp
11. Test configurationRun atmos terraform plan app -s previewapp

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 (vpc component)
  • ECS cluster deployed in each environment (ecs/cluster component)
  • GitHub OIDC Provider deployed in each environment (github-oidc-provider component)
  • TFstate bucket app deployed in each environment (tfstate-bucket-app component)
Platform Infrastructure

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.

  1. Navigate to cloudposse-examples/app-on-ecs-v2
  2. Click "Use this template" → "Create a new repository"
  3. Choose a name for the repository (e.g., acme/example-app)
  4. Select "Private" for repository visibility
  5. 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
Template Files Already Exist

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:

stacks/catalog/ecr.yaml
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.

stacks/catalog/iam-role/example-app.yaml
    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:

stacks/catalog/tfstate-backend/apps.yaml
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:

profiles/github/atmos.yaml
# ...

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
IAM Role ARNs

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.

  1. Navigate to your repository → Settings → Secrets and variables → Actions

  2. Click the "Variables" tab

  3. Click "New repository variable"

  4. Add the following variable:

    NameValue
    ECR_REGISTRYYour ECR registry URL (e.g., 444444444444.dkr.ecr.us-east-1.amazonaws.com)
Organization Variables

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).

Optional Step

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.

tip

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:

terraform/stacks/deps/vpc.yaml
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"
terraform/stacks/deps/ecs-cluster.yaml
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.

terraform/stacks/default/app.yaml
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:

  1. Add additional IAM permissions
  2. Configure health check settings
  3. Set up log configuration
  4. 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-apps provisioned in dev/staging/prod
  • Application repository created from template
  • ECR registry provisioned (core-use1-artifacts)
  • IAM roles provisioned in dev/staging/prod
  • tfstate-backend-apps updated with IAM role access
  • profiles/github/atmos.yaml configured with IAM role ARNs
  • GitHub repo variable ECR_REGISTRY configured
  • 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 preview runs successfully

Next Steps

Now that your ECS application is configured, learn how to deploy through the CI/CD pipeline.

Trigger Deployments

References