"Description" : "Yelb on ECS: Create a Yelb stack using Elastic Container Service and App Mesh. This requires public subnets"

Parameters:

    VPC:
        Description: The VPC that the ECS cluster is deployed to
        Type: AWS::EC2::VPC::Id

    PublicSubnetOne:
        Type: AWS::EC2::Subnet::Id
        Description: First Public Subnet

    PublicSubnetTwo:
        Type: AWS::EC2::Subnet::Id
        Description: Second Public Subnet

    Domain:
        Description: An arbitrary internal domain name for the application (only required for internal service discovery - default is yelb.local). It must be unique across multiple deploymemts.
        Type: String
        Default: "yelb.local"

    Mesh:
        Description: Please provide the App Mesh mesh that the components of this application will be scoped under.
        Type: String
        Default: "yelb"
    
    Username:
        Description: username for the yelb aurora db
        Type: String
        Default: "postgres"
        
    Password:
        Description: password for the yelb aurora db
        Type: String
        Default: "postgres_password"
        
    DBPort:
        Description: port for yelb aurora db
        Type: Number
        Default: 5432
        
    RedisPort:
        Description: port for yelb redis cache
        Type: Number
        Default: 6379

    EnvoyImage:
        Description: App Mesh Envoy container image. See https://docs.aws.amazon.com/app-mesh/latest/userguide/envoy.html.
        Type: String

    YelbUIImage:
        Description: Image for yelb-ui
        Type: String

    YelbAppServerImage:
        Description: Image for yelb-app
        Type: String

    CountOfUiTasks:
        Description: The number of tasks to be instantiated for the UI service
        Type: Number
        Default: 1

    CountOfAppserverTasks:
        Description: The number of tasks to be instantiated for the Application service
        Type: Number
        Default: 1
        
    CountOfGatewayTasks:
        Description: The number of tasks to be instantiated for the Gateway service
        Type: Number
        Default: 1

    LaunchType:
        Description: Please provide the LaunchType
        Type: String
        Default: FARGATE
        AllowedValues:
               - EC2
               - FARGATE

    PublicIP:
        Description: Please provide IP connectivity option
        Type: String
        Default: ENABLED
        AllowedValues:
               - ENABLED
               - DISABLED


Resources:

    Cluster:
        Type: 'AWS::ECS::Cluster'
        Properties:
            ClusterName: yelb
                        
    ServiceYelbAppserver:
        Type: AWS::ECS::Service
        Properties:
            LaunchType: !Ref LaunchType
            Cluster: !Ref Cluster
            DesiredCount: !Ref CountOfAppserverTasks
            ServiceRegistries:
                - RegistryArn: !GetAtt YelbAppserverServiceDiscoveryEntry.Arn
            TaskDefinition: !Ref 'TaskDefinitionYelbAppserver'
            NetworkConfiguration:
                AwsvpcConfiguration:
                    AssignPublicIp: !Ref PublicIP
                    Subnets: [ !Ref 'PublicSubnetOne' , !Ref 'PublicSubnetTwo'  ]
                    SecurityGroups: [!Ref 'YelbAppserverSecurityGroup' ]
                        
    TaskDefinitionYelbAppserver:
        Type: AWS::ECS::TaskDefinition
        Properties:
            Family: yelb-appserver
            NetworkMode: awsvpc
            RequiresCompatibilities:
                - FARGATE
            TaskRoleArn: !Ref YelbECSTaskIamRole
            ExecutionRoleArn: !Ref 'YelbECSTaskExecutionRole'
            Cpu: 256
            Memory: 512
            ProxyConfiguration:
                Type: APPMESH
                ContainerName: envoy
                ProxyConfigurationProperties:
                    - Name: IgnoredUID
                      Value: '1337'
                    - Name: ProxyIngressPort
                      Value: '15000'
                    - Name: ProxyEgressPort
                      Value: '15001'
                    - Name: AppPorts
                      Value: '4567'
                    - Name: EgressIgnoredIPs
                      Value: '169.254.170.2,169.254.169.254'
            ContainerDefinitions:
                - Name: yelb-appserver
                  Essential: true
                  Image: !Ref YelbAppServerImage
                  Environment:
                    - Name: SEARCH_DOMAIN
                      Value: !Ref 'Domain'
                    - Name: RECIPE_API_ENDPOINT
                      Value: 'http://www.recipepuppy.com/api/?q='
                    - Name: YELB_DB_SERVER_ENDPOINT
                      Value: !GetAtt YelbDb.Endpoint.Address
                    - Name: YELB_REDIS_SERVER_ENDPOINT
                      Value: !GetAtt YelbRedis.RedisEndpoint.Address
                  LogConfiguration:
                    LogDriver: awslogs
                    Options:
                        awslogs-group: !Ref AWS::StackName
                        awslogs-region: !Ref AWS::Region
                        awslogs-stream-prefix: yelb-appserver
                  DependsOn:
                      - ContainerName: envoy
                        Condition: HEALTHY
                - Name: xray
                  Image: public.ecr.aws/xray/aws-xray-daemon
                  Essential: true
                  User: '1337'
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref AWS::StackName
                          awslogs-region: !Ref AWS::Region
                          awslogs-stream-prefix: yelb-appserver
                - Name: envoy
                  Image: !Ref EnvoyImage
                  Essential: true
                  User: '1337'
                  DependsOn:
                      - ContainerName: xray
                        Condition: START
                  Ulimits:
                      - Name: nofile
                        HardLimit: 15000
                        SoftLimit: 15000
                  HealthCheck:
                      Command:
                         - CMD-SHELL
                         - curl -s http://localhost:9901/server_info | grep state | grep -q LIVE
                      Interval: 5
                      Timeout: 10
                      Retries: 10
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref AWS::StackName
                          awslogs-region: !Ref AWS::Region
                          awslogs-stream-prefix: yelb-appserver
                  Environment:
                      - Name: ENVOY_LOG_LEVEL
                        Value: debug
                      - Name: ENABLE_ENVOY_XRAY_TRACING
                        Value: '1'
                      - Name: ENABLE_ENVOY_STATS_TAGS
                        Value: '1'
                      - Name: ENABLE_ENVOY_DOG_STATSD
                        Value: '1'
                      - Name: APPMESH_RESOURCE_ARN
                        Value:
                            Fn::Join:
                                - ''
                                -
                                    - mesh/
                                    - !Ref Mesh
                                    - /virtualNode/
                                    - 'yelb-app-server'

    ServiceYelbGateway:
        Type: AWS::ECS::Service
        DependsOn: YelbLoadBalancerListener
        Properties:
            LaunchType: !Ref LaunchType
            Cluster: !Ref Cluster
            DesiredCount: !Ref CountOfGatewayTasks
            ServiceRegistries:
                - RegistryArn: !GetAtt YelbGatewayServiceDiscoveryEntry.Arn
            TaskDefinition: !Ref 'TaskDefinitionYelbGateway'
            LoadBalancers:
            - ContainerName: envoy
              ContainerPort: 80
              TargetGroupArn: !Ref YelbTargetGroup
            NetworkConfiguration:
                AwsvpcConfiguration:
                    AssignPublicIp: !Ref PublicIP
                    Subnets: [ !Ref 'PublicSubnetOne' , !Ref 'PublicSubnetTwo'  ]
                    SecurityGroups: [!Ref 'YelbGatewaySecurityGroup' ]

                
    TaskDefinitionYelbGateway:
        Type: AWS::ECS::TaskDefinition
        Properties:
            Family: yelb-gateway
            NetworkMode: awsvpc
            RequiresCompatibilities:
                - FARGATE
            TaskRoleArn: !Ref YelbECSTaskIamRole
            ExecutionRoleArn: !Ref 'YelbECSTaskExecutionRole'
            Cpu: 256
            Memory: 512
            ContainerDefinitions:
                - Name: envoy
                  PortMappings:
                    - ContainerPort: 80
                  Image: !Ref EnvoyImage
                  Essential: true
                  Ulimits:
                      - Name: nofile
                        HardLimit: 15000
                        SoftLimit: 15000
                  HealthCheck:
                      Command:
                          - CMD-SHELL
                          - curl -s http://localhost:9901/server_info | grep state | grep -q LIVE
                      Interval: 5
                      Timeout: 10
                      Retries: 10
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref AWS::StackName
                          awslogs-region: !Ref AWS::Region
                          awslogs-stream-prefix: yelb-gateway
                  Environment:
                      - Name: ENVOY_LOG_LEVEL
                        Value: debug
                      - Name: ENABLE_ENVOY_STATS_TAGS
                        Value: '1'
                      - Name: ENABLE_ENVOY_DOG_STATSD
                        Value: '1'
                      - Name: STATSD_PORT
                        Value: '8125'
                      - Name: APPMESH_RESOURCE_ARN
                        Value:
                            Fn::Join:
                                - ''
                                -
                                    - mesh/
                                    - !Ref Mesh
                                    - /virtualGateway/
                                    - 'yelb-gateway'
                                    
    YelbDb:
        Type: 'AWS::RDS::DBCluster'
        Properties:
            MasterUsername: !Ref 'Username'
            MasterUserPassword: !Ref 'Password'
            DatabaseName: 'yelbdatabase'
            DBClusterIdentifier: yelb-cluster
            Engine: aurora-postgresql
            EngineVersion: '10.18'
            Port: !Ref 'DBPort'
            DBClusterParameterGroupName: default.aurora-postgresql10
            EnableCloudwatchLogsExports:
                - postgresql
            VpcSecurityGroupIds: [!Ref 'YelbDbSecurityGroup' ]
                
    YelbDbInstance1:
        Type: 'AWS::RDS::DBInstance'
        Properties:
            DBInstanceIdentifier: yelb-cluster-instance1
            Engine: aurora-postgresql
            DBClusterIdentifier: !Ref YelbDb
            PubliclyAccessible: 'true'
            DBInstanceClass: db.r4.large
            
    YelbRedis:
        Type: 'AWS::ElastiCache::CacheCluster'
        Properties:
            AutoMinorVersionUpgrade: 'true'
            Engine: redis
            CacheNodeType: cache.t2.micro
            Port: !Ref 'RedisPort'
            NumCacheNodes: '1'
            VpcSecurityGroupIds: [!Ref 'YelbRedisServerSecurityGroup' ]

    ServiceYelbUi:
        Type: AWS::ECS::Service
        Properties:
            LaunchType: !Ref LaunchType
            Cluster: !Ref Cluster
            DesiredCount: !Ref CountOfUiTasks
            ServiceRegistries:
                - RegistryArn: !GetAtt YelbUiServiceDiscoveryEntry.Arn
            TaskDefinition: !Ref 'TaskDefinitionYelbUi'
            NetworkConfiguration:
                AwsvpcConfiguration:
                    AssignPublicIp: !Ref PublicIP
                    Subnets: [ !Ref 'PublicSubnetOne' , !Ref 'PublicSubnetTwo'  ]
                    SecurityGroups: [!Ref 'YelbUiSecurityGroup' ]

    TaskDefinitionYelbUi:
        Type: AWS::ECS::TaskDefinition
        Properties:
            Family: yelb-ui
            NetworkMode: awsvpc
            RequiresCompatibilities:
                - FARGATE
            TaskRoleArn: !Ref YelbECSTaskIamRole
            ExecutionRoleArn: !Ref 'YelbECSTaskExecutionRole'
            Cpu: 256
            Memory: 512
            ProxyConfiguration:
                Type: APPMESH
                ContainerName: envoy
                ProxyConfigurationProperties:
                    - Name: IgnoredUID
                      Value: '1337'
                    - Name: ProxyIngressPort
                      Value: '15000'
                    - Name: ProxyEgressPort
                      Value: '15001'
                    - Name: AppPorts
                      Value: '80'
                    - Name: EgressIgnoredIPs
                      Value: '169.254.170.2,169.254.169.254'
            ContainerDefinitions:
                - Name: yelb-ui
                  Essential: true
                  Image: !Ref YelbUIImage
                  Environment:
                    - Name: SEARCH_DOMAIN
                      Value: !Ref 'Domain'
                  PortMappings:
                    - ContainerPort: 80
                  LogConfiguration:
                    LogDriver: awslogs
                    Options:
                        awslogs-group: !Ref AWS::StackName
                        awslogs-region: !Ref AWS::Region
                        awslogs-stream-prefix: yelb-ui
                  DependsOn:
                      - ContainerName: envoy
                        Condition: HEALTHY
                - Name: xray
                  Image: public.ecr.aws/xray/aws-xray-daemon
                  Essential: true
                  User: '1337'
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref AWS::StackName
                          awslogs-region: !Ref AWS::Region
                          awslogs-stream-prefix: yelb-ui
                - Name: envoy
                  Image: !Ref EnvoyImage
                  Essential: true
                  User: '1337'
                  DependsOn:
                      - ContainerName: xray
                        Condition: START
                  Ulimits:
                      - Name: nofile
                        HardLimit: 15000
                        SoftLimit: 15000
                  HealthCheck:
                      Command:
                          - CMD-SHELL
                          - curl -s http://localhost:9901/server_info | grep state | grep -q LIVE
                      Interval: 5
                      Timeout: 10
                      Retries: 10
                  LogConfiguration:
                      LogDriver: awslogs
                      Options:
                          awslogs-group: !Ref AWS::StackName
                          awslogs-region: !Ref AWS::Region
                          awslogs-stream-prefix: yelb-ui
                  Environment:
                      - Name: ENVOY_LOG_LEVEL
                        Value: debug
                      - Name: ENABLE_ENVOY_XRAY_TRACING
                        Value: '1'
                      - Name: ENABLE_ENVOY_STATS_TAGS
                        Value: '1'
                      - Name: ENABLE_ENVOY_DOG_STATSD
                        Value: '1'
                      - Name: APPMESH_RESOURCE_ARN
                        Value:
                            Fn::Join:
                                - ''
                                -
                                    - mesh/
                                    - !Ref Mesh
                                    - /virtualNode/
                                    - 'yelb-ui'
                                    
                


    CloudWatchLogsGroup:
        Type: AWS::Logs::LogGroup
        Properties: 
            LogGroupName: !Ref AWS::StackName
            RetentionInDays: 365  
   
    YelbECSTaskIamRole:
        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
                - arn:aws:iam::aws:policy/AWSAppMeshEnvoyAccess

    YelbECSTaskExecutionRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Statement:
                - Effect: Allow
                  Principal:
                     Service: [ecs-tasks.amazonaws.com]
                  Action: ['sts:AssumeRole']
            Path: /
            ManagedPolicyArns:
                - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
                - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

    YelbServiceDiscoveryNameSpace:
        Type: AWS::ServiceDiscovery::PrivateDnsNamespace
        Properties:
            Description: "Service Discovery Namespace for Yelb"
            Vpc: !Ref 'VPC'
            Name: !Ref 'Domain'


    YelbAppserverServiceDiscoveryEntry:
        Type: AWS::ServiceDiscovery::Service
        Properties:
            Name: yelb-appserver
            DnsConfig:
                DnsRecords: 
                    - Type: A
                      TTL: "10"
                NamespaceId: !Ref 'YelbServiceDiscoveryNameSpace'
            HealthCheckCustomConfig:
                FailureThreshold: '1'
                

    YelbUiServiceDiscoveryEntry:
        Type: AWS::ServiceDiscovery::Service
        Properties:
            Name: yelb-ui
            DnsConfig:
                DnsRecords: 
                    - Type: A
                      TTL: "10"
                NamespaceId: !Ref 'YelbServiceDiscoveryNameSpace'
            HealthCheckCustomConfig:
                FailureThreshold: '1'
                
    YelbGatewayServiceDiscoveryEntry:
        Type: AWS::ServiceDiscovery::Service
        Properties:
            Name: yelb-gateway
            DnsConfig:
                DnsRecords: 
                    - Type: A
                      TTL: "10"
                NamespaceId: !Ref 'YelbServiceDiscoveryNameSpace'
            HealthCheckCustomConfig:
                FailureThreshold: '1'

    YelbDbSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: yelb-db security group
            SecurityGroupIngress:
            - SourceSecurityGroupId: !Ref YelbAppserverSecurityGroup
              IpProtocol: tcp
              ToPort: 5432
              FromPort: 5432
            - CidrIp: 0.0.0.0/0
              IpProtocol: tcp
              ToPort: 65535
              FromPort: 0
            VpcId: !Ref 'VPC'


    YelbRedisServerSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: redis-server security group
            SecurityGroupIngress:
            - SourceSecurityGroupId: !Ref YelbAppserverSecurityGroup
              IpProtocol: tcp
              ToPort: 6379
              FromPort: 6379
            VpcId: !Ref 'VPC'

    YelbAppserverSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: yelb-appserver security group
            SecurityGroupIngress:
            - CidrIp: '0.0.0.0/0'
              IpProtocol: tcp
              ToPort: 4567
              FromPort: 4567
            VpcId: !Ref 'VPC'
            

    YelbUiSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: yelb-ui security group
            SecurityGroupIngress:
            - CidrIp: '0.0.0.0/0'
              IpProtocol: tcp
              ToPort: 80
              FromPort: 80
            VpcId: !Ref 'VPC'

    YelbLBSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: yelb load balancer security group
            SecurityGroupIngress:
            - CidrIp: '0.0.0.0/0'
              IpProtocol: tcp
              ToPort: 80
              FromPort: 80
            VpcId: !Ref 'VPC'
            
    YelbGatewaySecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupDescription: yelb gateway security group
            SecurityGroupIngress:
            - CidrIp: '0.0.0.0/0'
              IpProtocol: tcp
              ToPort: 80
              FromPort: 80
            VpcId: !Ref 'VPC'

    YelbLoadBalancer:
      Type: AWS::ElasticLoadBalancingV2::LoadBalancer
      Properties:
        Type: network
        Scheme: internet-facing
        Subnets: [ !Ref 'PublicSubnetOne' , !Ref 'PublicSubnetTwo' ]

    YelbLoadBalancerListener:
      Type: AWS::ElasticLoadBalancingV2::Listener
      Properties:
        LoadBalancerArn: !Ref YelbLoadBalancer
        Port: 80
        Protocol: TCP
        DefaultActions:
          - Type: forward
            TargetGroupArn: !Ref YelbTargetGroup

    YelbTargetGroup:
      Type: AWS::ElasticLoadBalancingV2::TargetGroup
      Properties:
        HealthCheckIntervalSeconds: 10
        HealthCheckProtocol: TCP
        HealthCheckTimeoutSeconds: 10
        HealthyThresholdCount: 10
        UnhealthyThresholdCount: 10
        TargetType: ip 
        VpcId: !Ref VPC
        Port: 80
        Protocol: TCP
        

Outputs:
    YelbDBEndpointUrl:
        Description: Yelb Aurora Writer Endpoint URL
        Value: !GetAtt YelbDb.Endpoint.Address
    YelbRedisCacheUrl:
        Description: Yelb Redis Cache Endpoint URL
        Value: !GetAtt YelbRedis.RedisEndpoint.Address
    LoadBalancerUrl:
        Description: The URL of the NLB
        Value: !GetAtt YelbLoadBalancer.DNSName
    