Amazon CloudFormation templates are widely used in the AWS cloud for environment creation by the IT and application teams. We have been helping enterprises with Amazon CFT based automation for years and following are some of the best practices to follow while creating Amazon CFT Templates:
Practice #1: Version your CloudFormation templates
CloudFormation template should be commenced with a template format version and description such that it could be artifact-able with version control tools like Git, SVN, CVS, etc.,
Example:
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Template for CustomerXYZ Version 5.0",
Practice #2: Use input parameters
Input parameters section should be developed in scope with getting values from the end users of the CFT for environments, SSH Key Pairs, EC2 instance types, RDS instance types, ElastiCache node types, etc., and this makes the entire CloudFormation template configurable and maintainable.
Example:
"WebTierKeyName": {
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the WebTier",
"Type": "String",
"MinLength": "1",
"MaxLength": "64",
"Default": "testkey",
"AllowedPattern": "[-_ a-zA-Z0-9]*",
"ConstraintDescription": "can contain only alphanumeric characters, spaces, dashes and underscores."
Practice #3: AMI ID should be a parameter in inputs section
AMI ID should be asked as an input parameter from the end users for launching EC2 instances. It is highly recommended not to hard code it inside your template and make it configurable dynamically.
Example:
"NatAMIID": {
"Type": "String",
"Default": "ami-XXXXXXXX",
"Description": "The AMIID of NAT Instance"
},
Practice #4: Mention the AMI mappings
AMI Mappings should be included in the CloudFormation template to validate the respective AMI(s) in the region(s) specified else it will take default AMI for the region (which is not advisable).
Example:
"Mappings": {
"RegionMap": {
"us-east-1": {
"AMI": "ami-XXXXXXXX"
},
"us-west-2": {
"AMI": "ami-XXXXXXXX"
},
Practice #5: Use WaitConditionHandle, WaitCondition, DependsOn wherever applicable
When creating a large multi-tiered infrastructure using CFT on AWS, there are times when the order of the resource launch is important. For Example WaitCondition that waits for the desired number of instances in a web server group.
Using the AWS::CloudFormation::WaitCondition and AWS::CloudFormation::WaitConditionHandle resources, a wait condition can be placed with in a template to make AWS CloudFormation pause the creation of the stack and wait for a signal before it continues to create the stack.
We can specify that the creation of a specific resource to follow another using the “DependsOn” attribute. On adding a DependsOn attribute to a resource, it is created only after the creation of the resource specified in the DependsOn attribute.
Example:
"WebServerGroup" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : "1",
"MaxSize" : "5",
"DesiredCapacity" : { "Ref" : "WebServerCapacity" },
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
}
},
"WaitHandle" : {
"Type" : "AWS::CloudFormation::WaitConditionHandle"
},
"WaitCondition" : {
"Type" : "AWS::CloudFormation::WaitCondition",
"DependsOn" : "WebServerGroup",
"Properties" : {
"Handle" : { "Ref" : "WaitHandle" },
"Timeout" : "300",
"Count" : { "Ref" : "WebServerCapacity" }
}
}
For example, an Amazon EC2 instance with a public IP address is dependent on the VPC-gateway attachment if the VPC and Internet Gateway resources are also declared in the same template. The following snippet shows a sample gateway attachment and an Amazon EC2 instance that depends on that gateway attachment:
"GatewayToInternet" : {
"Type" : "AWS::EC2::VPCGatewayAttachment",
"Properties" : {
"VpcId" : { "Ref" : "VPC" },
"InternetGatewayId" : { "Ref" : "InternetGateway" }
}
},
"EC2Host" : {
"Type" : "AWS::EC2::Instance",
"DependsOn" : "GatewayToInternet",
"Properties" : {
"InstanceType" : { "Ref" : "EC2InstanceType" },
"KeyName" : { "Ref" : "KeyName" },
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "EC2InstanceType" }, "Arch" ] } ] },
"NetworkInterfaces" : [{
"GroupSet" : [{ "Ref" : "EC2SecurityGroup" }],
"AssociatePublicIpAddress" : "true",
"DeviceIndex" : "0",
"DeleteOnTermination" : "true",
"SubnetId" : { "Ref" : "PublicSubnet" }
}]
}
}
Practice #6: Tag the resources properly
Tags should be used for the creation of AWS resources. Tags can be environment, purpose, application specification etc., In addition to the stack name tags that AWS CloudFormation provides, custom tags can be added to the resources that support tagging. This helps in easy grouping of the assets associated with the environment.
Example:
"Tags": [
{
"Key": "Environment",
"Value": {
"Ref": "Environment"
}
},
{
"Key": "Purpose",
"Value": "CustomerXYZ VPC"
Practice #7: Understand your Resources
When a CloudFormation template is deleted all AWS resources assigned with the template will be automatically deleted by AWS. For example a VPC template will have all the related resources like Route tables, Subnets, Network ACLs, etc., will be deleted.
In real scenario, S3 buckets logs, EBS snapshots etc are associated with an infrastructure but they will not terminated when the CFT is deleted, hence have to be automated for deletion using scripts.
Practice #8: Create your Security groups using CFT
Security Groups can be governed and controlled using CloudFormation template for various tiers like Web, App, DB, etc.,
"GatewayTierSg": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Gateway Security Group",
"VpcId": {
"Ref": "Vpc"
},
"SecurityGroupIngress": [
{
"IpProtocol": "6",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "0.0.0.0/0"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "-1",
"CidrIp": "10.0.0.0/16"
}
]
}
},
Practice #9: Include the Output Section.
Output section should be declared with AWS specific end points to trace back. The template Outputs section enables in returning one or more values to the user in response to the AWS CloudFormation describe-stacks command. Example your endpoints are shown on the Output section.
Example:
"RDSDatabaseEndpointDetail": {
"Description": "RDSDatabaseEndpointDetails",
"Value": {
"Fn::GetAtt": [
"RdsTierService",
"Endpoint.Address"
]
}
},
"ElastiCacheEndpointDetail": {
"Description": "ElastiCacheEndpointDetails",
"Value": {
"Fn::GetAtt": [
"ElatiCacheTierService",
"ConfigurationEndpoint.Address"
]
}
},
"WebTierElbExternalEndpointDetail": {
"Description": "WebTierElb External Endpoint Access Details",
"Value": {
"Fn::GetAtt": [
"WebTierElbExternal",
"DNSName"
]
}
},
This article was co authored with ram