This blog post explains how to create (“spin up” in the cool lingo 🙂 ) an EC2 in AWS using Cloud Formation. I will attempt to explain each line or section of code before the code itself. The complete cloud formation template is available at the end of this post. This template is written in JSON. It could also have been done in YAML.
This is the opening curly brace. Just make sure you have one at the end to close it out. Strongly suggest using a JSON formatter and syntax checker.
{
These lines identify the AWS template format version and a description tag for documentation purposes
"AWSTemplateFormatVersion":"2010-09-09", "Description":"Linux EC2 to test with",
The parameters section contains the parameters that will be passed from my Jenkins job to this cloud formation template. Each of these can have a type, default value, and description.
"Parameters":{ "CFstackname":{ "Type":"String" }, "AppID":{ "Type":"String", "Default":"ABC", "Description":"Name of the application. Default=ABC" }, "Role":{ "Type":"String", "Default":"App", "Description":"Enter the Role name. Default=App" }, "UserID":{ "Type":"String", "Description":"Enter the userid of the owner(s)" } },
This next section specifies the resources that are to be created by this cloud formation template.
"Resources":{
The type of the resource to be created is an EC2 instance
"EC2Instance":{ "Type":"AWS::EC2::Instance",
This section contains the properties to describe the resource.
"Properties":{
This parameter specifies the instance type which in this case is a t3 small
"InstanceType":"t3.small",
The DisableApiTermination attribute controls whether the instance can be terminated using the console, CLI, or API. While writing this blog post I did learn that the DisableApiTermination attribute does not prevent Amazon EC2 Auto Scaling from terminating an instance. Just a useful piece of information.
"DisableApiTermination":"false",
This specifies the network interfaces. The AssociatePublicIpAddress is set to false in this case. The subnet ID is your subnet i.e. logical subdivision of an IP network. The group set contains the security groups that you want to be attached to this EC2 instance. These need to be predefined.
"NetworkInterfaces":[ { "AssociatePublicIpAddress":"false", "DeviceIndex":"0", "SubnetId":"my-subnet-01", "GroupSet":[ "sg-for-me-01", "sg-for-me-02" ] } ],
This next parameter specifies the Amazon machine image (AMI) that you wish to use to create this instance. In my case, I had a pre-created AMI that I was using as it was already encrypted with my KMS key
"ImageId":"ami-dean-01",
Here I’m specifying the block devices (disks) to be associated with this machine. The volume size is specified in gigabytes. Storage is deleted when the EC2 is terminated. The disk is to be encrypted with the KMS key specified
"BlockDeviceMappings":[ { "DeviceName":"/dev/sda1", "Ebs":{ "VolumeSize":"100", "DeleteOnTermination":true, "VolumeType":"gp2", "Encrypted":"true", "KmsKeyId":"my magical KMS key" } } ],
These are the tags that are to be associated with this EC2. Some of them are from the parameters of the top of the template. This is a free form to the extent that you can specify any tag string as the key and any value as the value.
"Tags":[ { "Key":"UserID", "Value":"xyz01" }, { "Key":"Name", "Value":"My Linux Machine" }, { "Key":"Role", "Value":"App" }
Close all the curly braces or else bad things will happen 🙂
] } } },
This section is the outputs that I am expecting. The first is the instance id, followed by the Availability Zone (AZ) and the private IP
"Outputs":{ "InstanceId":{ "Description":"InstanceId of the newly created EC2 instance", "Value":{ "Ref":"EC2Instance" } }, "AZ":{ "Description":"Availability Zone of the newly created EC2 instance", "Value":{ "Fn::GetAtt":[ "EC2Instance", "AvailabilityZone" ] } }, "PrivateIP":{ "Description":"PrivateIP of the newly created EC2 instance", "Value":{ "Fn::GetAtt":[ "EC2Instance", "PrivateIp" ] } } } }
Complete cloud formation template
{ "AWSTemplateFormatVersion":"2010-09-09", "Description":"Linux EC2 to test with", "Parameters":{ "CFstackname":{ "Type":"String" }, "AppID":{ "Type":"String", "Default":"ABC", "Description":"Name of the application. Default=ABC" }, "Role":{ "Type":"String", "Default":"App", "Description":"Enter the Role name. Default=App" }, "UserID":{ "Type":"String", "Description":"Enter the userid of the owner(s)" } }, "Resources":{ "EC2Instance":{ "Type":"AWS::EC2::Instance", "Properties":{ "InstanceType":"t3.small", "DisableApiTermination":"false", "NetworkInterfaces":[ { "AssociatePublicIpAddress":"false", "DeviceIndex":"0", "SubnetId":"my-subnet-01", "GroupSet":[ "sg-for-me-01", "sg-for-me-02" ] } ], "ImageId":"ami-dean-01", "BlockDeviceMappings":[ { "DeviceName":"/dev/sda1", "Ebs":{ "VolumeSize":"100", "DeleteOnTermination":true, "VolumeType":"gp2", "Encrypted":"true", "KmsKeyId":"my magical KMS key" } } ], "Tags":[ { "Key":"UserID", "Value":"xyz01" }, { "Key":"Name", "Value":"My Linux Machine" }, { "Key":"Role", "Value":"App" } ] } } }, "Outputs":{ "InstanceId":{ "Description":"InstanceId of the newly created EC2 instance", "Value":{ "Ref":"EC2Instance" } }, "AZ":{ "Description":"Availability Zone of the newly created EC2 instance", "Value":{ "Fn::GetAtt":[ "EC2Instance", "AvailabilityZone" ] } }, "PrivateIP":{ "Description":"PrivateIP of the newly created EC2 instance", "Value":{ "Fn::GetAtt":[ "EC2Instance", "PrivateIp" ] } } } }