In my Cloudformation template, I am using the blue-green deployment trigger and have the following TaskDefinition
TaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn: LogGroup
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ExecutionRoleArn: arn:aws:iam::579072907853:role/ecsTaskExecutionRole
TaskRoleArn: !Ref ServiceRole
ContainerDefinitions:
- Name: !Ref ServiceName
Essential: true
Image: !Ref ImageARN
PortMappings:
- ContainerPort: !Ref ContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Ref AWS::Region
awslogs-group: !Ref LogGroup
awslogs-stream-prefix: ecs
Environment:
- Name: RACK_ENV
Value: !Ref EnvironmentName
If I update the stack with a new value for the ImageARN parameter, I get the following error
'CodeDeployBlueGreenHook' of type AWS::CodeDeploy::BlueGreen failed with message: The submitted template's primary task definition 'TaskDefinition' was never updated. The template's 'AWS::CodeDeployBlueGreen' Transform might be missing or removed
If I don't use a parameter but instead use a hardcoded Image then update the template it works fine.
Is there a way to use a parameter?
Example
Below is a full example of a template that has this issue. I change the ImageARN parameter between these two images
nginx:1.18.0#sha256:001487e0a8c6abf91351ab45fdb308c0c2b95e8a67260b7ca5cf064462689a9e
nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Image:
Type: String
CIDRBlock:
Type: String
Default: 192.168.5.0/24
Description: CIDR block for VPC
ExecutionRoleARN:
Type: String
Transform:
- AWS::CodeDeployBlueGreen
Hooks:
CodeDeployBlueGreenHook:
Type: AWS::CodeDeploy::BlueGreen
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:
Type: AWS::ECS::Service
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: AWS::ElasticLoadBalancingV2::Listener
LogicalID: HTTPListener
TargetGroups:
- TargetGroup
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups: [!Ref ALBSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TargetGroupGreen:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ExecutionRoleArn: !Ref ExecutionRoleARN
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref Image
PortMappings:
- ContainerPort: 80
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups: [!Ref FargateSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
ServiceName: service
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt 'TaskSet.Id'
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CIDRBlock
EnableDnsHostnames: true
EnableDnsSupport: true
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [0, !Cidr [!Ref CIDRBlock, 2, 6 ]]
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [1, !Cidr [!Ref CIDRBlock, 2, 6 ]]
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayVPCAssoc:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
RouteTableSub1Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTable
SubnetId: !Ref Subnet1
RouteTableSub2Assoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTable
SubnetId: !Ref Subnet2
InternetGatewayRoute:
Type: AWS::EC2::Route
DependsOn: InternetGateway
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
I looked at your example, and what I have in my own projects. I can confirm that your example does not work as expected.
I investigated more and determined that the issue is likely to be due to creation of entire networking stack in your template. In my own projects I don't have VPC or subnets in my ecs templates, thus it all works.
So once I removed the VPC related things from your template it started to work as required. There are some other smaller changes, but they may not be necessary. I think the VPC-stuff is the main reason.
Also, please update the template by using Replace current template option, rather then Use current template. If you use the second option it will still not work. I think this is due to how macros work in CFN, and template should be replaced, rather then updated in-place. If you replace it, B/G in CodeDeploy is successful trigger and you can view it CodeDeploy console.
Here is the modified template:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
ImageUrl:
Type: String
VpcId:
Type: 'AWS::EC2::VPC::Id'
Subnet1:
Type: 'AWS::EC2::Subnet::Id'
Subnet2:
Type: 'AWS::EC2::Subnet::Id'
ExecutionRoleARN:
Type: String
DesiredCount:
Type: Number
Default: 2
Transform:
- 'AWS::CodeDeployBlueGreen'
Hooks:
CodeDeployBlueGreenHook:
Type: 'AWS::CodeDeploy::BlueGreen'
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
Type: 'AWS::ECS::Service'
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
LogicalID: HTTPListener
TargetGroups:
- TargetGroupnginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
#Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets:
- !Ref Subnet1
- !Ref Subnet2
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalannginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054cer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
#Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref VpcId
TargetGroupGreen:
Type: nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054AWS::ElasticLoadBalancingV2nginx:1.19.4#sha256:aec3f367f48745b280ee2fd8d8469c0c0ec6b9b2fca3cd3e6cff03e1b69ae054::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
#Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref VpcId
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 256
Memory: 512
ExecutionRoleArn: !Ref ExecutionRoleARN
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref ImageUrl
PortMappings:
- ContainerPort: 80
Protocol: tcp
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateSecurityGroup
Subnets:
- !Ref Subnet1
- !Ref Subnet2
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
#ServiceName: service
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: !Ref DesiredCount
EnableECSManagedTags: true
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt TaskSet.Id
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
#ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
Outputs:
ALBDNSName:
Value: !GetAtt LoadBalancer.DNSName
When I had this issue the solution was to replace the Image value within a step in the pipeline before the AWS CloudFomration step, basically "building" the file before deployment instead of passing parameters. Is not fancy but it works, I expect there are better solutions to this.
Related
Having no background with aws/devops and after a lot of efforts and trial/error I succeeded building the here bellow stack for my django app:
The stack relies heavily on celery which consumes a lot of tasks so I migrated to sqs for cost reasons (instead of aws redis). For the same reason I decided to disable the nat gateways because it costs so much and I rely only on security groups and acl for security
Initially I had two services: One with nginx, Django, celery,
pgadmin4, flower, and a second with Celery desired count 0 to scale
up on heavy loads. When celery starts working it tend to consume 100%
CPU and launch a lot of process taking all the available connections
of postgres, new process fail, so I added pgbouncer for connection
pooling
I decided to migrate to 3 services, one admin service with nginx,
pgbouncer and pgadmin4, a second non scalable minimal service with
django and one celery worker, and a third scalable celery service
with desired count to 0 which will be launched and scaled down by an
alarm on sqs queues. (I am also considering a fourth service with
django and a desired count to 0 which will be launched and scaled
down by an alarm on elb target response time).
The actual problem is the following : my django and celery containers
(in the django app service) talk with pgbouncer without a problem,
but the nginx container (in the admin service) can't reach the django
container ?
I tried playing with the securitygroups without success (I'm no
expert), any thoughts guys?
Thank you.
Here's the stack code:
# VPC Template Parameters
CIDRBlockIP:
Description: The first two values of the IP for the VPC
Type: String
MinLength: '3'
MaxLength: '7'
Default: 10.20
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})"
ConstraintDescription: must be a valid start to an IP range of the form x.x
AvailabilityZone1:
Description: The first availability zone in the region
Default: us-west-2a
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
AvailabilityZone2:
Description: The second availability zone in the region
Default: us-west-2b
Type: AWS::EC2::AvailabilityZone::Name
ConstraintDescription: Must be a valid availability zone
ELBIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 443
ELBHttpIngressPort:
Description: The ELB ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
AppIngressPort:
Description: The application ingress port used by security groups
Type: Number
MinValue: 0
MaxValue: 65535
ConstraintDescription: TCP ports must be between 0 - 65535
Default: 80
DbInVpc:
Type: String
Default: true
AllowedValues:
- true
- false
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Region Availability Zones
Parameters:
- AvailabilityZone1
- AvailabilityZone2
- Label:
default: Ingress Ports
Parameters:
- ELBIngressPort
- AppIngressPort
ParameterLabels:
AvailabilityZone1:
default: Availability Zone 1
AvailabilityZone2:
default: Availability Zone 2
ELBIngressPort:
default: Load Balancer Port
AppIngressPort:
default: Application Port
Conditions:
CreateDbSecurityGroup: !Equals [ !Ref DbInVpc, true ]
Mappings:
# Django App Mappings
appConfigs:
prod:
loadbalancerCertArn: "xxxx"
# Maps CIDR blocks to VPC and various subnets
CIDRMap:
VPC:
CIDR: ".0.0/16"
Public1:
CIDR: ".2.0/24"
Public2:
CIDR: ".3.0/24"
Private1:
CIDR: ".64.0/19"
Private2:
CIDR: ".96.0/19"
Resources:
###############
# VPC Template#
###############
VPC:
Type: AWS::EC2::VPC
UpdateReplacePolicy: Delete
Properties:
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, VPC, CIDR ]
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref "AWS::StackName"
PublicSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet1"
PublicSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Public2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet2"
PrivateSubnet1:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private1, CIDR ]
AvailabilityZone: !Ref AvailabilityZone1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2:
Type: AWS::EC2::Subnet
UpdateReplacePolicy: Delete
DependsOn:
- PublicSubnet1
- PrivateSubnet1
Properties:
VpcId: !Ref VPC
CidrBlock: !Sub
- "${beg_ip}${sec_val}"
- beg_ip: !Ref CIDRBlockIP
sec_val: !FindInMap [ CIDRMap, Private2, CIDR ]
AvailabilityZone: !Ref AvailabilityZone2
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet2"
InternetGateway:
Type: AWS::EC2::InternetGateway
UpdateReplacePolicy: Delete
Properties:
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-igw"
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
UpdateReplacePolicy: Delete
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-public-rt"
PublicRoute:
Type: AWS::EC2::Route
UpdateReplacePolicy: Delete
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PublicSubnetNetworkAclAssociation1:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet1
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
PublicSubnetNetworkAclAssociation2:
Type: AWS::EC2::SubnetNetworkAclAssociation
UpdateReplacePolicy: Delete
Properties:
SubnetId: !Ref PublicSubnet2
NetworkAclId: !GetAtt VPC.DefaultNetworkAcl
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable HTTP/HTTPs ingress
VpcId: !Ref VPC
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBIngressPort
FromPort: !Ref ELBIngressPort
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: !Ref ELBHttpIngressPort # ALB should redirect to HTTPS
FromPort: !Ref ELBHttpIngressPort
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ELBSecurityGroup"
ELBSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
DestinationSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access from ELB to app
VpcId: !Ref VPC
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref ELBSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
- SourceSecurityGroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
#!!! New, to enable intra vpc communication ??!!
- CidrIp: '0.0.0.0/0'
IpProtocol: tcp
ToPort: 80
FromPort: 80
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-AppSecurityGroup"
AppSecurityGroupFromELBIngress:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: !Ref AppIngressPort
FromPort: !Ref AppIngressPort
SourceSecurityGroupId: !Ref ELBSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressTcp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: tcp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
AppSecurityGroupFromAppSecurityGroupIngressUdp:
Type: AWS::EC2::SecurityGroupIngress
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref AppSecurityGroup
IpProtocol: udp
ToPort: 65535
FromPort: 0
SourceSecurityGroupId: !Ref AppSecurityGroup
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Properties:
GroupDescription: Enable access to the bastion host
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 65535
FromPort: 0
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-BastionSecurityGroup"
BastionSecurityGroupToAppEgress:
Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references
UpdateReplacePolicy: Delete
Properties:
GroupId: !Ref BastionSecurityGroup
IpProtocol: tcp
ToPort: 22
FromPort: 22
DestinationSecurityGroupId: !Ref AppSecurityGroup
#####################################################
# Database security groups, only create if required #
#####################################################
DbSecurityGroup:
Type: AWS::EC2::SecurityGroup
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupDescription: Enable access to the RDS DB
VpcId: !Ref VPC
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-DbSecurityGroup"
DbSecurityGroupFromBastionIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379 # Redis
FromPort: 6379
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromBastionIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432 # Postgres
FromPort: 5432
SourceSecurityGroupId: !Ref BastionSecurityGroup
DbSecurityGroupFromAppIngressRedis:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 6379
FromPort: 6379
SourceSecurityGroupId: !Ref AppSecurityGroup
DbSecurityGroupFromAppIngressPostgres:
Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references
UpdateReplacePolicy: Delete
Condition: CreateDbSecurityGroup
Properties:
GroupId: !Ref DbSecurityGroup
IpProtocol: tcp
ToPort: 5432
FromPort: 5432
SourceSecurityGroupId: !Ref AppSecurityGroup
#################
# Admin Apps Service#
#################
AdminAppsTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- AdminAppsLogGroup
- NginxRepository
- Pgadmin4Repository
- PgbouncerRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref AdminAppsServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_nginx"
Image: !Sub "${NginxRepository.RepositoryUri}:latest"
Essential: true
PortMappings:
- ContainerPort: !Ref AppContainerPort
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Nginx
- Name: !Sub "${AwsIamUserId}_pgbouncer"
Image: !Sub "${PgbouncerRepository.RepositoryUri}:latest"
PortMappings:
- ContainerPort: 5432
Protocol: tcp
Essential: true
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgbouncer
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: !Sub "${AwsIamUserId}_pgadmin4"
Image: !Sub "${Pgadmin4Repository.RepositoryUri}:latest"
Essential: false
PortMappings:
- ContainerPort: 5050
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref AdminAppsLogGroup
awslogs-stream-prefix: Pgadmin4
Command:
- "/generate_servers_json_and_pgpass_conf_files"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
AdminAppsService:
Type: AWS::ECS::Service
DependsOn:
- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref AdminAppsServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref AdminAppsTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
LoadBalancers:
- ContainerName: !Sub "${AwsIamUserId}_nginx"
ContainerPort: !Ref AppContainerPort
TargetGroupArn: !Ref DjangoAppTargetGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Django Service#
#################
DjangoAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- DjangoAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref DjangoAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: "0.5GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: !Sub "${AwsIamUserId}_django_celery"
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: true
#Cpu: 128
#Memory: 256
PortMappings:
- ContainerPort: 8000
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: DjangoAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
Essential: false
#Cpu: 128
#MemoryReservation: 256
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref DjangoAppLogGroup
awslogs-stream-prefix: CeleryAppContainer
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
DjangoAppService:
Type: AWS::ECS::Service
DependsOn:
#- DjangoAppHttpsListener
- GeneralECSCluster
Properties:
ServiceName: !Ref DjangoAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref DjangoAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DesiredCount: 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#################
# Celery Service#
#################
CeleryAppTaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn:
- CeleryAppLogGroup
- DjangoCeleryRepository
Properties:
Family: !Join ['-', [!Ref AwsIamUserId, !Ref CeleryAppServiceName, TaskDefinition]]
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 512
Memory: "1GB" #512 (0.5GB), 1024 (1GB), 2048 (2GB), 3072 (3GB), 4096 (4GB)
ExecutionRoleArn: !GetAtt ECSExecutionRole.Arn
TaskRoleArn: !GetAtt EcsTaskRole.Arn
ContainerDefinitions:
- Name: celery-default-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-default-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
- Name: celery-long-running-worker
Image: !Sub "${DjangoCeleryRepository.RepositoryUri}:latest"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Sub ${AwsRegion}
awslogs-group: !Ref CeleryAppLogGroup
awslogs-stream-prefix: Celery
EntryPoint:
- "/entrypoint"
Command:
- "/start-celery-long-running-worker"
Secrets:
- Name: DJANGO_ECS_SECRETS
ValueFrom: !Ref DjangoSecrets
- Name: AWS_ACCESS_CREDENTIALS
ValueFrom: !Ref AwsAccessSecrets
Environment:
CeleryAppService:
Type: AWS::ECS::Service
Properties:
ServiceName: !Ref CeleryAppServiceName
Cluster: !Ref GeneralECSCluster
TaskDefinition: !Ref CeleryAppTaskDefinition
DeploymentConfiguration:
MinimumHealthyPercent: 0
MaximumPercent: 200
DesiredCount: 0 #! this scales down the service to 0 task, if you want 1 minimum task always runing change to 1
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED #!! Switch to DISABLED to use nat, if DISABLED uncomment the nat section above of this file
Subnets:
#- !Ref PrivateSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
#- !Ref PrivateSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet1 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
- !Ref PublicSubnet2 #!! uncomment this to use nat, if enable uncomment the nat section above of this file
SecurityGroups:
- !Ref AppSecurityGroup
ServiceRegistries:
- RegistryArn: !GetAtt DiscoveryService.Arn
#####################################################################################
# Service Discovery to enable communication between Django and Celery Service #
#####################################################################################
# Private Namespace for Service Discovery
# https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-ecs-service-discovery/
PrivateNamespace:
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Properties:
Name: !Sub "${AwsIamUserId}_local"
Vpc: !Ref VPC
DiscoveryService:
Type: AWS::ServiceDiscovery::Service
Properties:
Description: Discovery Service to enable communication between Django and Celery Service
DnsConfig:
RoutingPolicy: WEIGHTED #MULTIVALUE
DnsRecords:
- TTL: 60
Type: A
HealthCheckCustomConfig:
FailureThreshold: 1
Name: discovery-service
NamespaceId: !Ref PrivateNamespace
###########################
# Load balancer resources #
###########################
DjangoAppLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: 'idle_timeout.timeout_seconds'
Value: 4000
- Key: 'routing.http2.enabled'
Value: 'true'
- Key: 'access_logs.s3.enabled'
Value: 'false'
Scheme: internet-facing
SecurityGroups:
- !Ref ELBSecurityGroup
Subnets:
- !Ref PublicSubnet1
- !Ref PublicSubnet2
Type: application
DjangoAppHttpsListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
Certificates:
- CertificateArn: !FindInMap [ appConfigs, !Ref EnvStageName, loadbalancerCertArn ]
DefaultActions:
- TargetGroupArn: !Ref DjangoAppTargetGroup
Type: forward
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 443
Protocol: HTTPS
DjangoAppHttpListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- RedirectConfig:
Host: "#{host}"
Path: "/#{path}"
Port: 443
Protocol: "HTTPS"
Query: "#{query}"
StatusCode: HTTP_301
Type: redirect
LoadBalancerArn: !Ref DjangoAppLoadBalancer
Port: 80
Protocol: HTTP
DjangoAppTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: !Ref AppContainerPort
HealthCheckPath: '/health/'
HealthCheckProtocol: HTTP
HealthCheckIntervalSeconds: 90
HealthCheckTimeoutSeconds: 60
HealthyThresholdCount: 2
UnhealthyThresholdCount: 10
Matcher:
HttpCode: '200,301'
Protocol: HTTP
TargetType: ip
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
VpcId: !Ref VPC
I am trying to translate a manually created service to a cloudformation template but I keep getting errors.
Task definition is already created with UI because it needs some specific roles
This template gives me: Classic Load Balancers are not supported with Fargate
ServicesSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for cluster services
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref "PublicLoadBalancerSG"
ServiceStaging:
Type: AWS::ECS::Service
Properties:
ServiceName: pouch-svc-staging
TaskDefinition: pouch-td-staging:4
Cluster: !Ref 'ClusterECS'
DesiredCount: 2
SchedulingStrategy: REPLICA
LaunchType: FARGATE
EnableECSManagedTags: true
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DeploymentCircuitBreaker:
Enable: false
Rollback: false
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups:
- !Ref ServicesSG
LoadBalancers:
- ContainerName: pouch-image-staging
LoadBalancerName: !Ref 'LoadBalancerName'
ContainerPort: 3100
Update: Here is the modified full cloudformation template without a load balancer name as explicited by some comments
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC, subnets and external, public facing load balancer, for forwarding public traffic to containers
Parameters:
LoadBalancerName:
Type: String
Default: pouch-api-elb
ClusterName:
Type: String
Default: pouch-api-cluster
Mappings:
SubnetConfig:
VPC:
CIDR: '172.16.0.0/16'
PublicOne:
CIDR: '172.16.0.0/24'
PublicTwo:
CIDR: '172.16.1.0/24'
Resources:
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
PublicLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the public facing load balancer
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
PublicLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref 'LoadBalancerName'
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: "30"
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups: [!Ref "PublicLoadBalancerSG"]
TargetGroupStaging:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 2
VpcId: !Ref 'VPC'
TargetGroupProduction:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 2
VpcId: !Ref 'VPC'
ListenerRuleProduction:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref "TargetGroupProduction"
Conditions:
- Field: path-pattern
PathPatternConfig:
Values:
- /production/*
ListenerArn: !Ref PublicLoadBalancerListener
Priority: 100
ListenerRuleStaging:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref "TargetGroupStaging"
Conditions:
- Field: path-pattern
PathPatternConfig:
Values:
- /staging/*
ListenerArn: !Ref PublicLoadBalancerListener
Priority: 50
PublicLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: "redirect"
RedirectConfig:
Protocol: "#{protocol}"
Port: "#{port}"
Host: "#{host}"
Path: "/production/"
StatusCode: "HTTP_301"
LoadBalancerArn: !Ref "PublicLoadBalancer"
Port: 80
Protocol: HTTP
ClusterECS:
Type: AWS::ECS::Cluster
DependsOn: PublicLoadBalancerListener
Properties:
ClusterName: !Ref 'ClusterName'
CapacityProviders:
- FARGATE
ServicesSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for cluster services
VpcId: !Ref 'VPC'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref "PublicLoadBalancerSG"
ServiceStaging:
Type: AWS::ECS::Service
Properties:
ServiceName: pouch-svc-staging
TaskDefinition: pouch-td-staging:4
Cluster: !Ref 'ClusterECS'
DesiredCount: 2
SchedulingStrategy: REPLICA
LaunchType: FARGATE
EnableECSManagedTags: true
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DeploymentCircuitBreaker:
Enable: false
Rollback: false
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups:
- !Ref ServicesSG
LoadBalancers:
- ContainerName: pouch-image-staging
TargetGroupArn: !Ref 'TargetGroupStaging'
ContainerPort: 3100
ServiceProduction:
Type: AWS::ECS::Service
Properties:
ServiceName: pouch-svc-production
TaskDefinition: pouch-td-production:4
Cluster: !Ref 'ClusterECS'
DesiredCount: 2
SchedulingStrategy: REPLICA
LaunchType: FARGATE
EnableECSManagedTags: true
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DeploymentCircuitBreaker:
Enable: false
Rollback: false
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups:
- !Ref ServicesSG
LoadBalancers:
- ContainerName: pouch-image-production
TargetGroupArn: !Ref 'TargetGroupProduction'
ContainerPort: 3100
Outputs:
VpcId:
Description: The ID of the VPC that this stack is deployed in
Value: !Ref 'VPC'
PublicSubnetOne:
Description: Public subnet one
Value: !Ref 'PublicSubnetOne'
PublicSubnetTwo:
Description: Public subnet two
Value: !Ref 'PublicSubnetTwo'
ExternalUrl:
Description: The url of the external load balancer
Value: !Sub http://${PublicLoadBalancer.DNSName}
And now I am getting "The target group with targetGroupArn arn:aws:elasticloadbalancing: ... :targetgroup/pouch-Targe-XFJ4AI7HCF6G/f2a665925da27326 does not have an associated load balancer"
From official AWS documentation:
LoadBalancerName
The name of the load balancer to associate with the Amazon ECS service or task set.
A load balancer name is only specified when using a Classic Load Balancer. If you are using an Application Load Balancer or a Network Load Balancer the load balancer name parameter should be omitted.
That means that you should not specify the name of the load balancer in the CloudFormation template, since you're using Fargate, and consequently, don't use the classic load balancer.
Also, in your place, I would consider using something like Former2. It's a great tool that can generate CloudFormation template for you. You can scan your account, choose the services for which you want a template created. In order to use it though, you need an Access key and secret key, so consider creating an IAM user with read-only privilege.
Edit to cover 2nd error about target group:
The Amazon ECS service requires an explicit dependency on the
Application Load Balancer listener rule and the Application Load
Balancer listener. This prevents the service from starting before the
listener is ready.
It's possible that AWS::ECS::Service is trying to attach to the target group before the target group is added to the load balancer. In order to fix that, you should add a dependency in your Service, like this:
Type: AWS::ECS::Service
DependsOn: Listener # Add exact listener name, depending on the service
Properties:
That should help fix the issue.
I cannot add a comment yet. I think the following link could help with the issue Classic Load Balancers are not supported with Fargate
AWS Load Balancing multiple ports for an ECS Service with Fargate
I'm experiencing a very annoying problem. I created a CI/CD pipelines using AWS CodePipeline and CloudFormation.
This is the template.yml used by CloudFormation to create a ScheduledTask on ECS.
AWSTemplateFormatVersion: "2010-09-09"
Description: Template for deploying a ECR image on ECS
Resources:
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Subnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Sub "10.0.0.0/20"
MapPublicIpOnLaunch: true
Subnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs ""]
CidrBlock: !Sub "10.0.32.0/20"
MapPublicIpOnLaunch: true
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
RouteTable:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
RouteTableAssociation1:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref Subnet1
RouteTableId: !Ref RouteTable
RouteTableAssociation2:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref Subnet2
RouteTableId: !Ref RouteTable
InternetRoute:
Type: "AWS::EC2::Route"
DependsOn: VPCGatewayAttachment
Properties:
GatewayId: !Ref InternetGateway
RouteTableId: !Ref RouteTable
DestinationCidrBlock: "0.0.0.0/0"
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: "SLAComputation"
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: ecs-services
Subnets:
- !Ref "Subnet1"
- !Ref "Subnet2"
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Protocol: HTTP
Port: 80
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for loadbalancer to services on ECS
VpcId: !Ref "VPC"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: default
VpcId: !Ref "VPC"
Protocol: "HTTP"
Port: "80"
CloudWatchLogsGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: "sla_computation"
RetentionInDays: 1
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref "VPC"
GroupDescription: for ecs containers
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref "LoadBalancerSecurityGroup"
IpProtocol: -1
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: apis
Cpu: 1024
Memory: 2048
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn: !Ref ECSTaskExecutionRole
ContainerDefinitions:
- Name: ass001
Image: !Sub 649905970782.dkr.ecr.eu-west-1.amazonaws.com/ass001:latest
Cpu: 1024
Memory: 2048
HealthCheck:
Command: [ "CMD-SHELL", "exit 0" ]
Interval: 30
Retries: 5
Timeout: 10
StartPeriod: 30
PortMappings:
- ContainerPort: 8080
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: "sla_computation"
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: "ass001"
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: employee-tg
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 200-299
TargetType: ip
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref LoadBalancerListener
Priority: 2
Conditions:
- Field: path-pattern
Values:
- /*
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# ECS Tasks to download images from ECR
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
# ECS tasks to upload logs to CloudWatch
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
TaskSchedule:
Type: AWS::Events::Rule
Properties:
Description: SLA rule ass001
Name: ass001
ScheduleExpression: cron(0/5 * * * ? *)
State: ENABLED
Targets:
- Arn:
!GetAtt ECSCluster.Arn
Id: dump-data-ecs-task
RoleArn:
!GetAtt ECSTaskExecutionRole.Arn
EcsParameters:
TaskDefinitionArn:
!Ref Task
TaskCount: 1
LaunchType: FARGATE
PlatformVersion: LATEST
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-07db5ae6616a8c5fc
Subnets:
- subnet-031d0787ad492c1c4
TaskSchedule:
Type: AWS::Events::Rule
Properties:
Description: SLA rule ass002
Name: ass002
ScheduleExpression: cron(0/5 * * * ? *)
State: ENABLED
Targets:
- Arn:
!GetAtt ECSCluster.Arn
Id: dump-data-ecs-task
RoleArn:
!GetAtt ECSTaskExecutionRole.Arn
EcsParameters:
TaskDefinitionArn:
!Ref Task
TaskCount: 1
LaunchType: FARGATE
PlatformVersion: LATEST
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- sg-07db5ae6616a8c5fc
Subnets:
- subnet-031d0787ad492c1c4
Outputs:
ApiEndpoint:
Description: Employee API Endpoint
Value: !Join ["", ["http://", !GetAtt LoadBalancer.DNSName, "/employees"]]
Export:
Name: "EmployeeApiEndpoint"
The ScheduledTask is created successfully but it is not running actually. Very strange. But the strangest thing is that the ScheduledTask starts working when I click on "Edit" from the AWS console and (without making any change) I save.
The main issue I see is that you are using wrong role for your scheduled rule. It can't be !GetAtt ECSTaskExecutionRole.Arn. Instead you should create new role (or edit existing one) which has AmazonEC2ContainerServiceEventsRole AWS Managed policy.
It works after you edit in console, because AWS console will probably create the correct role in the background and use it instead of yours.
I am trying to use the Cloudformation Codedeploy Blue/Green deployment feature so have a task set that looks like the following.
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster:
Fn::ImportValue: !Sub "${ClusterStackName}-ClusterID"
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- !Ref FargateSecurityGroup
Subnets:
- Fn::ImportValue: !Sub "${ClusterStackName}-SUBNET1ID"
- Fn::ImportValue: !Sub "${ClusterStackName}-SUBNET2ID"
PlatformVersion: 1.3.0
Scale:
Unit: PERCENT
Value: 1
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
LoadBalancers:
- ContainerName: !Ref ServiceName
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup
My ECS Service definition looks like the following
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPSListener
Properties:
ServiceName: !Sub "${ServiceName}-service"
Cluster:
Fn::ImportValue: !Sub "${ClusterStackName}-ClusterID"
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
HealthCheckGracePeriodSeconds: 30
SchedulingStrategy: REPLICA
This causes the following error in Cloudformation
Invalid request provided: CreateService error: Health check grace period is only valid for services configured to use load balancers
However omitting HealthCheckGracePeriodSeconds causes my tasks to fail the healthcheck while starting. I have checked the docs and there appears to be no option to add HealthCheckGracePeriodSeconds to the TaskSet definition.
How can I use Cloudformation blue/green deployment for ECS with a health check grace period?
Full Example
Here is a full template that I would expect to work but does not.
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Image:
Type: String
Vpc:
Type: 'AWS::EC2::VPC::Id'
Subnet1:
Type: 'AWS::EC2::Subnet::Id'
Subnet2:
Type: 'AWS::EC2::Subnet::Id'
Transform:
- AWS::CodeDeployBlueGreen
Hooks:
CodeDeployBlueGreenHook:
Type: AWS::CodeDeploy::BlueGreen
Properties:
TrafficRoutingConfig:
Type: AllAtOnce
Applications:
- Target:
Type: AWS::ECS::Service
LogicalID: ECSService
ECSAttributes:
TaskDefinitions:
- TaskDefinition
- GreenTaskDefinition
TaskSets:
- TaskSet
- GreenTaskSet
TrafficRouting:
ProdTrafficRoute:
Type: AWS::ElasticLoadBalancingV2::Listener
LogicalID: HTTPListener
TargetGroups:
- TargetGroup
- TargetGroupGreen
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP to load balancer
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
FargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow port 80 to service
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: load-balancer
Type: application
IpAddressType: ipv4
Scheme: internet-facing
SecurityGroups: [!Ref ALBSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-blue
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TargetGroupGreen:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/"
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
Name: targetgroup-green
Port: 80
Protocol: HTTP
TargetType: ip
UnhealthyThresholdCount: 10
VpcId: !Ref Vpc
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: "task-family-name"
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
Cpu: 512
Memory: 1024
ContainerDefinitions:
- Name: Container
Essential: true
Image: !Ref Image
PortMappings:
- ContainerPort: 80
TaskSet:
Type: AWS::ECS::TaskSet
Properties:
Cluster: !Ref ECSCluster
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups: [!Ref FargateSecurityGroup]
Subnets: [!Ref Subnet1, !Ref Subnet2]
Scale:
Unit: PERCENT
Value: 100
Service: !Ref ECSService
TaskDefinition: !Ref TaskDefinition
PlatformVersion: LATEST
LoadBalancers:
- ContainerName: Container
ContainerPort: 80
TargetGroupArn: !Ref TargetGroup
ECSService:
Type: AWS::ECS::Service
DependsOn: HTTPListener
Properties:
ServiceName: "service"
Cluster: !Ref ECSCluster
DeploymentController:
Type: EXTERNAL
DesiredCount: 4
EnableECSManagedTags: true
HealthCheckGracePeriodSeconds: 30
SchedulingStrategy: REPLICA
PrimaryTaskSet:
Type: 'AWS::ECS::PrimaryTaskSet'
Properties:
Cluster: !Ref ECSCluster
Service: !Ref ECSService
TaskSetId: !GetAtt 'TaskSet.Id'
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Cluster
CapacityProviders:
- FARGATE
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
The error is correct, health checks at the Service level, will need a Load Balancer. However, you can define at the task level, health checks:
EscTaskDef:
Type: 'AWS::ECS::TaskDefinition'
Properties:
...
ContainerDefinitions:
- Name: !Ref AWS::StackName
Image: !Ref Image
PortMappings:
- ContainerPort: 80
HealthCheck:
Command: [ "CMD-SHELL", "curl -f http://localhost:80/api/healthcheck/ping || exit 1" ]
StartPeriod: 200
Here is an example of a rollback example using this type of health check with Cloudformation.
I am creating ECS cluster, service and task using cloudformation but it gives an error: Embedded stack arn:aws:cloudformation:us-east-2:0212657325299:stack/Root-Cluster-153O1DKDIKGLV/f1123c5c-d1f9-11ea-1216-2a3e4111fce2 was not successfully created: The following resource(s) failed to create: [Myservice, LoadBalancerListener]. I have created a root stack which runs the vpc stack and Cluster stack. This error occurs when running the Cluster stack. I think the error is in the Load balancer and role in Myservice but I am unable to figure the solution. Any help would be appreciated.
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Type: String
SubnetB:
Type: String
VpcID:
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- !Ref Alb
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Type: String
SubnetB:
Type: String
VpcID:
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 80
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- !Ref Alb
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2
The AWS::ECS::Service LoadBalancer is an object. There were also other issues:
Missing DependsOn
Missing port on container
I used the template in us-east-1 using default VPC. The template will still not work as there are no container instances. But at least your original issue should be addressed.
---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SubnetA:
Default: subnet-00afd36c5eb1d367b
Type: String
SubnetB:
Default: subnet-0573cd428fe807ebc
Type: String
VpcID:
Default: vpc-040d4c42ee5a159fc
Type: String
Resources:
Albsg:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: alb-sg
VpcId: !Ref VpcID
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Description: For traffic from Internet
GroupDescription: Security Group for demo server
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
IpAddressType: ipv4
Name: Alb
Scheme: internet-facing
SecurityGroups:
- !Ref Albsg
Subnets:
- Ref: "SubnetA"
- Ref: "SubnetB"
Type: application
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: alb-tg
VpcId: !Ref VpcID
Port: 5000
Protocol: HTTP
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
MyCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: Flask-redis
Myservice:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
Cluster: !Ref MyCluster
DeploymentController:
Type: ECS
DesiredCount: 2
LaunchType: EC2
LoadBalancers:
- ContainerName: redis
ContainerPort: 5000
TargetGroupArn: !Ref DefaultTargetGroup
# Role: String
SchedulingStrategy: REPLICA
ServiceName: Python-service
TaskDefinition: !Ref Task
Task:
Type: AWS::ECS::TaskDefinition
Properties:
Family: redis-python
ContainerDefinitions:
- Essential: true
Image: redis:latest
Name: redis
PortMappings:
- ContainerPort: 5000
#HostPort: Integer
#Protocol: tcp
Cpu: .5 vCPU
# ExecutionRoleArn: !Ref Role
Memory: 0.5 GB
NetworkMode: bridge
RequiresCompatibilities:
- EC2