Skip to content
Go back

My CloudFormation Template Organization

I needed a repeatable way to deploy web apps on AWS. I developed a baseline structure that works for containerized web apps with a database. Then I modify and tweak it based on specific project requirements.

This setup handles VPC networking, ECS Fargate containers, RDS database, and load balancing. It includes ECS auto-scaling configuration and has separate test and prod environments. For my example project, it hosts API, Migrator, and Worker services.

My Template Structure

infrastructure/
├── cloudformation/
│   ├── parameters/
│   │   ├── test.json
│   │   └── prod.json
│   ├── 01-base-vpc-networking.json
│   ├── 02-base-security-groups.json
│   ├── 03-data-rds.json
│   ├── 04-compute-ecs-cluster.json
│   ├── 05-compute-alb.json
│   └── 06-compute-fargate-scaling.json
├── ecs/
│   ├── api-task-definition.json
│   ├── worker-task-definition.json
│   └── migrator-task-definition.json
├── scripts/
│   ├── deploy-environment.sh
│   ├── deploy-scaling.sh
│   ├── setup-secrets.sh
│   └── resolve-secrets.sh
└── README.md

Some notes on the structure:

Secrets Management

setup-secrets.sh creates secrets in AWS Secrets Manager during initial setup. Run once per environment.

resolve-secrets.sh replaces secret placeholders with actual ARNs before every deployment. AWS adds random suffixes to secret ARNs:

Task definition template:

{
  "secrets": [
    {
      "name": "ConnectionString",
      "valueFrom": "myproject/${ENVIRONMENT}/connection-string"
    }
  ]
}

ECS needs the full ARN including random suffix:

{
  "secrets": [
    {
      "name": "ConnectionString",
      "valueFrom": "arn:aws:secretsmanager:eu-central-1:123456789:secret:myproject/test/connection-string-AbCdEf"
    }
  ]
}

resolve-secrets.sh queries AWS for actual ARNs and updates task definitions. Runs automatically in CI/CD.

Deploying the Infrastructure

cd infrastructure/scripts
./deploy-environment.sh test myproject-infra

The script:

After infrastructure deployment:

  1. Run setup-secrets.sh to create placeholder secrets
  2. Update secrets with actual values via AWS CLI or Console
  3. Deploy ECS services via GitHub Actions
  4. Run deploy-scaling.sh to configure auto-scaling

Deploying the Application

Application deployment is separate from infrastructure. Infrastructure changes infrequently. Code changes constantly.

GitHub Actions workflow:

  1. Build Docker images
  2. Push to ECR
  3. Run resolve-secrets.sh on task definitions
  4. Update ECS task definitions with new image tags
  5. Deploy to ECS
  6. Wait for deployment completion

Cleanup

Delete stacks in reverse order (06 → 01):

aws cloudformation delete-stack --stack-name myproject-test-compute-fargate-scaling
aws cloudformation delete-stack --stack-name myproject-test-compute-alb
aws cloudformation delete-stack --stack-name myproject-test-compute-ecs-cluster
aws cloudformation delete-stack --stack-name myproject-test-data-rds
aws cloudformation delete-stack --stack-name myproject-test-base-security-groups
aws cloudformation delete-stack --stack-name myproject-test-base-vpc-networking

Manual cleanup needed: S3 buckets (CloudFormation won’t delete non-empty buckets), ECR images, CloudWatch log groups with never-expire retention, Secrets Manager secrets (deletion protection enabled).


Share this post on:

Previous Post
Property Getters vs Get Methods: When to Use Which
Next Post
The Static-Instance Singleton Pattern in Flutter