Infrastructure as Code with CloudFormation

Starting from this section, we will learn how to use a very important AWS tool, CloudFormation. AWS resources mostly have complicated and countless options, and provisioning them manually using AWS Console is not always a good option because this manual process may cause an issue because of a misconfiguration or forgotten option; also, the provisioning process would not be reproducible. Assume that you have to migrate the whole application to another AWS account or you want to remove and create everything from scratch. Creating BASH scripts with AWS CLI commands might be another option, but it would not solve some of the problems mentioned earlier; for example, deleting the whole application or changing some of the configuration in our stack would need refactoring of the BASH script or finding and removing the resources belonging to your application.

With CloudFormation, you can create a JSON (alternatively, a YAML) file that declares all the resources your application will use, relate the resources between them, and deploy, update, or delete the whole stack in one click. With CloudFormation, you also do not need to figure out the order of provisioning of different resources if some of them are depending on others. CloudFormation will calculate the dependencies between different resources and will reorder their provisioning.

CloudFormation is a very detailed topic through which we can only cover the relevant parts of our project. While authoring your templates, you will always need to refer to the documentation to see the available options for every resource type (https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide).
To get started, we can create an example template in any directory and deploy it. Now create a text file with the name cloudformation.test.template and write this snippet into it:

{ 
  "Resources": { 
    "DeploymentBucket": { 
      "Type": "AWS::S3::Bucket", 
      "Properties": { 
        "AccessControl": "PublicRead" 
      } 
    } 
  } 
} 

This template is very straightforward. It basically creates an S3 bucket with the PublicRead permission. The template does not specify any other properties, so AWS will use the default ones. Now let's look at the template in action. In the directory where you created the template file, execute this command:

    $ aws cloudformation create-stack --region us-east-1 --stack-name
test-stack --template-body file://${PWD}/cloudformation.test.template

This command will return stack ID. You can now navigate to AWS Console's CloudFormation section in the us-east-1 region and check out the progress. (You can find the console at https://eu-west-1.console.aws.amazon.com/cloudformation/home?region=us-east-1.)

Once the CloudFormation stack is deployed, you can see the CREATE_COMPLETE text on the status field of your stack. Now execute this command to see the S3 bucket created by your template:

    $ aws s3 ls  

Among the buckets, you can see a bucket with the name test-start-deploymentbucket-RANDOM_ID. The random ID is generated by CloudFormation and appended to the resource name. It is possible to override these random names, but if you are installing the same template more than once to the same account, it is a good practice to leave it as is in order to prevent any collision between different stack resources.

Now let's look at how we can update the stack. Let's assume that we want to update the website configuration of our bucket. Now we can update the template file like this:

{ 
  "Resources": { 
    "DeploymentBucket": { 
      "Type": "AWS::S3::Bucket", 
      "Properties": { 
        "AccessControl": "PublicRead", 
        "WebsiteConfiguration" : { 
          "IndexDocument" : "index.html", 
          "ErrorDocument" : "error.html" 
        } 
      } 
    } 
  } 
} 
 

As you can see, we added a new attribute to the bucket. Now let's execute the following command:

    $ aws cloudformation update-stack --region us-east-1
--stack-name test-stack --template-body
file://${PWD}/cloudformation.test.template

AWS will return the stack ID again, but this time, it will return the existing one and perform the changes to keep the stack up to date with the template. After the update completes, you can navigate to the S3 console to verify that your bucket has the configuration you specified in the template file.

As a last step, we can delete the stack, along with all its resources, with only one command:

    $ aws cloudformation delete-stack --region eu-west-1
--stack-name test-stack

This will clean up your stack.

The default behavior of removing a stack is to delete all the resources. But sometimes, especially when your resource has data, you might prefer to leave the resource even if the stack is deleted. Adding "DeletionPolicy": "Retain" to the resource will prevent AWS from deleting the resource when the CloudFormation stack is deleted.