---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'This template is for the blog Monitoring Server Fleets with Advanced Amazon CloudWatch Agent Capabilities. WARNING: This template creates multiple EC2 instances, and custom CloudWatch metrics. You will be billed for the AWS resources used if you create a stack from this template'

Parameters:
  VpcProdBlock:
    Type: String
    Default: 10.0.0.0/16
    Description: The CIDR range for the Prod VPC. This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  VpcTestBlock:
    Type: String
    Default: 10.1.0.0/16
    Description: The CIDR range for the Test VPC. This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  ProdSubnetABlock:
    Type: String
    Default: 10.0.0.0/24
    Description: CidrBlock for the first prod subnet within the VPC.This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  ProdSubnetBBlock:
    Type: String
    Default: 10.0.1.0/24
    Description: CidrBlock for the second prod subnet within the VPC.This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  TestSubnetABlock:
    Type: String
    Default: 10.1.0.0/24
    Description: CidrBlock for the first test subnet within the VPC.This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  TestSubnetBBlock:
    Type: String
    Default: 10.1.1.0/24
    Description: CidrBlock for the second test subnet within the VPC.This should be a valid private (RFC 1918) CIDR range.  CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

  AMI:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
    #Default: ami-047a51fa27710816e
    Description: The Amazon Linux 2 AMI ID in the region (us-east-1)

Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - 
        Label: 
          default: "Production Network Configuration"
        Parameters: 
          - VpcProdBlock
          - ProdSubnetABlock
          - ProdSubnetBBlock
      - 
        Label: 
          default: "Test Network Configuration"
        Parameters: 
          - VpcTestBlock
          - TestSubnetABlock
          - TestSubnetBBlock



Resources:
  #VPCs
  VPCProd:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:  !Ref VpcProdBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true

  VPCTest:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:  !Ref VpcTestBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true

 #Subnets
  ProdSubnetA:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
        - '0'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: ProdSubnetABlock
      VpcId:
        Ref: VPCProd

  ProdSubnetB:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
        - '1'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: ProdSubnetBBlock
      VpcId:
        Ref: VPCProd

  TestSubnetA:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
        - '2'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: TestSubnetABlock
      VpcId:
        Ref: VPCTest

  TestSubnetB:
    Type: AWS::EC2::Subnet
    Metadata:
      Comment: Subnet
    Properties:
      MapPublicIpOnLaunch: true
      AvailabilityZone:
        Fn::Select:
        - '2'
        - Fn::GetAZs:
            Ref: AWS::Region
      CidrBlock:
        Ref: TestSubnetBBlock
      VpcId:
        Ref: VPCTest

  #IGWs and VPC attachments
  IGWProd:
    Type: AWS::EC2::InternetGateway
    DependsOn: VPCProd
  IGWTest:
    Type: AWS::EC2::InternetGateway
    DependsOn: VPCTest
  IGWProdAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPCProd
      InternetGatewayId: !Ref IGWProd
  IGWTestAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPCTest
      InternetGatewayId: !Ref IGWTest

  #Public Route tables
  ProdSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCProd
      Tags:
      - Key: Environment
        Value: Prod

  TestSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPCTest
      Tags:
      - Key: Environment
        Value: Test
  #route table route entries
  ProdRoute1:
    Type: AWS::EC2::Route
    DependsOn: IGWProdAttachment
    Properties:
      RouteTableId: !Ref ProdSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGWProd
  TestRoute1:
    Type: AWS::EC2::Route
    DependsOn: IGWTestAttachment
    Properties:
      RouteTableId: !Ref TestSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGWTest

  #route table- subnet associations
  ProdSubnetARouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       SubnetId: !Ref ProdSubnetA
       RouteTableId: !Ref ProdSubnetRouteTable
  ProdSubnetBRouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       SubnetId: !Ref ProdSubnetB
       RouteTableId: !Ref ProdSubnetRouteTable  
  TestSubnetARouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       SubnetId: !Ref TestSubnetA
       RouteTableId: !Ref TestSubnetRouteTable
  TestSubnetBRouteTableAssociation:
     Type: AWS::EC2::SubnetRouteTableAssociation
     Properties:
       SubnetId: !Ref TestSubnetB
       RouteTableId: !Ref TestSubnetRouteTable  


#Production AutoScaling Group and Launch Template
  ProductionASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MinSize: '2'
      MaxSize: '4'
      DesiredCapacity: '2'
      LaunchTemplate:
        LaunchTemplateId: !Ref ProductionLaunchTemplate
        Version: !GetAtt ProductionLaunchTemplate.LatestVersionNumber
      VPCZoneIdentifier:
        - !Ref ProdSubnetA
        - !Ref ProdSubnetB
      MetricsCollection: 
        - Granularity: "1Minute"
          Metrics: 
           - "GroupMinSize"
           - "GroupMaxSize"
      Tags:
        - Key: Environment
          Value: Production
          PropagateAtLaunch: true
        - Key: ApplicationName
          Value: ApplicationA
          PropagateAtLaunch: true

  ProductionLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties: 
      LaunchTemplateName: !Sub ${AWS::StackName}-launch-template-prod
      LaunchTemplateData: 
        BlockDeviceMappings: 
          - Ebs:
              VolumeSize: 8
              VolumeType: gp3
              DeleteOnTermination: true
              Encrypted: true
            DeviceName: /dev/xvdcz
        ImageId: !Ref AMI
        InstanceType: t3.micro
        IamInstanceProfile: 
          Arn: !GetAtt 'BlogEC2InstanceProfile.Arn'
        Monitoring: 
          Enabled: true
        SecurityGroupIds: 
          - !Ref BlogEC2InstanceSecurityGroupProd
        UserData:
          Fn::Base64: |
            #!/bin/bash
            # Install all pending updates to the system
            yum -y update
            #install cloudwatch agent
            yum -y install amazon-cloudwatch-agent
            #install collectd
            amazon-linux-extras install collectd -y
            #install Apache 
            yum -y install httpd
            chkconfig httpd on
            service httpd start
            /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:AmazonCloudWatch-applicationA-prod

#Production AutoScaling Group and Launch Template
  TestASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MinSize: '2'
      MaxSize: '2'
      DesiredCapacity: '2'
      LaunchTemplate:
        LaunchTemplateId: !Ref TestLaunchTemplate
        Version: !GetAtt TestLaunchTemplate.LatestVersionNumber
      VPCZoneIdentifier:
        - !Ref TestSubnetA
        - !Ref TestSubnetB
      MetricsCollection: 
        - Granularity: "1Minute"
          Metrics: 
           - "GroupMinSize"
           - "GroupMaxSize"
      Tags:
        - Key: Environment
          Value: Test
          PropagateAtLaunch: true
        - Key: ApplicationName
          Value: ApplicationA
          PropagateAtLaunch: true

  TestLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties: 
      LaunchTemplateName: !Sub ${AWS::StackName}-launch-template-test
      LaunchTemplateData: 
        BlockDeviceMappings: 
          - Ebs:
              VolumeSize: 8
              VolumeType: gp3
              DeleteOnTermination: true
              Encrypted: true
            DeviceName: /dev/xvdcz
        ImageId: !Ref AMI
        InstanceType: t3.nano
        IamInstanceProfile: 
          Arn: !GetAtt 'BlogEC2InstanceProfile.Arn'
        Monitoring: 
          Enabled: true
        SecurityGroupIds: 
          - !Ref BlogEC2InstanceSecurityGroupTest
        UserData:
          Fn::Base64: |
            #!/bin/bash
            # Install all pending updates to the system
            yum -y update
            #install cloudwatch agent
            yum -y install amazon-cloudwatch-agent
            #install collectd
            amazon-linux-extras install collectd -y
            #install Apache 
            yum -y install httpd
            chkconfig httpd on
            service httpd start
            /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:AmazonCloudWatch-applicationA-test

  #ssm parameter store parameters 
  ApplicationAProdConfig:
    Type: AWS::SSM::Parameter
    Properties:
      Name: AmazonCloudWatch-applicationA-prod
      Type: String
      Value: |
        {
          "agent": {
            "metrics_collection_interval": 10,
            "run_as_user": "root"
          },
          "metrics": {
            "metrics_collected": {
              "collectd": {
                "metrics_aggregation_interval": 60
              },
              "cpu": {
                "measurement": ["cpu_usage_user","cpu_usage_system"],
                "metrics_collection_interval": 60,
                "totalcpu": true,
                "append_dimensions": {
                  "ApplicationName": "ApplicationA",
                  "Environment": "prod",
                  "MetricType": "infra"
                }
              },
              "mem": {
                "measurement": ["mem_used_percent"],
                "metrics_collection_interval": 60,
                "append_dimensions": {
                  "ApplicationName": "ApplicationA",
                  "Environment": "prod",
                  "MetricType": "infra"
                }
              },
              "procstat": [
                {
                    "exe": "httpd",
                    "measurement": ["cpu_usage","memory_data","memory_swap"],
                    "metrics_collection_interval": 60,
                    "append_dimensions": {
                      "ApplicationName": "ApplicationA",
                      "Environment": "prod",
                      "MetricType": "process"
                    }
                }
              ]
            },
            "append_dimensions": {
              "InstanceId": "${aws:InstanceId}",
              "AutoScalingGroupName": "${aws:AutoScalingGroupName}"
            },
            "aggregation_dimensions": [
                ["AutoScalingGroupName"],
                ["InstanceId"],
                ["Environment"]
            ]
          }
        }
      Description: SSM Parameter for ApplicationA's CloudWatch agent prod config.
      Tags:
        ApplicationName: ApplicationA

  ApplicationATestConfig:
    Type: AWS::SSM::Parameter
    Properties:
      Name: AmazonCloudWatch-applicationA-test
      Type: String
      Value: |
        {
          "agent": {
            "metrics_collection_interval": 10,
            "run_as_user": "root"
          },
          "metrics": {
            "metrics_collected": {
              "collectd": {
                "metrics_aggregation_interval": 60
              },
              "cpu": {
                "measurement": ["cpu_usage_user","cpu_usage_system"],
                "metrics_collection_interval": 60,
                "totalcpu": true,
                "append_dimensions": {
                  "ApplicationName": "ApplicationA",
                  "Environment": "test",
                  "MetricType": "infra"
                }
              },
              "mem": {
                "measurement": ["mem_used_percent"],
                "metrics_collection_interval": 60,
                "append_dimensions": {
                  "ApplicationName": "ApplicationA",
                  "Environment": "test",
                  "MetricType": "infra"
                }
              },
              "procstat": [
                {
                    "exe": "httpd",
                    "measurement": ["cpu_usage","memory_data","memory_swap"],
                    "metrics_collection_interval": 60,
                    "append_dimensions": {
                      "ApplicationName": "ApplicationA",
                      "Environment": "test",
                      "MetricType": "process"
                    }
                }
              ]
            },
             "append_dimensions": {
              "InstanceId": "${aws:InstanceId}",
              "AutoScalingGroupName": "${aws:AutoScalingGroupName}"
            },
            "aggregation_dimensions": [
                ["AutoScalingGroupName"],
                ["InstanceId"],
                ["Environment"]
            ]
          }
        }
      Description: SSM Parameter for ApplicationA's CloudWatch agent test config.
      Tags:
        ApplicationName: ApplicationA

  #ec2 instance related
  BlogEC2InstanceSecurityGroupProd:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Instance Security Group for Production EC2 instances
      VpcId: !Ref VPCProd

  BlogEC2InstanceSecurityGroupTest:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Instance Security Group for Test EC2 instances
      VpcId: !Ref VPCTest

  BlogEC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties: 
      InstanceProfileName: BlogInstanceProfile
      Path: /
      Roles: 
        - !Ref BlogEC2InstanceRole
  BlogEC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole