--- AWSTemplateFormatVersion: "2010-09-09" Description: This stack deploys the baseline infrastructure to be used in the App Mesh Workshop. Parameters: Cloud9IAMRole: Type: String Default: AppMesh-Workshop-Admin LatestAmiId: Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' EnvoyImage: Description: App Mesh Envoy container image. See https://docs.aws.amazon.com/app-mesh/latest/userguide/envoy.html Type: String Mappings: SubnetConfig: VPC: CIDR: '10.1.0.0/16' PublicOne: CIDR: '10.1.0.0/24' PublicTwo: CIDR: '10.1.1.0/24' PrivateOne: CIDR: '10.1.100.0/24' PrivateTwo: CIDR: '10.1.101.0/24' Resources: ECSCluster: Type: AWS::ECS::Cluster Properties: ClusterName: !Sub cluster-${AWS::StackName} NodejsTaskDefinition: Type: AWS::ECS::TaskDefinition Properties: Cpu: 256 ExecutionRoleArn: !GetAtt ECSServiceRole.Arn Family: !Sub nodejs-task-${AWS::StackName} Memory: 512 NetworkMode: awsvpc RequiresCompatibilities: - FARGATE TaskRoleArn: !GetAtt ECSTaskRole.Arn ContainerDefinitions: - Name: nodejs-service Image: brentley/ecsdemo-nodejs PortMappings: - ContainerPort: 3000 Protocol: http Essential: true HealthCheck: Command: - CMD-SHELL - curl -s http://localhost:3000/health Interval: 5 Retries: 3 StartPeriod: 10 Timeout: 2 - Name: envoy Image: !Ref EnvoyImage Essential: true User: 1337 Environment: - Name: "APPMESH_RESOURCE_ARN" Value: "mesh/appmesh-workshop/virtualNode/nodejs-lb-strawberry" HealthCheck: Command: - CMD-SHELL - curl -s http://localhost:9901/server_info | grep state | grep -q LIVE Retries: 3 Timeout: 2 Interval: 5 StartPeriod: 10 ProxyConfiguration: ContainerName: envoy Type: APPMESH ProxyConfigurationProperties: - Name: ProxyIngressPort Value: "15000" - Name: AppPorts Value: "3000" - Name: EgressIgnoredIPs Value: "169.254.170.2,169.254.169.254" - Name: IgnoredUID Value: "1337" - Name: ProxyEgressPort Value: "15001" VPC: Type: AWS::EC2::VPC Properties: EnableDnsSupport: true EnableDnsHostnames: true CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] Tags: - Key: Name Value: !Sub VPC-${AWS::StackName} 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 Tags: - Key: Name Value: !Sub PublicOne-${AWS::StackName} 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 Tags: - Key: Name Value: !Sub PublicTwo-${AWS::StackName} PrivateSubnetOne: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: {Ref: 'AWS::Region'} VpcId: !Ref VPC CidrBlock: !FindInMap ['SubnetConfig', 'PrivateOne', 'CIDR'] Tags: - Key: Name Value: !Sub PrivateOne-${AWS::StackName} PrivateSubnetTwo: Type: AWS::EC2::Subnet Properties: AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: {Ref: 'AWS::Region'} VpcId: !Ref VPC CidrBlock: !FindInMap ['SubnetConfig', 'PrivateTwo', 'CIDR'] Tags: - Key: Name Value: !Sub PrivateTwo-${AWS::StackName} 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 NatGatewayOneAttachment: Type: AWS::EC2::EIP DependsOn: GatewayAttachement Properties: Domain: vpc NatGatewayTwoAttachment: Type: AWS::EC2::EIP DependsOn: GatewayAttachement Properties: Domain: vpc NatGatewayOne: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGatewayOneAttachment.AllocationId SubnetId: !Ref PublicSubnetOne NatGatewayTwo: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGatewayTwoAttachment.AllocationId SubnetId: !Ref PublicSubnetTwo PrivateRouteTableOne: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PrivateRouteOne: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableOne DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGatewayOne PrivateRoutePeer: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableOne DestinationCidrBlock: 10.0.0.0/16 VpcPeeringConnectionId: !Ref VPCPeeringConnection PrivateRouteTableOneAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTableOne SubnetId: !Ref PrivateSubnetOne PrivateRouteTableTwo: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PrivateRouteTwo: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableTwo DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGatewayTwo PrivateRoutePeerTwo: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTableTwo DestinationCidrBlock: 10.0.0.0/16 VpcPeeringConnectionId: !Ref VPCPeeringConnection PrivateRouteTableTwoAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTableTwo SubnetId: !Ref PrivateSubnetTwo VPCPeeringConnection: Type: AWS::EC2::VPCPeeringConnection Properties: VpcId: !Ref VPC PeerVpcId: !ImportValue VPC1 FrontEndRoute: Type: AWS::EC2::Route Properties: RouteTableId: !ImportValue RT1 DestinationCidrBlock: 10.1.0.0/16 VpcPeeringConnectionId: !Ref VPCPeeringConnection VPCEndpointSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub SecurityGroup-VPCEndpoint-${AWS::StackName} GroupDescription: Access to the VPC endpoints VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] IpProtocol: -1 ServiceDiscovery: Type: AWS::ServiceDiscovery::PrivateDnsNamespace Properties: Vpc: !ImportValue VPC1 Name: appmeshworkshop.hosted.local CrystalServiceDiscovery: Type: AWS::ServiceDiscovery::Service Properties: Name: crystal NamespaceId: !Ref ServiceDiscovery HealthCheckCustomConfig: FailureThreshold: 1 DnsConfig: DnsRecords: - Type: A TTL: 60 NodejsServiceDiscovery: Type: AWS::ServiceDiscovery::Service Properties: Name: nodejs NamespaceId: !Ref ServiceDiscovery HealthCheckCustomConfig: FailureThreshold: 1 DnsConfig: DnsRecords: - Type: A TTL: 60 CrystalService: Type: AWS::ECS::Service Properties: Cluster: !ImportValue CrystalCluster ServiceName: crystal-service-lb TaskDefinition: !ImportValue CrystalTaskDefinition DesiredCount: 3 LaunchType: FARGATE NetworkConfiguration: AwsvpcConfiguration: SecurityGroups: - !ImportValue ContainerSg Subnets: - !ImportValue CrystalSubnet1 - !ImportValue CrystalSubnet2 - !ImportValue CrystalSubnet3 ServiceRegistries: - RegistryArn: !GetAtt CrystalServiceDiscovery.Arn ContainerName: crystal-service NodejsService: Type: AWS::ECS::Service Properties: Cluster: !Ref ECSCluster ServiceName: nodejs-service-lb TaskDefinition: !Ref NodejsTaskDefinition DesiredCount: 3 LaunchType: FARGATE NetworkConfiguration: AwsvpcConfiguration: SecurityGroups: - !Ref ContainerSecurityGroup Subnets: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo ServiceRegistries: - RegistryArn: !GetAtt NodejsServiceDiscovery.Arn ContainerName: nodejs-service EC2Endpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ec2' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC EC2MessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ec2messages' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC ECRAPIEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ecr.api' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC ECRDKREndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ecr.dkr' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC EnvoyEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.appmesh-envoy-management' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC CloudWatchEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.logs' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC SSMEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ssm' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC SSMMessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: VpcEndpointType: Interface SubnetIds: - !Ref PrivateSubnetOne - !Ref PrivateSubnetTwo SecurityGroupIds: - !Ref VPCEndpointSecurityGroup ServiceName: !Join [ '', [ 'com.amazonaws.', { 'Ref': 'AWS::Region' }, '.ssmmessages' ] ] PrivateDnsEnabled: true VpcId: !Ref VPC ContainerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub SecurityGroup-Container-${AWS::StackName} GroupDescription: Access to the containers VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] IpProtocol: -1 - CidrIp: 10.0.0.0/16 IpProtocol: -1 ECSServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ecs-tasks.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: ECSServiceRolePolicy PolicyDocument: Statement: - Effect: Allow Action: - 'ecr:GetAuthorizationToken' - 'ecr:BatchCheckLayerAvailability' - 'ecr:GetDownloadUrlForLayer' - 'ecr:BatchGetImage' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: '*' ECSTaskRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ecs-tasks.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: ECSTaskRolePolicy PolicyDocument: Statement: - Effect: Allow Action: - 'appmesh:StreamAggregatedResources' - 'acm:ExportCertificate' - 'acm-pca:GetCertificateAuthorityCertificate' - 'xray:PutTraceSegments' - 'xray:PutTelemetryRecords' - 'xray:GetSamplingRules' - 'xray:GetSamplingTargets' - 'xray:GertSamplingStatisticSumaries' Resource: '*' Outputs: VpcId: Value: !Ref VPC PrivateSubnetOne: Value: !Ref PrivateSubnetOne PrivateSubnetTwo: Value: !Ref PrivateSubnetTwo EcsClusterName: Value: !Ref ECSCluster StackName: Value: !Sub ${AWS::StackName} NodejsTaskDefinition: Value: !Ref NodejsTaskDefinition ContainerSecurityGroup: Value: !Ref ContainerSecurityGroup