I'm trying to bring up a CloudFormation stack to run an ECS service on EC2. My stack creation fails upon creation of the Auto Scaling group and the error in the console Activity tab shows:
Status: Failed
Description:
Launching a new EC2 instance. Status Reason: The requested configuration is currently not supported. Please check the documentation for supported configurations. Launching EC2 instance failed.
Cause:
At 2020-10-26T23:47:46Z a user request update of AutoScalingGroup constraints to min: 1, max: 1, desired: 1 changing the desired capacity from 0 to 1. At 2020-10-26T23:47:48Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1.
I have tried to play around with my CFT but with no luck so far.
AWSTemplateFormatVersion: '2010-09-09'
Description: Hhhhhhhhh Feed Services Containers
Parameters:
VpcId:
Type: String
SubnetId:
Type: String
ECSCluster:
Type: String
Default: dev-ecs
EcsSecurityGroup:
Type: String
Default: sg-74cb7b0c
FeedServicesSecurityGroup:
Type: String
Default: sg-0a695957eec3371bc
DesiredCount:
Type: Number
Default: '1'
EC2InstanceAMI:
Type: String
Default: 'ami-0dba2cb6798deb6d8'
InstanceType:
Type: String
Default: c6g.4xlarge
KeyName:
Type: String
Default: devops
Color:
Type: String
AllowedValues: ['blue', 'green']
Description: The deployment color
Default: 'blue'
XxxRouteTableId:
Type: String
Default: rtb-03eeb623aac1c1ccf
Resources:
YyyXxxLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [/ecs/feed-services-Yyy-Xxx, !Ref Color]]
YyyStableLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [/ecs/feed-services-Yyy-stable, !Ref Color]]
ZzzXxxLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [/ecs/feed-services-Zzz-Xxx, !Ref Color]]
ZzzStableLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [/ecs/feed-services-Zzz-stable, !Ref Color]]
WwwXxxLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [/ecs/feed-services-Www-Xxx, !Ref Color]]
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: feed-services
ExecutionRoleArn: arn:aws:iam::xxxxxxxxx:role/ecs-task-execution-role
TaskRoleArn: !Ref FeedServicesRole
ContainerDefinitions:
- Name: feed-services-Yyy-Xxx
Image: xxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/feed-services/feed-services-Yyy-Xxx
Essential: True
Memory: 512
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref YyyXxxLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
- Name: feed-services-Yyy-stable
Image: xxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/feed-services/feed-services-Yyy-stable
Essential: True
Memory: 512
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref YyyStableLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
- Name: feed-services-Zzz-Xxx
Image: xxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/feed-services/feed-services-Zzz-Xxx
Essential: True
Memory: 8192
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref ZzzXxxLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
- Name: feed-services-Zzz-stable
Image: xxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/feed-services/feed-services-Zzz-stable
Essential: True
Memory: 512
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref ZzzStableLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
- Name: feed-services-Www-Xxx
Image: xxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/feed-services/feed-services-Www-Xxx
Essential: True
Memory: 512
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref WwwXxxLogsGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
NetworkMode: awsvpc
FeedServicesRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ['ec2.amazonaws.com']
Action: ['sts:AssumeRole']
Policies:
- PolicyName: !Join ['-', [feed-services, !Ref Color, read-secrets]]
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'secretsmanager:ListSecrets'
- 'secretsmanager:DescribeSecret'
- 'secretsmanager:GetRandomPassword'
- 'secretsmanager:GetResourcePolicy'
- 'secretsmanager:GetSecretValue'
- 'secretsmanager:ListSecretVersionIds'
Resource: ['arn:aws:secretsmanager:us-east-1:xxxxxxxxx:secret:prod/feed-services']
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier: [!Ref SubnetId]
LaunchConfigurationName: !Ref ContainerInstances
MinSize: '1'
MaxSize: '1'
DesiredCapacity: '1'
CreationPolicy:
ResourceSignal:
Timeout: PT15M
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: 'true'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
LaunchConfigurationName: !Join ['-', [feed-services, !Ref Color, launch-configuration]]
AssociatePublicIpAddress: True
ImageId: !Ref EC2InstanceAMI
SecurityGroups: [!Ref FeedServicesSecurityGroup]
InstanceType: !Ref InstanceType
IamInstanceProfile: !Ref EC2InstanceProfile
PlacementTenancy: default
KeyName: !Ref KeyName
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
FeedServices:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref ECSCluster
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
DesiredCount: !Ref DesiredCount
LaunchType: EC2
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups: [!Ref FeedServicesSecurityGroup]
Subnets: [!Ref SubnetId]
ServiceName: !Join ['-', [feed-services, !Ref Color]]
TaskDefinition: !Ref TaskDefinition
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
DependsOn: FeedServices
Properties:
MaxCapacity: 1
MinCapacity: 1
ResourceId: !Join [ '', [ feed-services/, !Ref 'ECSCluster', /, !GetAtt [ FeedServices, Name ] ] ]
RoleARN: !GetAtt [ AutoscalingRole, Arn ]
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ ec2.amazonaws.com ]
Action: [ 'sts:AssumeRole' ]
Path: /
Policies:
- PolicyName: !Join ['-', [feed-services, !Ref Color, ecs-role]]
PolicyDocument:
Statement:
- Effect: Allow
Action: [ 'ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession',
'ecs:Submit*', 'logs:CreateLogStream', 'logs:PutLogEvents' ]
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ application-autoscaling.amazonaws.com ]
Action: [ 'sts:AssumeRole' ]
Path: /
Policies:
- PolicyName: !Join ['-', [feed-services, !Ref Color, autoscaling-role]]
PolicyDocument:
Statement:
- Effect: Allow
Action: [ 'application-autoscaling:*', 'cloudwatch:DescribeAlarms', 'cloudwatch:PutMetricAlarm',
'ecs:DescribeServices', 'ecs:UpdateService' ]
Resource: '*'
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref XxxRouteTableId
SubnetId: !Ref SubnetId
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [ !Ref 'EC2Role' ]
Outputs:
feedservices:
Value: !Ref FeedServices
taskdefinition:
Value: !Ref TaskDefinition
Based on your parameter defaults, you're trying to launch an Ubuntu Server 20.04 x86 AMI (ami-0dba2cb6798deb6d8) on an instance type (c6g.4xlarge) that requires an ARM based AMI.
Try switching the AMI to ami-0ea142bd244023692, which (at the time of this writing) is the ARM based AMI for Ubuntu Server 20.04
Related
I'm stuck at circular dependency loop in Cfn (ECS Stack), This can be easily resolve by segregating resources in different stack, but challenge is to resolve it within same/single stack. Spent a night solving it, still not getting any close to resolve it.
After a lot of debugging finally though of seeking some help, let me know if anyone can assist me in this, I'd really appreciate any leads or help.
`
AWSTemplateFormatVersion: '2010-09-09'
Description: This stack will deploy following resources , May
# Metadata:
Parameters:
VPC:
Description: Select One VPC available in your existing account
Type: AWS::EC2::VPC::Id
PubSubnets:
Type: 'List<AWS::EC2::Subnet::Id>'
Description: The list of PubSubnetIds in selected VPC)
PvtSubnets:
Type: 'List<AWS::EC2::Subnet::Id>'
Description: The list of PvtSubnetIds in selected VPC)
ClientName:
Type: String
Default: test
# Mappings:
# Conditions:
Resources:
ELBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
# DependsOn:
# - ElasticLoadBalancer
Properties:
Name: "ELB-TG"
HealthCheckIntervalSeconds: 6
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref VPC
TargetType: instance
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "ELBTraffic"
GroupDescription: "Enable HTTP access on the inbound port for ELB"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: ELBSecurityGroup
ElasticLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
# DependsOn:
# - ELBSecurityGroup
Properties:
Subnets:
- !Ref PubSubnets
SecurityGroups:
- !Ref ELBSecurityGroup
ElbListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
# DependsOn:
# - ElasticLoadBalancer
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ELBTargetGroup
LoadBalancerArn: !Ref ElasticLoadBalancer
Port: 80
Protocol: HTTP
AsgConfig:
Type: AWS::EC2::LaunchTemplate
DependsOn:
- ELBSecurityGroup
Properties:
LaunchTemplateName: !Sub ${ClientName}-launch-template
LaunchTemplateData:
ImageId: ami-0171959e760b38d59
InstanceType: t3.medium
SecurityGroups:
- !Ref ELBSecurityGroup
#ImageId: "ami-0171959e760b38d59"
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
sudo yum install -y python-pip pip
yum install -y aws-cfn-bootstrap
/opt/aws/apitools/cfn-init-2.0-6/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource AsGroup --region ${AWS::Region}
/opt/aws/apitools/cfn-init-2.0-6/bin/cfn-init -v --stack ${AWS::StackName} --resource AsgConfig --region ${AWS::Region} -c default
AsGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- AsgConfig
Properties:
VPCZoneIdentifier:
- !Ref PvtSubnets
LaunchTemplate:
LaunchTemplateId: !Ref AsgConfig
Version: !GetAtt AsgConfig.LatestVersionNumber
# LaunchConfigurationName: !Ref AsgConfig
MinSize: '1'
DesiredCapacity: '2'
MaxSize: '4'
TargetGroupARNs:
- !Ref ELBTargetGroup
CapacityProvider:
Type: AWS::ECS::CapacityProvider
DependsOn:
- AsGroup
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref AsGroup
CodeCommitRepository1:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "HTML code"
RepositoryName: "HTML_code"
CodeCommitRepository2:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "Python code"
RepositoryName: "Python_code"
CodeCommitRepository3:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryDescription: "Node code"
RepositoryName: "Node_code"
ECRrepo:
Type: AWS::ECR::Repository
Properties:
RepositoryName: "cfn_repo"
ECSCluster:
Type: AWS::ECS::Cluster
DependsOn:
- CapacityProvider
Properties:
CapacityProviders:
- !Ref CapacityProvider
ClusterName: !Ref ClientName
Configuration:
ExecuteCommandConfiguration:
Logging: DEFAULT
ClusterSettings:
- Name: containerInsights
Value: enabled
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ecs-service-role"
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: "ecs-execution-role"
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "ContainerSecurityGroup"
GroupDescription: "Security group for container"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Service:
Type: AWS::ECS::Service
DependsOn:
- ECSCluster
- ElasticLoadBalancer
# - ECSServiceRole
# - TaskDefinition
- ELBTargetGroup
Properties:
Cluster: !Ref ECSCluster
Role: !Ref ECSServiceRole
DesiredCount: 1
TaskDefinition: !Ref TaskDefinition
LaunchType: EC2
LoadBalancers:
- ContainerName: "deployment-container"
ContainerPort: 80
TargetGroupArn: !Ref ELBTargetGroup
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
RoleName: service-auto-scaling-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ["sts:AssumeRole"]
Policies:
- PolicyName: service-auto-scaling-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:UpdateService
- cloudwatch:PutMetricAlarm
- cloudwatch:DescribeAlarms
- cloudwatch:DeleteAlarms
Resource:
- "*"
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
# DependsOn:
# -
Properties:
RoleARN: !GetAtt AutoScalingRole.Arn
ResourceId: !Sub service/${ClientName}/Service
ServiceNamespace: ecs
ScalableDimension: ecs:Service:DesiredCount
MinCapacity: 1
MaxCapacity: 5
ScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
DependsOn:
- ScalableTarget
Properties:
PolicyName: service-auto-scaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ScalableTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 80.0
TaskDefinition:
Type: AWS::ECS::TaskDefinition
# DependsOn:
# - ExecutionRole
# - Service
Properties:
Family: deployment-task
Cpu: "256"
Memory: "512"
NetworkMode: bridge
ExecutionRoleArn: !Ref ExecutionRole
ContainerDefinitions:
- Name: deployment-container
Image: cfn_repo/cfnrepo:latest
PortMappings:
- ContainerPort: 80
RequiresCompatibilities:
- EC2
# Outputs:
# outputELBTargetGroup:
# Description: A reference to the created Target Group
# Value: !Ref ELBTargetGroup
# outputELBSecurityGroup:
# Description: A reference to the created Security Group
# Value: !Ref ELBSecurityGroup
# outputElasticLoadBalancer:
# Description: A reference to the created Elastic Load Balancer
# Value: !Ref ElasticLoadBalancer
# outputElasticListener:
# Description: A reference to the created Elastic Load Balancer Listener
# Value: !Ref ElbListener
# outputAsgConfig:
# Description: Id for autoscaling launch configuration
# Value: !Ref AsgConfig
# outputAsgGroup:
# Description: Id for autoscaling group
# Value: !Ref AsgGroup
# outputECSCluster:
# Description: Cluster name
# Value: !Ref ECSCluster
`
You have a circular dependency, as follows:
AsGroup depends on AsgConfig
AsgConfig depends on ECSCluster because of echo ECS_CLUSTER=${ECSCluster} in user data
ECSCluster depends on CapacityProvider
CapacityProvider depends on AsGroup (which is #1 above)
I suggest that instead of configuring the ECSCluster with the CapacityProvider, you simply create the ECSCluster without a capacity provider (and without the DependsOn) and add a later AWS::ECS::ClusterCapacityProviderAssociations to associate the CapacityProvider with the ECSCluster.
For example (note that I have not tested this so some tweaks may be required):
CapacityProvider:
Type: AWS::ECS::CapacityProvider
DependsOn:
- AsGroup
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref AsGroup
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref ClientName
Configuration:
ExecuteCommandConfiguration:
Logging: DEFAULT
ClusterSettings:
- Name: containerInsights
Value: enabled
ClusterCapacityProviderAssociation:
Type: AWS::ECS::ClusterCapacityProviderAssociations
Properties:
CapacityProviders:
- !Ref CapacityProvider
Cluster: ECSCluster
DefaultCapacityProviderStrategy:
- Base: 0
Weight: 1
CapacityProvider: !Ref CapacityProvider
I'm trying to setup via CloudFormation an EFS mount for self-hosted Prometheus. Below is the CloudFormation for my setup:
Resources:
ServiceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Prometheus SG'
VpcId:
Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
SecurityGroupIngress:
# Allow access from the Load Balancer only
- SourceSecurityGroupId:
Fn::ImportValue: !Sub '${LBStackName}-SG-LB'
IpProtocol: tcp
FromPort: 9090
ToPort: 9090
Tags:
- Key: Name
Value: !Sub 'SG-Prometheus-LB-${Stage}'
EFSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'Prometheus EFS SG'
VpcId:
Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
SecurityGroupIngress:
# Allow access from the ECS Service only
- SourceSecurityGroupId: !Ref ServiceSecurityGroup
IpProtocol: tcp
FromPort: 2049
ToPort: 2049
Tags:
- Key: Name
Value: !Sub 'SG-Prometheus-EFS-${Stage}'
MountTarget1:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId:
Fn::ImportValue: !Sub '${DataStackName}-EFSID'
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId:
Fn::ImportValue: !Sub '${NetworkStackName}-PRIVATE-SUBNET-A1'
MountTarget2:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId:
Fn::ImportValue: !Sub '${DataStackName}-EFSID'
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId:
Fn::ImportValue: !Sub '${NetworkStackName}-PRIVATE-SUBNET-B1'
Prometheus:
Type: AWS::ECS::Service
DependsOn: ListenerRule
Properties:
Cluster: !Ref Cluster
LaunchType: FARGATE
DesiredCount: !FindInMap [ ECSTaskDefinition, !Ref Stage, DesiredTaskCount ]
TaskDefinition: !Ref TaskDefinition
HealthCheckGracePeriodSeconds: 300
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
LoadBalancers:
- ContainerName: 'Prometheus-Container'
ContainerPort: 9090
TargetGroupArn: !Ref TargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
SecurityGroups:
- !Ref ServiceSecurityGroup
Subnets:
- Fn::ImportValue: !Sub '${NetworkStackName}-PRIVATE-SUBNET-A1'
- Fn::ImportValue: !Sub '${NetworkStackName}-PRIVATE-SUBNET-B1'
EnableECSManagedTags: true
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: Prometheus
RequiresCompatibilities:
- FARGATE
Cpu: !FindInMap [ ECSTaskDefinition, !Ref Stage, CPU ]
Memory: !FindInMap [ ECSTaskDefinition, !Ref Stage, Memory ]
ContainerDefinitions:
- Name: 'Prometheus-Container'
Essential: true
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RepositoryName}:${ImageTag}'
Cpu: !FindInMap [ ECSTaskDefinition, !Ref Stage, CPU ]
Memory: !FindInMap [ ECSTaskDefinition, !Ref Stage, Memory ]
PortMappings:
- ContainerPort: 9090
MountPoints:
- SourceVolume: 'Prometheus-Volume'
ContainerPath: '/prometheus'
ReadOnly: false
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group:
Fn::ImportValue: !Sub '${DataStackName}-CW-LogsGroup'
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: 'PrometheusApp'
Volumes:
- Name: 'Prometheus-Volume'
EFSVolumeConfiguration:
FilesystemId:
Fn::ImportValue: !Sub '${DataStackName}-EFSID'
RootDirectory: "/"
TransitEncryption: ENABLED
NetworkMode: awsvpc
TaskRoleArn:
Fn::ImportValue: !Sub '${LBStackName}-TaskRoleArn'
ExecutionRoleArn: !GetAtt ExecutionRole.Arn
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId:
Fn::ImportValue: !Sub '${NetworkStackName}-VPCID'
TargetType: ip
Port: 9090
Protocol: HTTP
Matcher:
HttpCode: '200'
TargetGroupAttributes:
- Key: 'deregistration_delay.timeout_seconds'
Value: '60'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /status
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 2
HealthyThresholdCount: 2
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn:
Fn::ImportValue: !Sub '${LBStackName}-LB-LISTENER'
Priority: 2
Conditions:
- Field: path-pattern
Values:
- /*
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
TaskPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: 'Prometheus-TaskPolicy'
Roles:
- Fn::ImportValue: !Sub '${LBStackName}-TaskRole'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: EnablePutMetricData
Effect: Allow
Resource: '*'
Action:
- cloudwatch:PutMetricData
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'
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub "Prometheus-${Stage}"
Everything gets deployed successfully but the containers die with the error message: ResourceInitializationError: failed to invoke EFS utils commands to set up EFS volumes: stderr: Failed to resolve "fs-xxxxxxxxxxx.efs.us-east-1.amazonaws.com. I've checked https://aws.amazon.com/premiumsupport/knowledge-center/fargate-unable-to-mount-efs/ and don't think we have an issue with zones.
Any ideas welcome.
Error:
service was unable to place a task because no container instance met all of its requirements. Reason: No Container Instances were found in your cluster
I see the list of resources are properly created:
VPC, subnets, route tables, internet gateways, NatGW, EC2 instance, security groups, load balancer.
Ec2 instance is up and running but still the deployment is stuck in progress and times out with rollback state.
I added the signaling script as well:
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
Don't know what else is missing.
Cloud formation template:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
VPCEnv:
Type: String
MinLength: 1
Description: 'The id for references to test Services created items.'
Environment:
Type: String
Description: 'Environment to create backend infra for'
KeyName:
Type: String
Description: 'Name of an existing EC2 KeyPair to enable SSH access to the ECS instances.'
DesiredCapacity:
Type: String
Default: '1'
Description: 'Number of instances to launch in your ECS cluster.'
MaxSize:
Type: String
Default: '1'
Description: Maximum number of instances that can be launched in your ECS cluster.
InstanceType:
Description: 'EC2 instance type'
Type: String
Default: 't2.medium'
BackendContainerImage:
Type: String
MinLength: 1
Version:
Type: String
MinLength: 1
AMIID:
Type: String
MinLength: 1
Resources:
ExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: ['ecs-tasks.amazonaws.com']
Action: ['sts:AssumeRole']
Policies:
- PolicyName: !Sub test-${Environment}-execution-user-role
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession',
'ecs:UpdateContainerInstancesState', 'ecs:Submit*', 'ecr:GetAuthorizationToken',
'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage',
'logs:CreateLogStream', 'logs:PutLogEvents', 'ssm:GetParameter', 'kms:Decrypt', 'ssm:GetParameters']
Resource: '*'
ECSCluster:
Type: AWS::ECS::Cluster
EcsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS Security Group
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
SourceSecurityGroupId:
Fn::ImportValue: !Sub "${VPCEnv}-BastionSecurityGroup"
-
IpProtocol: tcp
FromPort: '31000'
ToPort: '61000'
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "test-${Environment}-LBSecurityGroup"
GroupDescription: test service Load Balancer Security Group
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
SourceSecurityGroupId:
Fn::ImportValue: !Sub "${VPCEnv}-APILoadBalancerSecurityGroup"
testServiceTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Sub 'test-${Environment}'
ExecutionRoleArn: !Ref ExecutionRole
ContainerDefinitions:
- Name: !Sub 'test-${Environment}-container'
Cpu: 600
Essential: 'true'
Image: !Ref BackendContainerImage
Memory: 1800
PortMappings:
- ContainerPort: 3000
ECSALBDNS:
Type: "AWS::Route53::RecordSet"
Properties:
AliasTarget:
DNSName: !GetAtt [ ECSALB, DNSName ]
HostedZoneId: !GetAtt [ ECSALB, CanonicalHostedZoneID ]
Comment: Internal DNS entry for audit service load balancer.
HostedZoneId: Z03303053NOQR6YO05FA7
Name: !Sub "api.internal.audit.service.${Environment}.altusplatform.com."
Type: A
ECSALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "test-${Environment}-lb"
Scheme: internal
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '20'
Subnets:
Fn::Split:
- ','
- Fn::ImportValue: !Sub "${VPCEnv}-PrivateSubnets2"
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
- Fn::ImportValue : !Sub "${VPCEnv}-APILoadBalancerSecurityGroup"
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn: ECSServiceRole
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
LoadBalancerArn: !Ref 'ECSALB'
Port: '80'
Protocol: HTTP
ECSALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
DependsOn: ALBListener
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref 'ECSTG'
Conditions:
- Field: path-pattern
Values: [/]
ListenerArn: !Ref 'ALBListener'
Priority: 1
ECSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn: ECSALB
Properties:
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Name: !Sub "test-${Environment}-tg"
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId:
Fn::ImportValue: !Sub "${VPCEnv}-VPC"
ECSCapacityProvider:
Type: AWS::ECS::CapacityProvider
Properties:
AutoScalingGroupProvider:
AutoScalingGroupArn: !Ref 'ECSAutoScalingGroup'
ManagedScaling:
MaximumScalingStepSize: 10
MinimumScalingStepSize: 1
Status: ENABLED
TargetCapacity: 100
Tags:
- Key: environment
Value: !Sub '${Environment}'
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
Fn::Split:
- ','
- Fn::ImportValue: !Sub "${VPCEnv}-PrivateSubnets2"
LaunchConfigurationName: !Ref 'ContainerInstances'
MinSize: '1'
MaxSize: !Ref 'MaxSize'
DesiredCapacity: !Ref 'DesiredCapacity'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !Sub '${AMIID}'
SecurityGroups: [!Ref 'EcsSecurityGroup']
InstanceType: !Ref 'InstanceType'
IamInstanceProfile: !Ref 'EC2InstanceProfile'
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
yum update -y
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
yum install -y awslogs jq
region=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
sed -i -e "s/region = us-east-1/region = $region/g" /etc/awslogs/awscli.conf
yum install -y https://amazon-ssm-$region.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm
service:
Type: AWS::ECS::Service
DependsOn: ALBListener
Properties:
Cluster: !Ref 'ECSCluster'
DesiredCount: '2'
LoadBalancers:
- ContainerName: !Sub 'test-${Environment}-container'
ContainerPort: 3000
TargetGroupArn: !Ref 'ECSTG'
Role: !Ref 'ECSServiceRole'
TaskDefinition: !Ref 'testServiceTaskDefinition'
DeploymentConfiguration:
MaximumPercent: 150
MinimumHealthyPercent: 50
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['elasticloadbalancing:DeregisterInstancesFromLoadBalancer', 'elasticloadbalancing:DeregisterTargets',
'elasticloadbalancing:Describe*', 'elasticloadbalancing:RegisterInstancesWithLoadBalancer',
'elasticloadbalancing:RegisterTargets', 'ec2:Describe*', 'ec2:AuthorizeSecurityGroupIngress']
Resource: '*'
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
DependsOn: service
Properties:
MaxCapacity: 1
MinCapacity: 1
ResourceId: !Join ['', [service/, !Ref 'ECSCluster', /, !GetAtt [service, Name]]]
RoleARN: !GetAtt [AutoscalingRole, Arn]
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: AStepPolicy
PolicyType: StepScaling
ScalingTargetId: !Ref 'ServiceScalingTarget'
StepScalingPolicyConfiguration:
AdjustmentType: PercentChangeInCapacity
Cooldown: 60
MetricAggregationType: Average
StepAdjustments:
- MetricIntervalLowerBound: 0
ScalingAdjustment: 200
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint',
'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession', 'ecs:UpdateContainerInstancesState',
'ecs:Submit*', 'ecr:GetAuthorizationToken', 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchGetImage',
'logs:CreateLogStream', 'logs:PutLogEvents']
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action: ['application-autoscaling:*', 'cloudwatch:DescribeAlarms', 'cloudwatch:PutMetricAlarm',
'ecs:DescribeServices', 'ecs:UpdateService']
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
Outputs:
ecsservice:
Value: !Ref 'service'
ecscluster:
Value: !Ref 'ECSCluster'
ECSALB:
Description: Your ALB DNS URL
Value: !Join ['', [!GetAtt [ECSALB, DNSName]]]
taskdef:
Value: !Ref 'testServiceTaskDefinition'
Exported values:
Update: Added the ECSCapacityProvider with no luck
I have encountered the above issue when configuring autoscaling with an ECS Cluster. In order for autoscaling to correctly report the instances it creates back to the ECS Cluster an autoscaling group capacity provider needs to be created:
Type: AWS::ECS::CapacityProvider
Properties:
AutoScalingGroupProvider:
AutoScalingGroupProvider
Name: String
Tags:
- Tag
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-capacityprovider.html
service cc-ui-service was unable to place a task because no container instance met all of its requirements. Reason: No Container Instances were found in your cluster.
I too got this error and from the beginning there was not even a cluster. So maybe you can use my effort (done with Fargate but some of it could also work for ec2).
Parameters:
Image:
Description: The docker image
Type: String
Default: f00b4r
ImageVersion:
Description: The docker image version
Type: String
Default: "1.0.42"
LogGroupName:
Description: The CloudWatch log group
Type: String
Default: f00b4r-logs
Resources:
AvTestCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: NewCluster
ServiceTaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-TaskRole"
Path: "/"
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ecs-tasks.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-TaskRolePolicy"
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:*
- dynamodb:*
Resource: "*" # TODO: Set to least privilege
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: ExecutionRoleFargate
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
ServiceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: vpc-02d42422a429042
GroupDescription: Access to the ECS Service
SecurityGroupIngress:
- CidrIp: 42.42.42.42/16
IpProtocol: -1
EcsService:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref 'TestCluster'
DesiredCount: '1'
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 100
MinimumHealthyPercent: 0
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- !Ref ServiceSecurityGroup
Subnets:
- subnet-0345b4296042a84
- subnet-02f3452b9c142de
TaskDefinition: !Ref 'TaskDefinition'
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: new-latest
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: 0.5GB
ExecutionRoleArn: !Ref ExecutionRole
TaskRoleArn: !GetAtt ServiceTaskRole.Arn
ContainerDefinitions:
- Name: new-latest
Essential: true
Image:
Fn::Join:
- ""
- - "84272424226"
- ".dkr.ecr.eu-west-1.amazonaws.com/"
- !Ref Image
- ":"
- !Ref ImageVersion
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group:
!Ref LogGroupName
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: new-latest
Memory: 128
Command: ["sh", "-c", !Join [ "", [ "echo HelloFromFargate" ] ] ]
RestoreUptimeQuotationDDBECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: NewPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: 'ecs:RunTask'
Resource: !Ref TaskDefinition
LogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
RetentionInDays: 30
LogGroupName:
!Ref LogGroupName
I'm recently learning ECS from AWS documents from Module Two - Deploy the Monolith | AWS.
While I read the YAML file for the CloudFormation, the file creates two EC2 instances in the cluster and also specified two public subnets in the VPC. I'm new to the VPC, so is it because of the creation of 2 EC2 instances so two public subnets are needed?
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
DesiredCapacity:
Type: Number
Default: '2'
Description: Number of instances to launch in your ECS cluster.
MaxSize:
Type: Number
Default: '2'
Description: Maximum number of instances that can be launched in your ECS cluster.
InstanceType:
Description: EC2 instance type
Type: String
Default: t2.micro
AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large,
m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge,
c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge,
c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge,
r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge]
ConstraintDescription: Please choose a valid instance type.
Mappings:
AWSRegionToAMI:
us-east-1:
AMIID: ami-eca289fb
us-east-2:
AMIID: ami-446f3521
us-west-1:
AMIID: ami-9fadf8ff
us-west-2:
AMIID: ami-7abc111a
eu-west-1:
AMIID: ami-a1491ad2
eu-central-1:
AMIID: ami-54f5303b
ap-northeast-1:
AMIID: ami-9cd57ffd
ap-southeast-1:
AMIID: ami-a900a3ca
ap-southeast-2:
AMIID: ami-5781be34
SubnetConfig:
VPC:
CIDR: '10.0.0.0/16'
PublicOne:
CIDR: '10.0.0.0/24'
PublicTwo:
CIDR: '10.0.1.0/24'
Resources:
# VPC into which stack instances will be placed
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
MapPublicIpOnLaunch: true
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
MapPublicIpOnLaunch: true
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'VPC'
InternetGatewayId: !Ref 'InternetGateway'
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
Properties:
RouteTableId: !Ref 'PublicRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref 'InternetGateway'
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
# ECS Resources
ECSCluster:
Type: AWS::ECS::Cluster
EcsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ECS Security Group
VpcId: !Ref 'VPC'
EcsSecurityGroupHTTPinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
EcsSecurityGroupSSHinbound:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
EcsSecurityGroupALBports:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref 'EcsSecurityGroup'
IpProtocol: tcp
FromPort: '31000'
ToPort: '61000'
SourceSecurityGroupId: !Ref 'EcsSecurityGroup'
CloudwatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [ECSLogGroup, !Ref 'AWS::StackName']]
RetentionInDays: 14
ECSALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: demo
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups: [!Ref 'EcsSecurityGroup']
ECSAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
LaunchConfigurationName: !Ref 'ContainerInstances'
MinSize: '1'
MaxSize: !Ref 'MaxSize'
DesiredCapacity: !Ref 'DesiredCapacity'
CreationPolicy:
ResourceSignal:
Timeout: PT15M
UpdatePolicy:
AutoScalingReplacingUpdate:
WillReplace: 'true'
ContainerInstances:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !FindInMap [AWSRegionToAMI, !Ref 'AWS::Region', AMIID]
SecurityGroups: [!Ref 'EcsSecurityGroup']
InstanceType: !Ref 'InstanceType'
IamInstanceProfile: !Ref 'EC2InstanceProfile'
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
yum install -y aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}
ECSServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer'
- 'elasticloadbalancing:DeregisterTargets'
- 'elasticloadbalancing:Describe*'
- 'elasticloadbalancing:RegisterInstancesWithLoadBalancer'
- 'elasticloadbalancing:RegisterTargets'
- 'ec2:Describe*'
- 'ec2:AuthorizeSecurityGroupIngress'
Resource: '*'
EC2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'ecs:CreateCluster'
- 'ecs:DeregisterContainerInstance'
- 'ecs:DiscoverPollEndpoint'
- 'ecs:Poll'
- 'ecs:RegisterContainerInstance'
- 'ecs:StartTelemetrySession'
- 'ecs:Submit*'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
- 'ecr:GetAuthorizationToken'
- 'ecr:BatchGetImage'
- 'ecr:GetDownloadUrlForLayer'
Resource: '*'
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'application-autoscaling:*'
- 'cloudwatch:DescribeAlarms'
- 'cloudwatch:PutMetricAlarm'
- 'ecs:DescribeServices'
- 'ecs:UpdateService'
Resource: '*'
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles: [!Ref 'EC2Role']
Outputs:
ClusterName:
Description: The name of the ECS cluster, used by the deploy script
Value: !Ref 'ECSCluster'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ClusterName" ]]
Url:
Description: The url at which the application is available
Value: !Join ['', [!GetAtt 'ECSALB.DNSName']]
ALBArn:
Description: The ARN of the ALB, exported for later use in creating services
Value: !Ref 'ECSALB'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ALBArn" ]]
ECSRole:
Description: The ARN of the ECS role, exports for later use in creating services
Value: !GetAtt 'ECSServiceRole.Arn'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "ECSRole" ]]
VPCId:
Description: The ID of the VPC that this stack is deployed in
Value: !Ref 'VPC'
Export:
Name: !Join [':', [!Ref "AWS::StackName", "VPCId" ]]
In your example, two AZs are being used which requires two subnets (one for each AZ). This is not related to the number of EC2 instances.
A typical best practices with AWS and other cloud vendors is to use multiple availability zones (AZ) for fault tolerance. For AWS each AZ needs its own subnet. Auto scaling and load balancing will attempt to keep the number of instances the same in each AZ.
PS. If I was learning AWS, I would not start with this example. This example is very complex but very realistic for a real world deployment. There are lots of cloudformation examples that are much easy to master to start with.