# 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-windows-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' Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base' 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 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 - |- if ((Get-WindowsFeature Web-Server).InstallState -eq "Installed") { Write-Host "IIS is installed, stopping and starting IIS" try { Start-Process "iisreset.exe" -NoNewWindow -Wait Write-Host "Completing Lifecycle Action with CONTINUE" $INSTANCE = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id") Complete-ASLifecycleAction -InstanceId $INSTANCE -LifecycleHookName "${lifecycleHookName}" -AutoScalingGroupName "${autoScalingGroupName}" -LifecycleActionResult CONTINUE } catch { Write-Output "An error occured, Completing Lifecycle Action with ABANDON." $INSTANCE = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id") Complete-ASLifecycleAction -InstanceId $INSTANCE -LifecycleHookName "${lifecycleHookName}" -AutoScalingGroupName "${autoScalingGroupName}" -LifecycleActionResult ABANDON } } else { Write-Host "IIS is not installed, installing, stopping and starting IIS." try { Install-WindowsFeature -name Web-Server -IncludeManagementTools Start-Process "iisreset.exe" -NoNewWindow -Wait Write-Host "Sleeping for 120 Seconds to Simulate IIS Configuration." Start-Sleep -s 120 Write-Host "Completing Lifecycle Action with CONTINUE" $INSTANCE = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id") Complete-ASLifecycleAction -InstanceId $INSTANCE -LifecycleHookName "${lifecycleHookName}" -AutoScalingGroupName "${autoScalingGroupName}" -LifecycleActionResult CONTINUE } catch { Write-Output "An error occured, Completing Lifecycle Action with ABANDON." $INSTANCE = (New-Object System.Net.WebClient).DownloadString("http://169.254.169.254/latest/meta-data/instance-id") Complete-ASLifecycleAction -InstanceId $INSTANCE -LifecycleHookName "${lifecycleHookName}" -AutoScalingGroupName "${autoScalingGroupName}" -LifecycleActionResult ABANDON } } true - { 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"