AWSTemplateFormatVersion: '2010-09-09' Description: EC2 ECS cluster running containers in a public subnet. Only supports public facing load balancer, and public service discovery namespaces. Parameters: EnvironmentName: Type: String Default: staging Description: "A friendly environment name that will be used for namespacing all cluster resources. Example: dev, qa, staging, or prod" InstanceType: Type: String Default: t2.micro Description: Class of EC2 instance used to host containers. Choose t2 for testing, m5 for general purpose, c5 for CPU intensive services, and r5 for memory intensive services AllowedValues: [ t2.micro, t2.small, t2.medium, t2.large, t2.xlarge, t2.2xlarge, m5.large, m5.xlarge, m5.2large, m5.4xlarge, m5.12xlarge, m5.24large, c5.large, c5.xlarge, c5.2xlarge, c5.4xlarge, c5.9xlarge, c5.18xlarge, r5.large, r5.xlarge, r5.2xlarge, r5.4xlarge, r5.12xlarge, r5.24xlarge ] ConstraintDescription: Please choose a valid instance type. DesiredCapacity: Type: Number Default: '1' Description: Number of EC2 instances to launch in your ECS cluster. MaxSize: Type: Number Default: '1' Description: Maximum number of EC2 instances that can be launched in your ECS cluster. ECSAMI: Type: AWS::SSM::Parameter::Value Default: /aws/service/ecs/optimized-ami/amazon-linux/recommended/image_id Description: The Amazon Machine Image ID used for the cluster, leave it as the default value to get the latest AMI Mappings: # Hard values for the subnet masks. These masks define # the range of internal IP addresses that can be assigned. # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255 # There are two subnets which cover the ranges: # # 10.0.0.0 - 10.0.1.255 # 10.0.1.0 - 10.0.2.255 # # If you need more IP addresses (perhaps you have so many # instances that you run out) then you can customize these # ranges to add more SubnetConfig: VPC: CIDR: '10.0.0.0/16' PublicOne: CIDR: '10.0.1.0/24' PublicTwo: CIDR: '10.0.2.0/24' Resources: ################################################################## # Network Configuration ################################################################## # VPC in which containers will be networked. It has two public subnets # We distribute the subnets across the first two available subnets # for the region, for high availability. VPC: Type: AWS::EC2::VPC Properties: EnableDnsSupport: true EnableDnsHostnames: true CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] # Two public subnets, where containers can have public IP addresses 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 # Routing # Setup networking resources for the public subnets. Containers # in the public subnets have public IP addresses and the routing table # sends network traffic via the internet gateway. 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 ################################################################## # Security Configuration ################################################################## # A security group a LoadBalancer that will communicate with our # containers. Communication is allowed over only port 80. The # LoadBalancer will redirect to the containers running on # ports 32000-64000, but those don't need to be available on # incoming traffic. LBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Access to the ECS hosts that run containers VpcId: !Ref 'VPC' SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 # A security group for the EC2 hosts that will run the containers. # Communication is allowed over ports in the range 32000-64000, # because those are the ports ECS will randomly assign to # our services so they don't conflict. ContainerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Access to the ECS hosts that run containers VpcId: !Ref 'VPC' SecurityGroupIngress: - IpProtocol: tcp FromPort: 32000 ToPort: 64000 CidrIp: 0.0.0.0/0 ################################################################## # Role Configuration ################################################################## # !!!!Service-Link Roles aren't included here. They are automatically # created the first time the CloudFormation template is run. # Role for the EC2 hosts. This allows the ECS agent on the EC2 hosts # to communicate with the ECS control plane, as well as download the docker # images from ECR to run on your host. 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: '*' # This is an IAM role provides permission for ECS to launch containers, # and should be added to all ECS Task definitions (ExecutionRoleArn). # This role allows access to ECR, logs, and Parameter Store during # launch of containers. The permissions for the running service are # set by ECS Task definition (TaskRoleArn), which should be created # per service TasksExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ecs-tasks.amazonaws.com] Action: ['sts:AssumeRole'] Path: / Policies: - PolicyName: ecs-service PolicyDocument: Statement: - Effect: Allow Action: - "ecr:GetAuthorizationToken" - "ecr:BatchCheckLayerAvailability" - "ecr:GetDownloadUrlForLayer" - "ecr:BatchGetImage" - "logs:CreateLogStream" - "logs:PutLogEvents" - "ssm:GetParameterHistory" - "ssm:GetParameter" - "ssm:GetParameters" - "ssm:GetParametersByPath" Resource: "*" ################################################################## # Cluster Configuration ################################################################## # ECS Resources ECSCluster: Type: AWS::ECS::Cluster # Autoscaling group. This launches the actual EC2 instances that will register # themselves as members of the cluster, and run the docker containers. 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: !Ref 'ECSAMI' SecurityGroups: [!Ref 'ContainerSecurityGroup'] 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} EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: [!Ref 'EC2Role'] # These are the values output by the CloudFormation template. Be careful # about changing any of them, because of them are exported with specific # names so that the other task related CF templates can use them. Outputs: ClusterName: Description: The name of the ECS cluster Value: !Ref 'ECSCluster' Export: Name: !Sub ${EnvironmentName}:ClusterName TasksExecutionRole: Description: The ARN of the ECS role Value: !GetAtt 'TasksExecutionRole.Arn' Export: Name: !Sub ${EnvironmentName}:TasksExecutionRole VpcId: Description: The ID of the VPC that this stack is deployed in Value: !Ref 'VPC' Export: Name: !Sub ${EnvironmentName}:VpcId PublicSubnetOne: Description: Public subnet one Value: !Ref 'PublicSubnetOne' Export: Name: !Sub ${EnvironmentName}:PublicSubnetOne PublicSubnetTwo: Description: Public subnet two Value: !Ref 'PublicSubnetTwo' Export: Name: !Sub ${EnvironmentName}:PublicSubnetTwo LBSecurityGroup: Description: A security group used to allow containers to receive traffic Value: !Ref 'LBSecurityGroup' Export: Name: !Sub ${EnvironmentName}:LBSecurityGroup ContainerSecurityGroup: Description: A security group used to allow containers to receive traffic Value: !Ref 'ContainerSecurityGroup' Export: Name: !Sub ${EnvironmentName}:ContainerSecurityGroup