Raywon Kari - Published on July 13, 2020 - 12 min read
Infrastructure as Code (IaC), is a practice which enables the use of automation to manage resources declaratively such as provisioning, configuring, updating, de-commissioning etc. More theory around this topic can be found on wikipedia. IaC practises have gained a lot of popularity because it brings lot of consistency, predictability, reusability into the system.
Since this is the cloud era at the moment, cloud providers more or less have given the same ability to use IaC practises to manage cloud infrastructure, and IaC in AWS is powered by a service called CloudFormation. Using it, we declaratively define cloud resources in either JSON 📃 or YAML 📃 templates and deploy them to AWS. Rest is taken care by the service itself.
In this blog post, we will explore basics of cloudformation, how to use it, some tips and tricks and a few examples.
When we go out to a restaurant, what do we usually do 🍛 ?
We tell what we want to eat, we get the food 😄 and at the end we pay the bill.
That is exactly what is happening with CloudFormation as well. We tell what we want in the templates, we upload the template, and it creates and configures the stuff for us. So writing the template is ordering the food, creating and configuring the resources is getting the food, and paying the bill here is the common aspect 😉.
Following diagram is an example of how CloudFormation (CFN) works.
Template is the heart of CFN service. Therefore it is very important to understand the format and how to write a template. A template can consist of the following sections:
Out of all the sections, Resources section is the only mandatory section which needs to be present and at least one resource should be defined in any particular template.
Here is an example structure of a template in JSON format:
{"AWSTemplateFormatVersion" : "version date","Description" : "JSON string","Metadata" : {template metadata},"Parameters" : {set of parameters},"Mappings" : {set of mappings},"Conditions" : {set of conditions},"Transform" : {set of transforms},"Resources" : {set of resources},"Outputs" : {set of outputs}}
Here is an example of an actual usable template:
{"AWSTemplateFormatVersion" : "2010-09-09","Description" : "AWS CloudFormation Sample template showing how to create a publicly accessible S3 bucket configured for website access with a deletion policy of retain on delete.","Resources" : {"S3Bucket" : {"Type" : "AWS::S3::Bucket","Properties" : {"AccessControl" : "PublicRead","WebsiteConfiguration" : {"IndexDocument" : "index.html","ErrorDocument" : "error.html"}},"DeletionPolicy" : "Retain"}},"Outputs" : {"WebsiteURL" : {"Value" : { "Fn::GetAtt" : [ "S3Bucket", "WebsiteURL" ] },"Description" : "URL for website hosted on S3"}}}
In this template, we are simply creating an S3 bucket configured to act as a website with read access from internet and also a deletion policy, meaning if the CFN stack is deleted, it will not delete the s3 bucket. And in the outputs section, we are exposing the website URL.
I am using AWS CLI throughout. For instructions on how to install and configure the CLI refer here.
Lets create a file named stack.json
and copy the template to it. In order to deploy the template, lets use the following command:
aws cloudformation create-stack --stack-name mytestwebsite \--template-body file://stack.json --profile raywon# Once the command succeeds, we should see an output like this{"StackId": "arn:aws:cloudformation:eu-west-1:XXXXXXX:stack/mytestwebsite/xxxxxxxxxxxxxxxx"}
In the template section description above, I mentioned that all the outputs are exposed outside CFN, so any service
can fetch the data from outputs. Lets try to query the data using describe-stacks
CLI call as shown below:
aws cloudformation describe-stacks \--stack-name mytestwebsite --profile raywon
This command will give a JSON response as shown below:
{"Stacks": [{"StackId": "arn:aws:cloudformation:eu-west-1:xxxxxxxxxx:stack/mytestwebsite/xxxxxxxxxxxxxxxxxxx","DriftInformation": {"StackDriftStatus": "NOT_CHECKED"},"Description": "AWS CloudFormation Sample template showing how to create a publicly accessible S3 bucket configured for website access with a deletion policy of retain on delete.","Tags": [],"Outputs": [{"Description": "URL for website hosted on S3","OutputKey": "WebsiteURL","OutputValue": "http://mytestwebsite-s3bucket-v5t8i1hj340y.s3-website-eu-west-1.amazonaws.com"}],"EnableTerminationProtection": false,"CreationTime": "2020-07-11T10:03:21.690Z","StackName": "mytestwebsite","NotificationARNs": [],"StackStatus": "CREATE_COMPLETE","DisableRollback": false,"RollbackConfiguration": {}}]}
So here we can see the outputs objects with the data which we wanted to be exposed of.
Now lets try to create an index.html
& error.html
files which are configured to be served as part of the S3 website.
I use very minimal stuff as shown:
#This is index.html<h1>This is index</h1>#This is error.html<h1>This is error</h1>
Once the files are uploaded, make them public and our website is ready. Head over to the http endpoint which you see from the outputs section to access the website.
Detailed documentation on templates can be found here.
So far we have seen how to use templates and create resources. In this section, lets explore how to update resources by updating templates and executing them.
In order for us to see what we are updating, before executing the changes, we will use a feature called change sets. CFN allows us to see what the changes are, before actually executing them.
So in our earlier example, lets simply update the bucket name. From the stack.json
I will add the bucket name property.
Here is how the resource section looks like now:
"Resources" : {"S3Bucket" : {"Type" : "AWS::S3::Bucket","Properties" : {"AccessControl" : "PublicRead","WebsiteConfiguration" : {"IndexDocument" : "index.html","ErrorDocument" : "error.html"},"BucketName": "my-test-bucket-name-raywon"},"DeletionPolicy" : "Retain"}},
Now that our new template is ready, lets try to generate a changeset. I use the following command:
aws cloudformation create-change-set --stack-name mytestwebsite \--change-set-name update-bucket-name \--template-body file://stack.json --profile raywon# we get a JSON response as follows{"StackId": "arn:aws:cloudformation:eu-west-1:xxxxxxxx:stack/mytestwebsite/xxxxxxxxxx","Id": "arn:aws:cloudformation:eu-west-1:xxxxxxx:changeSet/update-bucket-name/xxxxxxxx"}
Now to see the changes, we need to look at changeset again, as follows:
aws cloudformation describe-change-set \--stack-name mytestwebsite \--change-set-name update-bucket-name \--profile raywon# I get a JSON response with changes."ResourceChange": {"ResourceType": "AWS::S3::Bucket","PhysicalResourceId": "mytestwebsite-s3bucket-v5t8i1hj340y","Details": [{"ChangeSource": "DirectModification","Evaluation": "Static","Target": {"Attribute": "Properties","Name": "BucketName","RequiresRecreation": "Always"}}],"Action": "Modify","Scope": ["Properties"],"LogicalResourceId": "S3Bucket","Replacement": "True"},
Here we can see that, a new bucket will be recreated. So we can go ahead executing these changes.
Once the changes are executed, we need to copy the index.html
and error.html
in to the new bucket, and make sure to delete the old bucket manually because we have the deletion policy enabled.
aws cloudformation execute-change-set \--stack-name mytestwebsite \--change-set-name update-bucket-name \--profile raywon
After this change is done, I have a new website using the name of the newly created bucket accessible http://my-test-bucket-name-raywon.s3-website-eu-west-1.amazonaws.com.
So we have now seen an example on how to use CFN to spin aws resoruces and also to execute changes to those resources,
Here are some other resources which can help gain more thorough understanding about CFN and its capabilities:
This will be last section of this blog post, and in here we will explore some of the tips and tricks when using CFN.
Description :You can find all the code used in this blog post here .
If you have any questions/thoughts/feedback, feel free to contact me in any platform you prefer.