The sample
template illustrates a simple bootstrapping scenario; it creates a windows
helper instance, which is set up to execute a PowerShell script stored in external
repository (S3). You may develop a number of scripts to build, manage, and
monitor the VPC and associated resources, this method can be used to deploy those
capabilities systematically and automatically.
Everything
is built around “WindowInstance”, which has these main components associated
with bootstrapping
- “UserData” section defines "cloud-init" bootstrapping, which performs the execution of cfn-init
- “Metadata” section is defined for "cfn-init" bootstrapping. It installs PowerShell script from S3 to the local directory, and defines the command to run powershell script with
- cfn-signal script is used to return the status of command execution back to CloudFormation with the use of a WaitConditionHandle
Note the instance
is defined with IAM Instance Profile, which provides it necessary privilege to
access external data store, and perform VPC operations.
This simple
method works nicely for initial deployment. How to manage ongoing changes? In
this simple model, we will pick up new configuration by launching a new instance.
We can put the instance behind an auto-scaling group, by terminating the
existing instance, a new one will spin up automatically, triggering the execution
of updated configurations.
There is an
alternative method to trigger updates without launching new instances. AWS has
designed cfn-hup
to assist with updates by polling the CloudFormation meta-data for changes, and
then executes defined actions when a change is detected. Now, instead of
recreating the stack and launching a new instance, an update of CloudFormation
stack will kick of the configuration change on the running instance. Please see
Peter Hancock’s “Updating
your AWS bootstrap” for a nice explanation of the technique.
See sample template below:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "CF bootstrapping template sample: windows instance running a powershell script, obtained from S3, note how cfn:init defines command to use option switch to run powershell",
"Parameters" : {
"KeyPairName" : {
"Description" : "Name of an existing Amazon EC2 key pair",
"Type" : "String",
"Default" : "xxx"
},
"WindowInstanceSubnet" : {
"Description" : "Subnet ID to launch instance",
"Type" : "String",
"Default" : "subnet-xxx"
},
"WindowInstanceSGs": {
"Description": "Comma-delimited list of Security Group IDs for instance",
"Type": "CommaDelimitedList",
"Default": "sg-xxx, sg-xxx"
},
"InstanceType" : {
"Description" : "Amazon EC2 instance type",
"Type" : "String",
"Default" : "t1.micro",
"AllowedValues" : [ "t1.micro", "m1.small", "m1.medium", "m1.large", "m1.xlarge", "m2.xlarge", "m2.2xlarge", "m2.4xlarge", "c1.medium", "c1.xlarge"]
}
},
"Mappings" : {
"AWSInstanceType2Arch" : {
"t1.micro" : { "Arch" : "64" },
"m1.small" : { "Arch" : "64" },
"m1.medium" : { "Arch" : "64" },
"m1.large" : { "Arch" : "64" },
"m1.xlarge" : { "Arch" : "64" },
"m2.xlarge" : { "Arch" : "64" },
"m2.2xlarge" : { "Arch" : "64" },
"m2.4xlarge" : { "Arch" : "64" },
"c1.medium" : { "Arch" : "64" },
"c1.xlarge" : { "Arch" : "64" }
},
"AWSRegionArch2AMI" : {
"us-east-1" : {"64" : "ami-dfcdc4b6"},
"us-west-1" : {"64" : "ami-c2cef187"},
"us-west-2" : {"64" : "ami-16197726"},
"eu-west-1" : {"64" : "ami-fde21e8a"},
"ap-southeast-1" : {"64" : "ami-08f5a45a"},
"ap-southeast-2" : {"64" : "ami-7377ee49"},
"ap-northeast-1" : {"64" : "ami-514e3e50"},
"sa-east-1" : {"64" : "ami-35319228"}
}
},
"Resources" : {
"InstanceRole":{
"Type":"AWS::IAM::Role",
"Properties" : {
"AssumeRolePolicyDocument" : {
"Statement": [{
"Effect" : "Allow",
"Principal" : {
"Service" : [ "ec2.amazonaws.com" ]
},
"Action" : [ "sts:AssumeRole" ]
}]
},
"Path" : "/"
}
},
"RolePolicies" : {
"Type" : "AWS::IAM::Policy",
"Properties" : {
"PolicyName" : "VPCupdate",
"PolicyDocument" : {
"Statement" : [
{
"Action" : [ "ec2:*" ],
"Effect" : "Allow",
"Resource" : "*"
},
{
"Action" : [ "s3:*" ],
"Effect" : "Allow",
"Resource" : "*"
}
]
},
"Roles" : [ { "Ref" : "InstanceRole" } ]
}
},
"InstanceProfile" : {
"Type":"AWS::IAM::InstanceProfile",
"Properties" : {
"Path" : "/",
"Roles" : [ { "Ref":"InstanceRole" } ]
}
},
"WindowInstance": {
"Type" : "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"files" : {
"C:\\cfn\\yourscript.ps1" : {
"source" : "https://s3.amazonaws.com/your-cfn-repo/yourscript.ps1"
}
},
"commands" : {
"1-update" : {
"command" : "powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -File C:\\cfn\\yourscript.ps1"
}
}
}
}
},
"Properties": {
"InstanceType" : { "Ref" : "InstanceType" },
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"Tags":[
{
"Key":"Name",
"Value":"WindowInstance"
}
],
"IamInstanceProfile" : { "Ref" : "InstanceProfile" },
"SubnetId" : { "Ref" : "WindowInstanceSubnet" },
"SecurityGroupIds" : { "Ref" : "WindowInstanceSGs" },
"KeyName" : { "Ref" : "KeyPairName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
""
]]}}
}
},
"WindowInstanceWaitHandle" : {
"Type" : "AWS::CloudFormation::WaitConditionHandle"
},
"WindowInstanceWaitCondition" : {
"Type" : "AWS::CloudFormation::WaitCondition",
"DependsOn" : "WindowInstance",
"Properties" : {
"Handle" : {"Ref" : "WindowInstanceWaitHandle"},
"Timeout" : "500"
}
}
},
"Outputs" : {
...
}
}