AWS CloudFormation ECS
Overview
Provides CloudFormation patterns for ECS clusters, task definitions, services, container definitions, auto scaling, blue/green deployments, ALB integration, monitoring, and cross-stack references.
When to Use
- Creating or updating ECS clusters with CloudFormation
- Configuring Fargate/EC2 launch types and capacity providers
- Deploying services with ALB/NLB integration or blue/green deployments
- Implementing auto scaling for ECS services
- Setting up monitoring with Container Insights
Instructions
Follow these steps to create ECS infrastructure with CloudFormation:
1. Define ECS Cluster Parameters
Specify launch type, networking, and capacity settings:
yamlParameters: LaunchType: Type: String Default: FARGATE AllowedValues: - EC2 - FARGATE Description: ECS launch type ContainerPort: Type: Number Default: 80 Description: Container port TaskCPU: Type: String Default: 256 AllowedValues: - 256 - 512 - 1024 - 2048 - 4096 Description: Task CPU units TaskMemory: Type: String Default: 512 AllowedValues: - 512 - 1024 - 2048 - 3072 - 4096 - 5120 - 6144 - 7168 - 8192 - 9216 - 10240 Description: Task memory in MB
2. Create ECS Cluster
Define the cluster infrastructure:
yamlResources: ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub "${AWS::StackName}-cluster" ClusterSettings: - Name: containerInsights Value: enabled CapacityProviders: - FARGATE - FARGATE_SPOT DefaultCapacityProviderStrategy: - CapacityProvider: FARGATE Weight: 1 - CapacityProvider: FARGATE_SPOT Weight: 0
3. Create Task Definition
Define container configurations:
yamlResources: TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Family: !Sub "${AWS::StackName}-task" NetworkMode: awsvpc RequiresCompatibilities: - FARGATE Cpu: !Ref TaskCPU Memory: !Ref TaskMemory ExecutionRoleArn: !Ref ExecutionRole TaskRoleArn: !Ref TaskRole ContainerDefinitions: - Name: application Image: !Ref ImageUrl PortMappings: - ContainerPort: !Ref ContainerPort Protocol: tcp Environment: - Name: LOG_LEVEL Value: INFO LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: ecs Memory: !Ref TaskMemory
Validate task definition syntax before proceeding:
bashaws cloudformation validate-template --template-body file://template.yaml
4. Configure Execution Roles
Set up IAM roles for task execution:
yamlResources: ExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy TaskRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: S3Access PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:GetObject Resource: !Sub "${DataBucket.Arn}/*"
5. Create ECS Service
Define the service configuration:
yamlResources: ECSService: Type: AWS::ECS::Service Properties: ServiceName: !Sub "${AWS::StackName}-service" Cluster: !Ref ECSCluster TaskDefinition: !Ref TaskDefinition DesiredCount: 2 LaunchType: FARGATE NetworkConfiguration: AwsvpcConfiguration: Subnets: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 SecurityGroups: - !Ref SecurityGroup AssignPublicIp: DISABLED LoadBalancers: - TargetGroupArn: !Ref TargetGroup ContainerName: application ContainerPort: !Ref ContainerPort
6. Configure Load Balancer
Set up ALB for traffic distribution:
yamlResources: LoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: !Sub "${AWS::StackName}-alb" Scheme: internet-facing Type: application Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 SecurityGroups: - !Ref ALBSecurityGroup TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 80 Protocol: HTTP VpcId: !Ref VPC TargetType: ip Listener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref TargetGroup Type: forward LoadBalancerArn: !Ref LoadBalancer Port: 80 Protocol: HTTP
7. Implement Auto Scaling
Configure Application Auto Scaling:
yamlResources: ScalableTarget: Type: AWS::ApplicationAutoScaling::ScalableTarget Properties: MaxCapacity: 10 MinCapacity: 1 ResourceId: !Sub "service/${ECSCluster}/${ECSService}" ScalableDimension: ecs:service:DesiredCount ServiceNamespace: ecs ScalingPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: PolicyName: !Sub "${AWS::StackName}-scaling" PolicyType: TargetTrackingScaling ScalingTargetId: !Ref ScalableTarget TargetTrackingScalingPolicyConfiguration: TargetValue: 70.0 PredefinedMetricSpecification: PredefinedMetricType: ECSServiceAverageCPUUtilization
8. Configure Monitoring
Enable CloudWatch Container Insights:
yamlResources: LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/ecs/${AWS::StackName}" RetentionInDays: 7
Before deployment: Create a change set to preview changes:
bashaws cloudformation create-change-set \ --stack-name my-ecs-stack \ --template-body file://template.yaml \ --change-set-type CREATE aws cloudformation execute-change-set --change-set-name <arn>
Best Practices
Task Definition
- Use
Familynaming for version tracking and immutable deployments - Keep task definition under 1 KB (CloudFormation limit) by referencing external configs
- Configure
HealthCheckin container definitions for ECS health monitoring - Set both
CpuandMemoryat task level for Fargate
Service Deployment
- Enable
DeploymentCircuitBreakerfor automatic rollback on failures - Use
HealthCheckGracePeriodSecondsmatching application startup time - Configure
MinimumHealthyPercent(100) andMaximumPercent(200) for zero-downtime updates - Reference task definition by logical ID only—ECS automatically uses latest revision
Networking
- Always use
awsvpcnetwork mode for Fargate - Place tasks in private subnets with NAT gateway for outbound access
- Configure security groups to allow only required ports (not 0.0.0.0/0)
Scaling
- Use Fargate Spot with base capacity of 1 on-demand for cost optimization
- Set
MaxHealthyDurationon capacity provider strategy for Spot interruption handling - Monitor
STEADY_STATEfailures in CloudWatch for task startup issues
Constraints and Warnings
Resource Limits
- Task definition size limit: 1 KB when using CloudFormation (use ParameterStore/Secrets Manager for large configs)
- Maximum 10 containers per task definition in Fargate
- CPU must be specified for Fargate tasks (256-4096 units, in 1024 increments)
- Memory must be allocated (512-30720 MB depending on CPU)
Operational Limits
- ENI limits apply per subnet—plan for at least 1 ENI per task in each AZ
- Fargate Spot tasks receive 2-minute interruption notice—implement graceful shutdown signals (SIGTERM)
- Service updates require new task definition revision—cannot modify existing versions
DesiredCountupdates during deployment may conflict with auto scaling policies
CloudFormation-Specific
- Cross-stack references required for VPC IDs, Subnet IDs, and Security Group IDs passed between stacks
- Use
!GetAttfor referencing stack outputs in same template - Ensure IAM role ARNs use
Fn::Subwith stack name for portability
Examples
Minimal Fargate Service
yamlAWSTemplateFormatVersion: "2010-09-09" Description: Minimal ECS Fargate service Resources: Cluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub "${AWS::StackName}-cluster" TaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Family: !Sub "${AWS::StackName}-task" NetworkMode: awsvpc RequiresCompatibilities: [FARGATE] Cpu: 256 Memory: 512 ContainerDefinitions: - Name: app Image: nginx:latest PortMappings: - ContainerPort: 80 Service: Type: AWS::ECS::Service Properties: Cluster: !Ref Cluster ServiceName: !Sub "${AWS::StackName}-svc" TaskDefinition: !Ref TaskDefinition DesiredCount: 2 LaunchType: FARGATE DeploymentCircuitBreaker: Enable: true Rollback: true
ECS with ALB Integration
yamlResources: Service: Type: AWS::ECS::Service Properties: Cluster: !Ref ECSCluster TaskDefinition: !Ref TaskDefinition DesiredCount: 2 LaunchType: FARGATE HealthCheckGracePeriodSeconds: 30 NetworkConfiguration: AwsvpcConfiguration: Subnets: [!Ref PrivateSubnet1, !Ref PrivateSubnet2] SecurityGroups: [!Ref TaskSecurityGroup] LoadBalancers: - TargetGroupArn: !Ref TargetGroup ContainerName: app ContainerPort: 8080 TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 80 Protocol: HTTP VpcId: !Ref VPC TargetType: ip HealthCheckPath: /health
References
For detailed implementation guidance, see:
- constraints.md - Resource limits (task definition size, container limits, memory limits, CPU limits), operational constraints (service updates, ENI limits, task start time, scaling delays), security constraints (IAM roles, network mode, security groups, secrets rotation), cost considerations (Fargate pricing, data transfer, ECR storage, monitoring), deployment constraints (blue/green requirements, CodeDeploy integration, rollbacks, task drift), and availability constraints (Fargate Spot, multi-AZ, health checks, service discovery)

