When I first started working with Amazon Web Services (AWS), it didn’t take long before I started to look for a way to automate the creation of cloud resources. This was years ago, when DevOps was only just starting to become a popular concept. Back then, the newly releases Boto3 Python library gave me the result I needed. Using Python scripts, I could make API calls to AWS and interact with resources. For example, creating a S3 bucket can be done with these lines of code:
import boto3
s3 = boto3.resource('s3')
bucket = s3.create_bucket(Bucket='mybucket')
This had the advantage of being fast and providing an automated way to get a consistent result. In fact, I still use Boto3 to this day. However, this isn’t true DevOps because it doesn’t give you a way to track changes or destroy the resources linked to the script. Enter CloudFormation.
When looking for a proper DevOps tool, I stumbled upon CloudFormation. This is Amazon’s answer to Terraform, a way to use templates to interact with cloud resources. The advantage of CloudFormation is that the template, or set of instructions, is state aware. Meaning that you can check at any time to make sure that the real world is identical to how things should be. You can also destroy all resources created by a template.
The same code would now look like this:
AWSTemplateFormatVersion: 2010-09-09
Resources:
mybucket:
Type: AWS::S3::Bucket
My main gripe with CloudFormation is that it can be difficult to guess the right syntax, and if your template fails, it’s even harder to know why it failed. Also, templates can become very big very fast, far more so than code.
But thankfully, last year Amazon announced CDK, and the circle was complete. CDK is a way to write code, just like with Boto3, but the code then in turn creates CloudFormation templates. So you get all the advantages of writing Python, with the advantages of having a state aware template.
Now, creating that S3 bucket looks like this:
from aws_cdk import core, aws_s3
class MyBucketStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
bucket = aws_s3.Bucket(self, "mybucket")
Creating the template is done with cdk synth
and deploying it using cdk deploy
. I won’t point out the irony of doing this, but out of the three methods, I think I prefer using CDK.