AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 ECS cluster running containers in public or private subnets.
Parameters:
  EnvironmentName:
    Type: String
    Default: production
    Description: A friendly environment name that will be used for namespacing all
                 cluster resources, for example staging, qa, or production
  LaunchType:
    Type: String
    Default: Fargate
    AllowedValues: [Fargate, EC2]
  InstanceType:
    Type: String
    Default: t2.micro
    Description: Class of EC2 instance used to host containers
    AllowedValues: [ t2.micro, t2.small, t2.medium, t2.large, t2.xlarge, t2.2xlarge ]
  ECSAMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id
    Description: The Amazon Machine Image ID used for the EC2 cluster

Conditions:
  EC2: !Equals [ !Ref LaunchType, 'EC2' ]
  
Resources:

  #-----------------------------------------------------------------------------#
  # ECS Cluster
  #-----------------------------------------------------------------------------#
  ECSCluster:
    Type: AWS::ECS::Cluster

  #-----------------------------------------------------------------------------#
  # Instance Autoscaling Group (EC2 Launch Type)
  #-----------------------------------------------------------------------------#
  ECSAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Condition: EC2
    Properties:
      NewInstancesProtectedFromScaleIn: true
      VPCZoneIdentifier:
        # Choose private subnets if using NAT gateways
        - Fn::ImportValue: !Sub ${EnvironmentName}:PublicSubnetOne
        - Fn::ImportValue: !Sub ${EnvironmentName}:PublicSubnetTwo
      LaunchConfigurationName: !Ref ContainerInstances
      MinSize: 1
      MaxSize: 3
      DesiredCapacity: 1
    CreationPolicy:
      ResourceSignal:
        Timeout: PT15M
    UpdatePolicy:
      AutoScalingReplacingUpdate:
        WillReplace: true

  ContainerInstances:
    Type: AWS::AutoScaling::LaunchConfiguration
    Condition: EC2
    Properties:
      ImageId: !Ref ECSAMI
      SecurityGroups:
        - Fn::ImportValue: !Sub ${EnvironmentName}:ContainerSecurityGroup
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
          yum install -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource ECSAutoScalingGroup --region ${AWS::Region}

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Condition: EC2
    Properties:
      Path: /
      Roles: [!Ref EC2Role]

  #-----------------------------------------------------------------------------#
  # Capacity Provider (EC2 Launch Type)
  #-----------------------------------------------------------------------------#
  ECSCapacityProvider:
    Type: AWS::ECS::CapacityProvider
    Condition: EC2
    Properties:
        AutoScalingGroupProvider:
            AutoScalingGroupArn: !Ref ECSAutoScalingGroup
            ManagedScaling:
                MaximumScalingStepSize: 2
                MinimumScalingStepSize: 1
                Status: ENABLED
                TargetCapacity: 100
            ManagedTerminationProtection: ENABLED

  ECSClusterCapProvAssoc:
    Type: AWS::ECS::ClusterCapacityProviderAssociations
    Condition: EC2
    Properties:
      Cluster: !Ref ECSCluster
      CapacityProviders:
        - !Ref ECSCapacityProvider
      DefaultCapacityProviderStrategy:
        - CapacityProvider: !Ref ECSCapacityProvider
          Weight: 100

  #-----------------------------------------------------------------------------#
  # Role for Application Autoscaling
  #-----------------------------------------------------------------------------#
  AutoscalingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [application-autoscaling.amazonaws.com]
          Action: [sts:AssumeRole]
      Path: /
      Policies:
      - PolicyName: service-autoscaling
        PolicyDocument:
          Statement:
          - Effect: Allow
            Action:
              - application-autoscaling:*
              - cloudwatch:DescribeAlarms
              - cloudwatch:PutMetricAlarm
              - ecs:DescribeServices
              - ecs:UpdateService
            Resource: '*'

  #-----------------------------------------------------------------------------#
  # Role for EC2 Hosts (EC2 Launch Type)
  #-----------------------------------------------------------------------------#
  EC2Role:
    Type: AWS::IAM::Role
    Condition: EC2
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [ec2.amazonaws.com]
          Action: [sts:AssumeRole]
      Path: /
      Policies:
      - PolicyName: AmazonEC2ContainerServiceforEC2Role
        PolicyDocument:
          Statement:
          - Effect: Allow
            Action:
              - ec2:DescribeTags
              - ecs:CreateCluster
              - ecs:DeregisterContainerInstance
              - ecs:DiscoverPollEndpoint
              - ecs:Poll
              - ecs:RegisterContainerInstance
              - ecs:StartTelemetrySession
              - ecs:UpdateContainerInstancesState
              - ecs:Submit*
              - ecr:GetAuthorizationToken
              - ecr:BatchCheckLayerAvailability
              - ecr:GetDownloadUrlForLayer
              - ecr:BatchGetImage
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: '*'

  #-----------------------------------------------------------------------------#
  # Role ECS Tasks
  #-----------------------------------------------------------------------------#
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [ecs-tasks.amazonaws.com]
          Action: [sts:AssumeRole]
      Path: /
      Policies:
        - PolicyName: AmazonECSTaskExecutionRolePolicy
          PolicyDocument:
            Statement:
            - Effect: Allow
              Action:
                # Allow the ECS Tasks to download images from ECR
                - ecr:GetAuthorizationToken
                - ecr:BatchCheckLayerAvailability
                - ecr:GetDownloadUrlForLayer
                - ecr:BatchGetImage

                # Allow the ECS tasks to upload logs to CloudWatch
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource: '*'

  # # This is an IAM role which authorizes ECS to manage resources on your
  # # account on your behalf, such as updating your load balancer with the
  # # details of where your containers are, so that traffic can reach your
  # # containers.
  # ECSRole:
  #   Type: AWS::IAM::Role
  #   Properties:
  #     AssumeRolePolicyDocument:
  #       Statement:
  #       - Effect: Allow
  #         Principal:
  #           Service: [ecs.amazonaws.com]
  #         Action: ['sts:AssumeRole']
  #     Path: /
  #     Policies:
  #     - PolicyName: ecs-service
  #       PolicyDocument:
  #         Statement:
  #         - Effect: Allow
  #           Action:
  #             # Rules which allow ECS to attach network interfaces to instances
  #             # on your behalf in order for awsvpc networking mode to work right
  #             - 'ec2:AttachNetworkInterface'
  #             - 'ec2:CreateNetworkInterface'
  #             - 'ec2:CreateNetworkInterfacePermission'
  #             - 'ec2:DeleteNetworkInterface'
  #             - 'ec2:DeleteNetworkInterfacePermission'
  #             - 'ec2:Describe*'
  #             - 'ec2:DetachNetworkInterface'

  #             # Rules which allow ECS to update load balancers on your behalf
  #             # with the information sabout how to send traffic to your containers
  #             - 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer'
  #             - 'elasticloadbalancing:DeregisterTargets'
  #             - 'elasticloadbalancing:Describe*'
  #             - 'elasticloadbalancing:RegisterInstancesWithLoadBalancer'
  #             - 'elasticloadbalancing:RegisterTargets'
  #           Resource: '*'

Outputs:
  ClusterName:
    Description: The name of the ECS cluster
    Value: !Ref ECSCluster
    Export:
      Name: !Sub ${EnvironmentName}:ClusterName
  AutoscalingRole:
    Description: The ARN of the role used for autoscaling
    Value: !GetAtt AutoscalingRole.Arn
    Export:
      Name: !Sub ${EnvironmentName}:AutoscalingRole
  # ECSRole:
  #   Description: The ARN of the ECS role
  #   Value: !GetAtt 'ECSRole.Arn'
  #   Export:
  #     Name: !Sub ${EnvironmentName}:ECSRole
  ECSTaskExecutionRole:
    Description: The ARN of the ECS role
    Value: !GetAtt ECSTaskExecutionRole.Arn
    Export:
      Name: !Sub ${EnvironmentName}:ECSTaskExecutionRole
