Description: >
  This template deploys a VPC, with a pair of public and private subnets spread 
  across two Availabilty Zones. It deploys an Internet Gateway, with a default 
  route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ), 
  and default routes for them in the private subnets.

Parameters:

  EnvironmentName:
    Description: An environment name that will be prefixed to resource names
    Type: String

  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

  LogGroupName:
    Type: String
    Default: "AppMeshExamples/mtls-acm-appmesh"

  ECSServiceLogGroupRetentionInDays:
    Type: Number
    Default: 30

  ECSServicesDomain:
    Type: String
    Description: "Domain name registered under Route-53 that will be used for Service Discovery"
    Default: mtls-ec2.svc.cluster.local

  TlsState:
    Type: String
    Default: mtls
    Description: State of TLS in AWS App Mesh
    AllowedValues:
      - no-tls
      - 1way-tls
      - mtls

  AppMeshMeshName:
    Type: String
    Description: Name of mesh

  EnvoyImageName:
    Type: String
    Description: The image to use for the Envoy container

  ColorTellerImageName:
    Description: The name for the color teller image
    Type: String

  EC2Ami:
    Description: EC2 AMI ID
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"

Conditions:
  1wayTls: !Equals 
    - !Ref TlsState
    - 1way-tls
  mtls: !Equals 
    - !Ref TlsState
    - mtls
  setTls: !Or 
    - Condition: mtls
    - Condition: 1wayTls

Resources:

  VPC: 
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      Tags: 
        - Key: Name 
          Value: !Ref EnvironmentName
      
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref EnvironmentName
      
  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 ${EnvironmentName} 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 ${EnvironmentName} 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 ${EnvironmentName} Private Subnet (AZ1)
        - Key: "kubernetes.io/role/internal-elb"
          Value: "1"

  PrivateSubnet2: 
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 1, !GetAZs '' ]
      CidrBlock: !Ref PrivateSubnet2CIDR
      MapPublicIpOnLaunch: false
      Tags: 
        - Key: Name 
          Value: !Sub ${EnvironmentName} Private Subnet (AZ2)
        - Key: "kubernetes.io/role/internal-elb"
          Value: "1"

  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 ${EnvironmentName} 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 ${EnvironmentName} 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 ${EnvironmentName} 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
  
  AcmPcaColorTellerRootCA:
    Type: AWS::ACMPCA::CertificateAuthority
    Properties:
      Type: ROOT
      KeyAlgorithm: RSA_2048
      SigningAlgorithm: SHA256WITHRSA
      Subject:
        CommonName: "AcmPcaColorTeller"

  AcmPcaColorTellerRootCACert:
    Type: 'AWS::ACMPCA::Certificate'
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorTellerRootCA
      CertificateSigningRequest: !GetAtt
        - AcmPcaColorTellerRootCA
        - CertificateSigningRequest
      SigningAlgorithm: SHA256WITHRSA
      TemplateArn: 'arn:aws:acm-pca:::template/RootCACertificate/V1'
      Validity:
        Type: YEARS
        Value: 10

  AcmPcaColorTellerRootCAActivation:
    Type: 'AWS::ACMPCA::CertificateAuthorityActivation'
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorTellerRootCA
      Certificate: !GetAtt
        - AcmPcaColorTellerRootCACert
        - Certificate
      Status: ACTIVE

  AcmPcaColorTellerEndpointCert:
    Type: AWS::CertificateManager::Certificate
    DependsOn: AcmPcaColorTellerRootCAActivation
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorTellerRootCA
      DomainName: !Sub 'colorteller.${ECSServicesDomain}'

  AcmPcaColorGatewayRootCA:
    Type: AWS::ACMPCA::CertificateAuthority
    Properties:
      Type: ROOT
      KeyAlgorithm: RSA_2048
      SigningAlgorithm: SHA256WITHRSA
      Subject:
        CommonName: "AcmPcaColorGateway"

  AcmPcaColorGatewayRootCACert:
    Type: 'AWS::ACMPCA::Certificate'
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorGatewayRootCA
      CertificateSigningRequest: !GetAtt
        - AcmPcaColorGatewayRootCA
        - CertificateSigningRequest
      SigningAlgorithm: SHA256WITHRSA
      TemplateArn: 'arn:aws:acm-pca:::template/RootCACertificate/V1'
      Validity:
        Type: YEARS
        Value: 10

  AcmPcaColorGatewayRootCAActivation:
    Type: 'AWS::ACMPCA::CertificateAuthorityActivation'
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorGatewayRootCA
      Certificate: !GetAtt
        - AcmPcaColorGatewayRootCACert
        - Certificate
      Status: ACTIVE

  AcmPcaColorGatewayEndpointCert:
    Type: AWS::CertificateManager::Certificate
    DependsOn: AcmPcaColorGatewayRootCAActivation
    Properties:
      CertificateAuthorityArn: !Ref AcmPcaColorGatewayRootCA
      DomainName: !Sub 'colorgateway.${ECSServicesDomain}'

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref EnvironmentName

  ECSServiceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: "Security group for the service"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - CidrIp: !GetAtt VPC.CidrBlock
          IpProtocol: -1

  TaskIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:  [ "ecs-tasks.amazonaws.com" ]
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchFullAccess'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AWSAppMeshEnvoyAccess'
      Policies:
        - PolicyName: access-to-acm
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: acm:ExportCertificate
                Resource: 
                  - !Ref AcmPcaColorTellerEndpointCert
                  - !Ref AcmPcaColorGatewayEndpointCert
              - Effect: Allow
                Action: acm-pca:GetCertificateAuthorityCertificate
                Resource:
                  - !Ref AcmPcaColorTellerRootCA
                  - !Ref AcmPcaColorGatewayRootCA

  TaskExecutionIamRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:  [ "ecs-tasks.amazonaws.com" ]
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchFullAccess'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly'
      Policies:
      - PolicyName: secretsmanager-access
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
              - Effect: Allow
                Action: 'secretsmanager:GetSecretValue'
                Resource: !Ref SecretCert

  ECSServiceLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
      LogGroupName: !Ref LogGroupName
      RetentionInDays: !Ref ECSServiceLogGroupRetentionInDays

  ECSServiceDiscoveryNamespace:
    Type: AWS::ServiceDiscovery::PrivateDnsNamespace
    Properties:
      Vpc: !Ref VPC
      Name: !Ref ECSServicesDomain

  Mesh:
    Type: 'AWS::AppMesh::Mesh'
    Properties:
      MeshName: !Ref AppMeshMeshName
      
  Gateway:
    Type: 'AWS::AppMesh::VirtualGateway'
    Properties:
      MeshName: !GetAtt Mesh.MeshName
      VirtualGatewayName: gateway-vgw
      Spec: !If 
        - setTls
        - !If 
          - 1wayTls
          -
            BackendDefaults:
              ClientPolicy:
                TLS:
                  Enforce: true
                  Validation:
                    Trust:
                      ACM:
                        CertificateAuthorityArns:
                          - !Ref AcmPcaColorTellerRootCA
            Listeners:
              - PortMapping:
                  Port: 9080
                  Protocol: http
          -
            BackendDefaults:
              ClientPolicy:
                TLS:
                  Certificate:
                    File:
                      CertificateChain: /keys/colorgateway_endpoint_cert_chain.pem
                      PrivateKey: /keys/colorgateway_endpoint_dec_pri_key.pem
                  Enforce: true
                  Validation:
                    Trust:
                      ACM:
                        CertificateAuthorityArns:
                            - !Ref AcmPcaColorTellerRootCA
            Listeners:
              - PortMapping:
                  Port: 9080
                  Protocol: http
        - Listeners:
            - PortMapping:
                Port: 9080
                Protocol: http
  Service:
    Type: 'AWS::AppMesh::VirtualService'
    Properties:
      MeshName: !GetAtt Mesh.MeshName
      VirtualServiceName: !Sub colorteller.${ECSServicesDomain}
      Spec:
        Provider:
          VirtualNode:
            VirtualNodeName: !GetAtt Node.VirtualNodeName
  Route:
    Type: 'AWS::AppMesh::GatewayRoute'
    Properties:
      MeshName: !GetAtt Mesh.MeshName
      VirtualGatewayName: gateway-vgw
      GatewayRouteName: colorteller-route
      Spec:
        HttpRoute:
          Action:
            Target:
              VirtualService:
                VirtualServiceName: !GetAtt Service.VirtualServiceName
          Match:
            Prefix: /
      
  Node:
    Type: 'AWS::AppMesh::VirtualNode'
    Properties:
      MeshName: !GetAtt Mesh.MeshName
      VirtualNodeName: colorteller-vn
      Spec:
        Listeners:
          - PortMapping:
              Port: 9080
              Protocol: http
            TLS: !If
            - setTls
            - !If 
              - 1wayTls
              - Certificate:
                  ACM:
                    CertificateArn: !Ref AcmPcaColorTellerEndpointCert
                Mode: STRICT
              - Certificate:
                  ACM:
                    CertificateArn: !Ref AcmPcaColorTellerEndpointCert
                Mode: STRICT
                Validation:
                  Trust:
                    File:
                      CertificateChain: /keys/colorgateway_endpoint_cert_chain.pem
            - !Ref 'AWS::NoValue'
        ServiceDiscovery:
          AWSCloudMap:
            NamespaceName: !Ref ECSServicesDomain
            ServiceName: colorteller
  
  SecretCert:
    Type: AWS::SecretsManager::Secret
    Properties:
      SecretString: |
        {
          "GatewayCertificate": "tempcert",
          "GatewayCertificateChain": "tempcertchain",
          "GatewayPrivateKey": "privatekey",
          "Passphrase": "passphrase"
        }
  InitCertRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: [lambda.amazonaws.com]
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Policies:
        - PolicyName: acm-secretsmanager-access
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: acm:ExportCertificate
                Resource: 
                  - !Ref AcmPcaColorTellerEndpointCert
                  - !Ref AcmPcaColorGatewayEndpointCert
              - Effect: Allow
                Action: acm-pca:GetCertificateAuthorityCertificate
                Resource:
                  - !Ref AcmPcaColorTellerRootCA
                  - !Ref AcmPcaColorGatewayRootCA
              - Effect: Allow
                Action:
                  - secretsmanager:GetRandomPassword
                Resource: '*'
              - Effect: Allow
                Action: secretsmanager:PutSecretValue
                Resource: !Ref SecretCert
  InitCertFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Initial function to populate secrets manager from ACM
      Handler: index.lambda_handler
      Role: !GetAtt InitCertRole.Arn
      Runtime: python3.8
      Timeout: 900
      Environment:
        Variables:
          COLOR_GATEWAY_ACM_ARN: !Ref AcmPcaColorGatewayEndpointCert
          SECRET: !Ref SecretCert
      Code:
        ZipFile: |
          import json
          import boto3
          import base64
          import os
          import cfnresponse
          sm = boto3.client('secretsmanager')
          cm = boto3.client('acm')
          gate_cm = os.environ['COLOR_GATEWAY_ACM_ARN']
          secret = os.environ['SECRET']
          def lambda_handler(event, context):
            print (json.dumps(event))
            if (event['RequestType'] == 'Delete') or (event['RequestType'] == 'Update'):
              cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, '')
            elif event['RequestType'] == 'Create':
              try:
                passphrase = sm.get_random_password(ExcludePunctuation=True)['RandomPassword']
                passphrase_enc = base64.b64encode(passphrase.encode('utf-8'))
                gate_rsp = cm.export_certificate(CertificateArn=gate_cm, Passphrase=passphrase_enc)
                sm_value={}
                sm_value['GatewayCertificate']=gate_rsp['Certificate']
                sm_value['GatewayCertificateChain']=gate_rsp['CertificateChain']
                sm_value['GatewayPrivateKey']=gate_rsp['PrivateKey']
                sm_value['Passphrase']=passphrase
                sm.put_secret_value(SecretId=secret, SecretString=json.dumps(sm_value))
                cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, '')
              except Exception as e:
                print(e) 
                cfnresponse.send(event, context, cfnresponse.FAILED, {}, '')

  InitCert:
    Type: Custom::InitCert
    Properties:
      ServiceToken: !GetAtt InitCertFunction.Arn
      Secret: !Ref SecretCert
  
  ColorTellerServiceDiscoveryRecord:
    Type: 'AWS::ServiceDiscovery::Service'
    Properties:
      Name: "colorteller"
      DnsConfig:
        NamespaceId: !Ref ECSServiceDiscoveryNamespace
        DnsRecords:
          - Type: A
            TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  ColorTellerTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
        - 'FARGATE'
      Family: 'colorteller'
      NetworkMode: 'awsvpc'
      Cpu: 256
      Memory: 512
      TaskRoleArn: !GetAtt TaskIamRole.Arn
      ExecutionRoleArn: !GetAtt TaskExecutionIamRole.Arn
      ProxyConfiguration:
        Type: 'APPMESH'
        ContainerName: 'envoy'
        ProxyConfigurationProperties:
          - Name: 'IgnoredUID'
            Value: '1337'
          - Name: 'ProxyIngressPort'
            Value: '15000'
          - Name: 'ProxyEgressPort'
            Value: '15001'
          - Name: 'AppPorts'
            Value: '9080'
          - Name: 'EgressIgnoredIPs'
            Value: '169.254.170.2,169.254.169.254'
      ContainerDefinitions:
        - Name: 'app'
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ColorTellerImageName}'
          Essential: true
          DependsOn:
            - ContainerName: 'envoy'
              Condition: 'HEALTHY'
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref ECSServiceLogGroup
              awslogs-region: !Ref 'AWS::Region'
              awslogs-stream-prefix: 'colorteller'
          PortMappings:
            - ContainerPort: 9080
              Protocol: 'http'
          Environment:
            - Name: 'PORT'
              Value: 9080
            - Name: 'COLOR'
              Value: 'yellow'
        - Name: envoy
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EnvoyImageName}'
          Essential: true
          User: '1337'
          Ulimits:
            - Name: "nofile"
              HardLimit: 15000
              SoftLimit: 15000
          PortMappings:
            - ContainerPort: 9901
              Protocol: 'tcp'
          HealthCheck:
            Command:
              - 'CMD-SHELL'
              - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE'
            Interval: 5
            Timeout: 2
            Retries: 3
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref ECSServiceLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'colorteller'
          Environment:
            - Name: 'APPMESH_RESOURCE_ARN'
              Value: !Sub 'mesh/${AppMeshMeshName}/virtualNode/colorteller-vn'
            - Name: AWS_REGION
              Value: !Ref 'AWS::Region'
            - Name: ENVOY_LOG_LEVEL
              Value: warning
          # Set secret environment variable for the container
          Secrets:
            - Name: CertSecret
              ValueFrom: !Ref SecretCert

  ColorTellerService:
    Type: 'AWS::ECS::Service'
    DependsOn: InitCert
    Properties:
      Cluster: !Ref ECSCluster
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: FARGATE
      ServiceRegistries:
        - RegistryArn:
            'Fn::GetAtt': ColorTellerServiceDiscoveryRecord.Arn
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - !Ref ECSServiceSecurityGroup
          Subnets: 
            - !Ref PrivateSubnet1
            - !Ref PrivateSubnet2
      TaskDefinition: !Ref ColorTellerTaskDefinition

  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: tcp
            FromPort: 80
            ToPort: 80

  GatewayServiceDiscoveryRecord:
    Type: 'AWS::ServiceDiscovery::Service'
    Properties:
      Name: "gateway"
      DnsConfig:
        NamespaceId: !Ref ECSServiceDiscoveryNamespace
        DnsRecords:
          - Type: A
            TTL: 300
      HealthCheckCustomConfig:
        FailureThreshold: 1

  GatewayTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
        - 'FARGATE'
      Family: 'gateway'
      NetworkMode: 'awsvpc'
      Cpu: 256
      Memory: 512
      TaskRoleArn: !GetAtt TaskIamRole.Arn
      ExecutionRoleArn: !GetAtt TaskExecutionIamRole.Arn
      ContainerDefinitions:
        - Name: envoy
          Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${EnvoyImageName}'
          Essential: true
          User: '1337'
          Ulimits:
            - Name: "nofile"
              HardLimit: 15000
              SoftLimit: 15000
          PortMappings:
            - ContainerPort: 9080
              Protocol: 'tcp'
            - ContainerPort: 9901
              Protocol: 'tcp'
          HealthCheck:
            Command:
              - 'CMD-SHELL'
              - 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE'
            Interval: 5
            Timeout: 2
            Retries: 3
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref ECSServiceLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'gateway'
          Environment:
            - Name: 'APPMESH_RESOURCE_ARN'
              Value: !Sub 'mesh/${AppMeshMeshName}/virtualGateway/gateway-vgw'
            - Name: AWS_REGION
              Value: !Ref 'AWS::Region'
            - Name: ENVOY_LOG_LEVEL
              Value: warning
            
          # Set secret environment variable for the container
          Secrets:
            - Name: CertSecret
              ValueFrom: !Ref SecretCert

  GatewayService:
    Type: 'AWS::ECS::Service'
    DependsOn: InitCert
    Properties:
      Cluster: !Ref ECSCluster
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 100
      DesiredCount: 1
      LaunchType: FARGATE
      ServiceRegistries:
        - RegistryArn: !GetAtt GatewayServiceDiscoveryRecord.Arn
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups: 
            - !Ref ECSServiceSecurityGroup
          Subnets: 
            - !Ref PrivateSubnet1
            - !Ref PrivateSubnet2
      LoadBalancers:
        - ContainerName: envoy
          ContainerPort: 9080
          TargetGroupArn: !Ref WebTargetGroup
      TaskDefinition: !Ref GatewayTaskDefinition

  # public NLB for gateway
  PublicLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Type: network
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2

  WebTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    DependsOn:
      - PublicLoadBalancer
    Properties:
      Protocol: TCP
      TargetType: ip
      Name: !Sub "${EnvironmentName}-web2"
      Port: 80
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: 120
      VpcId: !Ref VPC
  
  PublicLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref WebTargetGroup
          Type: 'forward'
      LoadBalancerArn: !Ref 'PublicLoadBalancer'
      Port: 80
      Protocol: TCP

  BastionHost:
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !Ref EC2Ami
      InstanceType: t3.micro
      SecurityGroupIds:
      - !Ref ECSServiceSecurityGroup
      SubnetId: !Ref PrivateSubnet1
      IamInstanceProfile: !Ref BastionInstanceProfile
      Tags: 
        - Key: Name
          Value: bastion-host
  
  BastionInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref BastionInstanceRole
  
  BastionInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: [ec2.amazonaws.com]
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'

Outputs: 

  ColorAppEndpoint:
    Description: Public endpoint for Color App service
    Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']]
