There is a LeetCode problem I was working today in which I used JavaScript array destructuring. While working on this problem, I was thinking what AWS service can I destructure (breakdown) to help beginners gain a better understanding? CloudFormation and other services came to mind but I got hooked on unpacking the anatomy of a CloudFormation template. So that's what has inspired what you are reading right now.
Before we begin, imagine you have to create a solution that has many different resources in the AWS cloud. Say you want to deploy a three tier application with a web server running on EC2 instances with security groups, load balancers, auto scaling groups amongst others; an application tier with it own fleet of instances, security groups, NACLs and other configurations and then a database tier running on RDS, DynamoDB or any other database service. Imagine how much clicking around you would have to do in the. management console just to create a few of these resources.
CloudFormation comes to wipe the tears of such tedious and repetitive task from your eyes so to speak. It is an Infrastructure as Code (IaC) tool that enables you to define your infrastructure declaratively using either YAML or JSON. With CloudFormation, you can automate the creation, the updating, and the deletion of your infrastructure and its configurations all in one place. That's it for the intro. It's time for the main event!
Breaking Down the CloudFormation Template
You might have gleaned from the introductory paragraphs that the cornerstone of CloudFormation is the template. This is where you specify the resources you want CloudFormation to create or update. Regardless of whether you choose JSON or YAML, the structure of a CloudFormation template remains the same. Personally, I prefer using YAML because it allows for comments within templates and its indentation style is more readable than JSON with its curly braces.
Every CloudFormation template has several sections, including: the format version, the document description, parameters, mappings, conditions, transform, resources (the only compulsory section) and the output section. Let's go over these sections one by one.
Format version
This refers to the version of the CloudFormation template. The latest template format version is 2010-09-09 and it is also the only valid value at the time of writing.
Description
This is an optional section where you can leave comments to describe the infrastructure you are creating within the template. It's your template's documentation as it informs collaborators on what’s happening within the template without having to look further.
Resources
The resources section is the only required section of a CloudFormation template, meaning you have to declare at least one infrastructure resource. Each resource is broken down into three parts, a logical ID that is unique within the template, a resource type, and properties for that resource.
The logical ID is used to describe the resources, and can be referenced in other parts of the template. The resource type is a predefined CloudFormation code for the resource that you’re creating. You’ll need to look this up in the CloudFormation documentation. For example, the resource type of an S3 bucket is “AWS::S3::Bucket”.
Resource properties are additional options that you can specify for a resource. For example, to create an S3, you can declare resource properties such as the bucket name, access control, replication configurations and more.
Parameters
When creating some resources, it may not be operationally efficient to hard-code values. For example, when creating an EC2 instance, you might want to allow flexibility in choosing the instance type. By using parameters, you can empower the person deploying the CloudFormation template to select the desired instance type during stack creation. Additionally, parameters can be used to set default values that can be easily overridden during deployment.
CloudFormation also ships with some predefined parameters called pseudo parameters. For example, you can use the predefined AWS account ID pseudo parameter (AWS::AccountId) to retrieve your account ID or the AWS region pseudo parameter (AWS::Region) to get the AWS region in which a resource has been created. To use pseudo parameters, all you have to do is simply reference them whenever you need them in your template.
Mappings
Within your template, you might encounter values that are impractical to hard code. For example, consider a CloudFormation template that creates EC2 instances in two AWS regions, using distinct AMIs from each region. If you hard code the ID of any of these AMIs, the creation of EC2 instances will likely fail in one region due to referencing an AMI ID that doesn't exist in that location. To address this, you can use mappings to associate each AMI ID with its corresponding region.
Conditions
The conditions section allows you to control which resources are created based on defined conditions. Think of it as an if/else statement. If a specified condition is true, the designated resources will be created. If the condition is false, those resources will not be created or different resources will be provisioned. For instance, you could conditionally create a resource based on whether the stack is intended for production or a test environment.
Output
The output section is used to define values that you want to make available outside a CloudFormation stack after it has been created. For example, if you have a template creating an EC2 instance, you can use the outputs section to return the public IP address of that instance or the instance ID. These values can then be viewed in the CloudFormation console or imported into another stack, if desired.
The best way to end this article will be to show you a sample template that creates a web server EC2 instance. This will show you how the different sections come together.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Creates EC2 Instance with a web server installed with. a security group that allows HTTP"
Parameters:
InstanceType:
Description: EC2 Instance type for the web server
Type: String
Default: t2.nano
AllowedValues:
- t2.micro
- t2.small
- t2.nano
ConstraintDescription: Must be a valid instance type
Mappings:
RegionMap:
us-east-1:
MyAMI1: ami-0ff8a91507f77f867
MyAMI2: ami-0a584ac55a7631c0c
us-west-1:
MyAMI1: ami-0bdb828fd58c52235
MyAMI2: ami-066ee5fd4a9ef77f1
eu-west-1:
MyAMI1: ami-047bb4163c506cd98
MyAMI2: ami-0a7c483d527806435
ap-southeast-1:
MyAMI1: ami-08569b978cc4dfa10
MyAMI2: ami-0be9df32ae9f92309
ap-northeast-1:
MyAMI1: ami-06cd52961ce9f0d85
MyAMI2: ami-053cdd503598e4a9d
Resources:
WebServerEc2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstancType
SecurityGroups:
- Ref: WebServerSecurityGroup
ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", MyAMI1 ]
UserData:
Fn::Base64:
Fn::Join:
- "\n"
- - "#!/bin/bash"
- sudo yum update -y
- sudo yum install httpd -y
- sudo /etc/ini.d/httpd start
- echo "<html><body><h1>Brandon's Web Server</h1>" > /var/www/html/index.html
- echo "</body></html>" >> /var/www/html/index.html
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable HTTP access through port 80
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 88
ToPort: 88
CidrIp: 0.0.0.0/0
Outputs:
AZ:
Description: Availability Zone of the newly created EC2 instance
value: !GetAtt WebServerEc2Instance.AvailabilityZone
PublicIp:
Description: Public IP of the EC2 instance
Value: !GetAtt WebServerEc2Instance.PublicIp
This CloudFormation template creates a web server EC2 instance in the specified AWS region. The instance type can be customized using the InstanceType
parameter. A security group is created to allow HTTP access to the instance. The template also includes a user data script that installs and starts an HTTP server, serving a basic web page.
Key components:
Parameters:
InstanceType
: Allows the user to specify the EC2 instance type.
Mappings: Associates AMI IDs with different regions.
Resources:
WebServerEc2Instance
: Creates an EC2 instance with the specified instance type, security group, and AMI.WebServerSecurityGroup
: Creates a security group allowing HTTP traffic.
User Data: Provides a script that installs and configures a web server on the EC2 instance.
Outputs: Returns the Availability Zone and public IP address of the created EC2 instance.
This template demonstrates the use of parameters, mappings, and resources to create a customizable and functional web server environment in AWS.
Conclusion
We have seen how using CloudFormation makes infrastructure creation an easy and painless process. One of the great advantages of defining infrastructure as code (IaC) is that you can apply the same best practices used in software development to the development and deployment of your cloud infrastructure. This approach can help reduce errors in your templates and allows for easy redeployment to create multiple instances of your infrastructure.
This is particularly helpful when you have multiple environments, such as development, testing, staging, and production, and need to quickly stand up identical versions of these environments.
To fully leverage the power of CloudFormation and other IaC tools, I encourage you to continue exploring their features and capabilities. By adopting these practices, you can streamline your infrastructure management and improve the efficiency and reliability of your cloud deployments.