May 20, 2020
Evolution of a S3 Bucket in CloudFormation
AWS CloudFormation is a foundational service from AWS that allows the management of AWS resources via JSON or YAML templates.
CloudFormation has changed a lot over the years. As new features and services become available, the way to define those resources in CloudFormation is expanded or sometimes changed.
In this blog, we’ll walk through setting up a S3 bucket with CloudFormation. We’ll start with a simple resource and work up to a more real-life example with support for versioning, encryption, and preventing any objects from becoming public.
Let’s get started with just about the simplest CloudFormation template we can write:
---
AWSTemplateFormatVersion: 2010-09-09
Resources:
MyBucket:
Type: AWS::S3::Bucket
Save this into a file (in my case I’ll use s3-cfn-example.yaml
) and then you can deploy it with the aws cli. I’m also going to use AWS Vault for managing my AWS profiles / credentials.
$ aws-vault exec oe-sandbox -- aws cloudformation deploy \
--no-fail-on-empty-changeset \
--template-file s3-cfn-example.yaml \
--stack-name s3-cfn-example-stack
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - s3-cfn-example-stack
$
We can run this deploy command repeatedly (it is idempotent).
So after this initial deployment we can look in the console and we will see a CloudFormation stack with one resource - our bucket!
Note that CloudFormation generated a unique name for our bucket since we didn’t specify the BucketName
property. In general you shouldn’t explicitly specify a bucket name, because if you do then CloudFormation can’t perform updates that require replacement of this resource.
Let’s look at the basic features of our bucket:
Hmm - let’s enable versioning and default encryption - update your CloudFormation template to look like this:
---
AWSTemplateFormatVersion: 2010-09-09
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
VersioningConfiguration:
Status: Enabled
After we re-run our deploy command, we can check the properties of our bucket again and see that we now have versioning and default encryption enabled.
Nice!
Let’s take a quick look at permissions. If we go back to the list of buckets, we see that our new bucket has been listed as ‘Objects can be public’…so while they aren’t public by default, they have the option of being public.
Let’s lock this down. We can use the relatively new PublicAccessBlockConfiguration
property for this, as well as explicitly setting the AccessControl
property to Private
.
Update your template to match the below:
---
AWSTemplateFormatVersion: 2010-09-09
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
After we run our deploy command again, we see that our bucket access now says ‘Not Public’:
In this post, we started with the most basic representation of a CloudFormation resource possible (in our example a S3 bucket) and then progressively built out more features and functionality using CloudFormation. There are tons more we could do with this bucket, but I hope this simple example shows how you can build upon your resources with CloudFormation to iteratively create complex infrastructures.
Good luck & happy coding!
Dylan