Parameters: ProjectName: Type: String Description: Project name to link stacks VpcCIDR: Description: Please enter the IP range (CIDR notation) for this VPC Type: String Default: 10.0.0.0/16 PublicSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone Type: String Default: 10.0.0.0/19 PublicSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone Type: String Default: 10.0.32.0/19 PrivateSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone Type: String Default: 10.0.64.0/19 PrivateSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone Type: String Default: 10.0.96.0/19 ColorAppImage: Type: String Description: Color app container image FrontAppImage: Type: String Description: Front app container image ContainerPort: Type: Number Description: Port number to use for applications Default: 8080 Resources: # START infra ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Ref ProjectName VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCIDR EnableDnsHostnames: true Tags: - Key: Name Value: !Ref ProjectName InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Ref ProjectName InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PublicSubnet1CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub '${ProjectName} Public Subnet (AZ1)' PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PublicSubnet2CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub '${ProjectName} Public Subnet (AZ2)' PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet1CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub '${ProjectName} Private Subnet (AZ1)' PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet2CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub '${ProjectName} Private Subnet (AZ2)' NatGateway1EIP: Type: AWS::EC2::EIP DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway2EIP: Type: AWS::EC2::EIP DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway1: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 NatGateway2: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGateway2EIP.AllocationId SubnetId: !Ref PublicSubnet2 PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub '${ProjectName} Public Routes' DefaultPublicRoute: Type: AWS::EC2::Route DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet1 PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet2 PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub '${ProjectName} Private Routes (AZ1)' DefaultPrivateRoute1: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 PrivateRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub '${ProjectName} Private Routes (AZ2)' DefaultPrivateRoute2: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable2 SubnetId: !Ref PrivateSubnet2 AppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "Security group for the apps" VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: !Ref VpcCIDR IpProtocol: -1 LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub '${ProjectName}-log-group' RetentionInDays: 30 TaskIamRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: | { "Statement": [{ "Effect": "Allow", "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]}, "Action": [ "sts:AssumeRole" ] }] } ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchFullAccess - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess TaskExecutionIamRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: | { "Statement": [{ "Effect": "Allow", "Principal": { "Service": [ "ecs-tasks.amazonaws.com" ]}, "Action": [ "sts:AssumeRole" ] }] } ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess # END infra # START colorapp ColorLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme: internal LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: '30' Subnets: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 SecurityGroups: - !Ref AppSecurityGroup ColorTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup DependsOn: - ColorLoadBalancer Properties: HealthCheckIntervalSeconds: 60 HealthCheckPath: '/ping' HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 TargetType: ip Name: !Sub '${ProjectName}-colortarget' Port: !Ref ContainerPort Protocol: HTTP UnhealthyThresholdCount: 2 TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 120 VpcId: !Ref VPC ColorLoadBalancerListener: DependsOn: - ColorLoadBalancer Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref ColorTargetGroup Type: 'forward' LoadBalancerArn: !Ref ColorLoadBalancer Port: !Ref ContainerPort Protocol: HTTP ColorLoadBalancerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref ColorTargetGroup Type: 'forward' Conditions: - Field: path-pattern Values: - '*' ListenerArn: !Ref ColorLoadBalancerListener Priority: 1 ColorTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: !Sub '${ProjectName}-color' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ContainerDefinitions: - Name: 'app' Image: !Ref ColorAppImage Essential: true DependsOn: - ContainerName: 'xray' Condition: 'START' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'color' PortMappings: - ContainerPort: !Ref ContainerPort HostPort: !Ref ContainerPort Protocol: 'tcp' Environment: - Name: COLOR Value: 'green' - Name: PORT Value: !Sub '${ContainerPort}' - Name: XRAY_APP_NAME Value: 'color-green' - Name: 'xray' Image: "public.ecr.aws/xray/aws-xray-daemon" Essential: true User: '1337' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'color' PortMappings: - ContainerPort: 2000 Protocol: 'udp' ColorECSService: Type: AWS::ECS::Service DependsOn: - ColorLoadBalancerListener - ColorLoadBalancerRule Properties: Cluster: !Ref ECSCluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 3 LaunchType: 'FARGATE' LoadBalancers: - ContainerName: app ContainerPort: !Ref ContainerPort TargetGroupArn: !Ref ColorTargetGroup NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref AppSecurityGroup Subnets: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 TaskDefinition: !Ref ColorTaskDef # END colorapp # START feapp FrontTargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 60 HealthCheckPath: '/ping' HealthCheckPort: !Ref ContainerPort HealthCheckProtocol: HTTP HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 TargetType: ip Name: !Sub '${ProjectName}-front' Port: !Ref ContainerPort Protocol: HTTP UnhealthyThresholdCount: 2 TargetGroupAttributes: - Key: deregistration_delay.timeout_seconds Value: 120 VpcId: !Ref VPC FrontTaskDef: Type: AWS::ECS::TaskDefinition Properties: RequiresCompatibilities: - 'FARGATE' Family: !Sub '${ProjectName}-front' NetworkMode: 'awsvpc' Cpu: 256 Memory: 512 TaskRoleArn: !Ref TaskIamRole ExecutionRoleArn: !Ref TaskExecutionIamRole ContainerDefinitions: - Name: 'app' Image: !Ref FrontAppImage Essential: true LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'front' PortMappings: - ContainerPort: !Ref ContainerPort Protocol: 'tcp' DependsOn: - ContainerName: 'xray' Condition: 'START' Environment: - Name: 'COLOR_HOST' Value: Fn::Join: - '' - - !GetAtt ColorLoadBalancer.DNSName - ':' - !Sub '${ContainerPort}' - Name: PORT Value: !Sub '${ContainerPort}' - Name: XRAY_APP_NAME Value: 'front' - Name: 'xray' Image: "public.ecr.aws/xray/aws-xray-daemon" Essential: true User: '1337' LogConfiguration: LogDriver: 'awslogs' Options: awslogs-group: !Ref LogGroup awslogs-region: !Ref AWS::Region awslogs-stream-prefix: 'front' PortMappings: - ContainerPort: 2000 Protocol: 'udp' FrontECSService: Type: AWS::ECS::Service DependsOn: - PublicLoadBalancerListener - FrontListenerRule Properties: Cluster: !Ref ECSCluster DeploymentConfiguration: MaximumPercent: 200 MinimumHealthyPercent: 100 DesiredCount: 3 LaunchType: 'FARGATE' TaskDefinition: !Ref FrontTaskDef LoadBalancers: - ContainerName: app ContainerPort: !Ref ContainerPort TargetGroupArn: !Ref FrontTargetGroup NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: DISABLED SecurityGroups: - !Ref AppSecurityGroup Subnets: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 # END feapp # START public-lb SecurityGroupIngressFromPublicLB: Type: AWS::EC2::SecurityGroupIngress Properties: Description: Ingress from the public LB GroupId: !Ref AppSecurityGroup IpProtocol: -1 SourceSecurityGroupId: !Ref PublicLoadBalancerSecurityGroup PublicLoadBalancerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: 'Access to the public facing load balancer' VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 80 ToPort: 80 PublicLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme: internet-facing LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: '30' Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 SecurityGroups: - !Ref PublicLoadBalancerSecurityGroup PublicLoadBalancerListener: DependsOn: - PublicLoadBalancer Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref FrontTargetGroup Type: 'forward' LoadBalancerArn: !Ref PublicLoadBalancer Port: 80 Protocol: HTTP FrontListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - TargetGroupArn: !Ref FrontTargetGroup Type: 'forward' Conditions: - Field: path-pattern Values: - '/color' ListenerArn: !Ref PublicLoadBalancerListener Priority: 10 # END public-lb Outputs: FrontEndpoint: Description: 'Public endpoint for Front service' Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']] Export: Name: !Sub "${ProjectName}:FrontEndpoint"