# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy of this
 # software and associated documentation files (the "Software"), to deal in the Software
 # without restriction, including without limitation the rights to use, copy, modify,
 # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 # permit persons to whom the Software is furnished to do so.
 #
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

AWSTemplateFormatVersion: '2010-09-09'

Description: >
  amazon-ec2-autoscaling-lifecycle-hook-userdata-linux-example
  
  Sample CloudFormation template that deploys an Auto Scaling Group with Lifecycle Hooks that are managed from User Data. 

Parameters:
  AmiId:
    Description: AMI Id
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  AutoScalingGroupName:
    Description: TBD
    Type: String
    Default: Example Auto Scaling Group
  AutoScalingGroupMinSize:
    Description: TBD
    Type: Number
    Default: 0
  AutoScalingGroupMaxSize:
    Description: TBD
    Type: Number
    Default: 2
  AutoScalingGroupDesiredCapacity:
    Description: TBD
    Type: Number
    Default: 0
  InstanceType:
    Description: Amazon EC2 Instance Type
    Type: String
    Default: "t2.micro"
  InstanceKeyPair: 
    Description: Amazon EC2 Key Pair
    Type: "AWS::EC2::KeyPair::KeyName"
  LifecycleHookName:
    Description: TBD
    Type: String
    Default: "app-install-hook"
  VpcCIDR: 
      Description: Please enter the IP range (CIDR notation) for this VPC
      Type: String
      Default: 10.192.0.0/16
  AvailabilityZone1CIDR:
      Description: Please enter the IP range (CIDR notation) for the app subnet in the first Availability Zone
      Type: String
      Default: 10.192.10.0/24
  AvailabilityZone2CIDR:
      Description: Please enter the IP range (CIDR notation) for the app subnet in the second Availability Zone
      Type: String
      Default: 10.192.11.0/24
  AvailabilityZone3CIDR:
      Description: Please enter the IP range (CIDR notation) for the app subnet in the third Availability Zone
      Type: String
      Default: 10.192.12.0/24
Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups:
    - 
        Label: 
          default: "VPC Configuration"
        Parameters: 
          - VpcCIDR
          - AvailabilityZone1CIDR
          - AvailabilityZone2CIDR
          - AvailabilityZone3CIDR
    -
      Label:
        default: "Instance Configuration"
      Parameters:
        - AmiId
        - InstanceType
        - InstanceKeyPair 
    -
      Label: 
        default: "Auto Scaling Group Configuration"
      Parameters: 
        - AutoScalingGroupName
        - AutoScalingGroupVpcID
        - AutoScalingGroupSubnetIDs
        - AutoScalingGroupMinSize
        - AutoScalingGroupMaxSize
        - AutoScalingGroupDesiredCapacity
        - LifecycleHookName


Mappings: 
  RegionMap: 
    us-east-1: 
      "HVM64": "ami-0ff8a91507f77f867"
    us-west-1: 
      "HVM64": "ami-0bdb828fd58c52235"
    eu-west-1: 
      "HVM64": "ami-047bb4163c506cd98"
    ap-southeast-1: 
      "HVM64": "ami-08569b978cc4dfa10"
    ap-northeast-1: 
      "HVM64": "ami-06cd52961ce9f0d85"

Resources:

  # VPC/Networking Resources

  VPC: 
      Type: AWS::EC2::VPC
      Properties:
          CidrBlock: !Ref VpcCIDR
          EnableDnsHostnames: true
          
  InternetGateway:
      Type: AWS::EC2::InternetGateway
          
  InternetGatewayAttachment:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
          InternetGatewayId: !Ref InternetGateway
          VpcId: !Ref VPC

  AvailabilityZoneSubnet1: 
      Type: AWS::EC2::Subnet
      Properties:
          VpcId: !Ref VPC
          AvailabilityZone: !Select [ 0, !GetAZs '' ]
          CidrBlock: !Ref AvailabilityZone1CIDR
          MapPublicIpOnLaunch: true

  AvailabilityZoneSubnet2: 
      Type: AWS::EC2::Subnet
      Properties:
          VpcId: !Ref VPC
          AvailabilityZone: !Select [ 1, !GetAZs '' ]
          CidrBlock: !Ref AvailabilityZone2CIDR
          MapPublicIpOnLaunch: true

  AvailabilityZoneSubnet3: 
    Type: AWS::EC2::Subnet
    Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 2, !GetAZs '' ]
        CidrBlock: !Ref AvailabilityZone3CIDR
        MapPublicIpOnLaunch: true

  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties: 
        VpcId: !Ref VPC
        GroupDescription: Instance Security Group

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties: 
        VpcId: !Ref VPC

  DefaultPublicRoute: 
      Type: AWS::EC2::Route
      DependsOn: InternetGatewayAttachment
      Properties: 
          RouteTableId: !Ref RouteTable
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId: !Ref InternetGateway

  AvailabilityZoneSubnet1RouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
          RouteTableId: !Ref RouteTable
          SubnetId: !Ref AvailabilityZoneSubnet1

  AvailabilityZoneSubnet2RouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
          RouteTableId: !Ref RouteTable
          SubnetId: !Ref AvailabilityZoneSubnet2

  AvailabilityZoneSubnet3RouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
          RouteTableId: !Ref RouteTable
          SubnetId: !Ref AvailabilityZoneSubnet3          

  # Instance/Auto Scaling Group Resources

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref AmiId
        InstanceType: !Ref InstanceType
        KeyName: !Ref InstanceKeyPair
        SecurityGroupIds:
          - !Ref InstanceSecurityGroup
        IamInstanceProfile:
          Arn: !GetAtt
            - InstanceProfile
            - Arn
        TagSpecifications:
          - ResourceType: instance
            Tags:
              - Key: Name
                Value: LifecycleHookExampleInstance       
        UserData: 
          'Fn::Base64': 
            !Sub 
              - |-
                Content-Type: multipart/mixed; boundary="//"
                MIME-Version: 1.0

                --//
                Content-Type: text/cloud-config; charset="us-ascii"
                MIME-Version: 1.0
                Content-Transfer-Encoding: 7bit
                Content-Disposition: attachment; filename="cloud-config.txt"

                #cloud-config
                cloud_final_modules:
                - [scripts-user, always]

                --//
                Content-Type: text/x-shellscript; charset="us-ascii"
                MIME-Version: 1.0
                Content-Transfer-Encoding: 7bit
                Content-Disposition: attachment; filename="userdata.txt"

                #!/bin/bash
                rpm -q httpd &> /dev/null
                if [ $? -ne 0 ]
                  then
                    sudo amazon-linux-extras install epel -y
                    sudo yum install stress -y
                fi

                rpm -q httpd &> /dev/null
                if [ $? -ne 0 ]
                  then
                    echo "Application is not installed, install and start it."
                    INSTANCE_ID="`wget -q -O - http://instance-data/latest/meta-data/instance-id`" && \
                    sudo yum -y install httpd && \
                    sudo service httpd start && \
                    echo "Sleeping for 120 seconds to simulate additional configuration time." && \
                    sleep 120 && \
                    aws autoscaling complete-lifecycle-action --lifecycle-action-result CONTINUE --instance-id $INSTANCE_ID --lifecycle-hook-name "${lifecycleHookName}" --auto-scaling-group-name "${autoScalingGroupName}"  --region ${AWS::Region}  || \
                    aws autoscaling complete-lifecycle-action --lifecycle-action-result ABANDON --instance-id $INSTANCE_ID --lifecycle-hook-name "${lifecycleHookName}" --auto-scaling-group-name "${autoScalingGroupName}"  --region ${AWS::Region} 
                  else
                    echo "Application is installed, start it."
                    INSTANCE_ID="`wget -q -O - http://instance-data/latest/meta-data/instance-id`" && \
                    sudo service httpd start && \
                    aws autoscaling complete-lifecycle-action --lifecycle-action-result CONTINUE --instance-id $INSTANCE_ID --lifecycle-hook-name "${lifecycleHookName}" --auto-scaling-group-name "${autoScalingGroupName}"  --region ${AWS::Region}  || \
                    aws autoscaling complete-lifecycle-action --lifecycle-action-result ABANDON --instance-id $INSTANCE_ID --lifecycle-hook-name "${lifecycleHookName}" --auto-scaling-group-name "${autoScalingGroupName}"  --region ${AWS::Region} 
                fi
                --//
              - { 
                  autoScalingGroupName: !Ref AutoScalingGroupName,
                  lifecycleHookName: !Ref LifecycleHookName
                }

  InstanceRole:
    Type: "AWS::IAM::Role"
    Properties:
      Policies:
        -
          PolicyName: "CompleteLifecycleActionAllowPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: "autoscaling:CompleteLifecycleAction"
                Resource: !Sub "arn:aws:autoscaling:${AWS::Region}:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AutoScalingGroupName}"                         
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "ec2.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns:
              - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
              
  InstanceProfile: 
    Type: "AWS::IAM::InstanceProfile"
    Properties: 
      Path: "/"
      Roles: 
        - 
          Ref: "InstanceRole"

  AutoScalingGroup:    
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties: 
      AutoScalingGroupName: !Ref AutoScalingGroupName
      LaunchTemplate: 
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      DesiredCapacity: !Ref AutoScalingGroupDesiredCapacity
      MaxSize: !Ref AutoScalingGroupMaxSize
      MinSize: !Ref AutoScalingGroupMinSize
      VPCZoneIdentifier: 
        - !Ref AvailabilityZoneSubnet1
        - !Ref AvailabilityZoneSubnet2
        - !Ref AvailabilityZoneSubnet3

  LifecycleHook:
    Type: AWS::AutoScaling::LifecycleHook
    Properties:
      LifecycleHookName: !Ref LifecycleHookName
      AutoScalingGroupName: !Ref AutoScalingGroup
      DefaultResult: ABANDON
      HeartbeatTimeout: 900
      LifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING"