AWSTemplateFormatVersion: '2010-09-09' Description: Deploy a service on AWS Fargate, hosted in a public or private subnet, and accessible via a public load balancer. Mappings: TaskSize: x-small: cpu: 256 memory: 512 small: cpu: 512 memory: 1024 medium: cpu: 1024 memory: 2048 large: cpu: 2048 memory: 4096 x-large: cpu: 4096 memory: 8192 Resources: LoadBalancer: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: LoadBalancerAttributes: - Key: deletion_protection.enabled Value: 'false' Scheme: internet-facing SecurityGroups: - !GetAtt - LoadBalancerSecurityGroup - GroupId Subnets: - 'public-subnet-1' - 'public-subnet-2' Type: application LoadBalancerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Automatically created Security Group for Application LB! SecurityGroupIngress: - CidrIp: 0.0.0.0/0 Description: Allow from anyone on port 80 FromPort: 80 IpProtocol: tcp ToPort: 80 VpcId: '12343-vpc' LoadBalancerSecurityGrouptoServiceSecurityGroup: Type: 'AWS::EC2::SecurityGroupEgress' Properties: GroupId: !GetAtt - LoadBalancerSecurityGroup - GroupId IpProtocol: tcp Description: Load balancer to target DestinationSecurityGroupId: !GetAtt - ServiceSecurityGroup - GroupId FromPort: 80 ToPort: 80 LoadBalancerPublicListener: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: DefaultActions: - TargetGroupArn: !Ref LoadBalancerPublicListenerECSGroup Type: forward LoadBalancerArn: !Ref LoadBalancer Port: 80 Protocol: HTTP LoadBalancerPublicListenerECSGroup: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: Port: '80' Protocol: HTTP TargetGroupAttributes: - Key: stickiness.enabled Value: 'false' TargetType: ip VpcId: '12343-vpc' ServiceTaskDefTaskRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Statement: - Action: 'sts:AssumeRole' Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Version: 2012-10-17 Policies: - PolicyName: 'Publish2SNS' PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: 'sns:Publish' Resource: 'arn:aws:sns::012345678901:topic/role-name-with-path' PermissionsBoundary: !Ref TaskRolePermissionBoundaryPolicy ManagedPolicyArns: - !Ref BaseTaskRoleManagedPolicy # Basic permissions for writing metrics and log events to Cloudwatch, this is something that would typically # be required by every production-ready service. BaseTaskRoleManagedPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "cloudwatch:PutMetricData" - "logs:PutLogEvents" Resource: "*" # We expect our developers to never scope permissions for the task outside of these additional guard-rails # for accessing dependencies. TaskRolePermissionBoundaryPolicy: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - 's3:Get*' - 's3:List*' - 's3:PutObject' Resource: '*' ServiceTaskDef: Type: 'AWS::ECS::TaskDefinition' Properties: ContainerDefinitions: - Essential: true Image: 'public.ecr.aws/nginx/nginx:1.21' LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ServiceTaskDefLogGroup awslogs-stream-prefix: 'sample-service/load-balanced-fargate-svc-prod' awslogs-region: !Ref 'AWS::Region' Name: 'load-balanced-fargate-svc-prod' PortMappings: - ContainerPort: '80' Protocol: tcp Environment: - Name: SNS_TOPIC_ARN Value: '{"ping":"arn:aws:sns::012345678901:topic/role-name-with-path"}' - Name: SNS_REGION Value: "us-west-2" - Name: BACKEND_URL Value: "backend-svc-inst.backend-svc.fargate-env.local:80" Cpu: !FindInMap [TaskSize, x-small, cpu] ExecutionRoleArn: arn:aws:iam::012345678901:role/role-name-with-path Family: 'sample-service_load-balanced-fargate-svc-prod' Memory: !FindInMap [TaskSize, x-small, memory] NetworkMode: awsvpc RequiresCompatibilities: - FARGATE TaskRoleArn: !GetAtt - ServiceTaskDefTaskRole - Arn ServiceTaskDefLogGroup: Type: 'AWS::Logs::LogGroup' UpdateReplacePolicy: Retain DeletionPolicy: Retain Service: Type: 'AWS::ECS::Service' Properties: ServiceName: 'sample-service_load-balanced-fargate-svc-prod' Cluster: 'my-cluster' DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 50 DesiredCount: '1' EnableECSManagedTags: false HealthCheckGracePeriodSeconds: 60 LaunchType: FARGATE LoadBalancers: - ContainerName: 'load-balanced-fargate-svc-prod' ContainerPort: '80' TargetGroupArn: !Ref LoadBalancerPublicListenerECSGroup NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !GetAtt - ServiceSecurityGroup - GroupId Subnets: - 'public-subnet-1' - 'public-subnet-2' ServiceRegistries: - RegistryArn: !GetAtt - CloudMapService - Arn TaskDefinition: !Ref ServiceTaskDef DependsOn: - LoadBalancerPublicListenerECSGroup - LoadBalancerPublicListener CloudMapService: Type: 'AWS::ServiceDiscovery::Service' Properties: DnsConfig: DnsRecords: - TTL: 60 Type: A NamespaceId: 'cluster-name-space' RoutingPolicy: MULTIVALUE HealthCheckCustomConfig: FailureThreshold: 1 Name: 'load-balanced-fargate-svc-prod.sample-service' NamespaceId: 'cluster-name-space' ServiceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Automatically created Security Group for the Service SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all outbound traffic by default IpProtocol: '-1' VpcId: '12343-vpc' ServiceSecurityGroupfromLoadBalancerSecurityGroup: Type: 'AWS::EC2::SecurityGroupIngress' Properties: IpProtocol: tcp Description: Load balancer to target GroupId: !GetAtt - ServiceSecurityGroup - GroupId FromPort: 80 ToPort: 80 SourceSecurityGroupId: !GetAtt - LoadBalancerSecurityGroup - GroupId TaskCountTarget: Type: 'AWS::ApplicationAutoScaling::ScalableTarget' DependsOn: Service Properties: MaxCapacity: 10 MinCapacity: 1 ResourceId: !Join - '' - - service/ - 'my-cluster' - / - 'sample-service_load-balanced-fargate-svc-prod' RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService ScalableDimension: 'ecs:service:DesiredCount' ServiceNamespace: ecs TaskCountTargetCpuScaling: Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' Properties: PolicyName: LBFargateServiceTaskCountTargetCpuScaling PolicyType: TargetTrackingScaling ScalingTargetId: !Ref TaskCountTarget TargetTrackingScalingPolicyConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ECSServiceAverageCPUUtilization TargetValue: 50 TaskCountTargetMemoryScaling: Type: 'AWS::ApplicationAutoScaling::ScalingPolicy' Properties: PolicyName: LBFargateServiceTaskCountTargetMemoryScaling PolicyType: TargetTrackingScaling ScalingTargetId: !Ref TaskCountTarget TargetTrackingScalingPolicyConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ECSServiceAverageMemoryUtilization TargetValue: 50 Outputs: ServiceURL: Value: !Join - '' - - 'http://' - !GetAtt - LoadBalancer - DNSName