I manage a bunch of redirected subdomains. We use these with third-party applications, where the application lives at some non-Lafayette URL and we want a clean, simple Lafayette URL for our users. We call these vanity plates, and at the architectural level they’re pretty simple: a Cloudfront distribution with a certificate and LambdaEdge function to handle the redirection. We monitor the state of the redirection with a Synthetic Canary.
A small challenge is that we like to have lifecycle rules on all our S3 buckets. With the above stack we actually have three buckets:
- The logging bucket for the Cloudfront distribution
- An empty S3 bucket that serves as the unused target for the Cloudfront distribution
- The artifacts bucket for the Synthetic Canary.
The first of these should have a retention rule; the second one is empty and it doesn’t matter. For the last of these, CDK creates a bucket if you don’t specify one, and you can’t (as of October 2022) pass lifecycle rules to it after the fact.
Who’s who?
Here’s a test for an S3 bucket in CDK:
1 | template.hasResourceProperties("AWS::S3::Bucket", { |
Nothing about that test tells me which of the three buckets, none of which had a lifecycle rule, is matching. All three can and do match. We need to differentiate them, and we’ll use tags. Here’s a code block to add a tag to the Cloudfront log bucket:
1 | Tags.of(this.logBucket).add('VanityPlateApplication', 'LogBucket'); |
Now, I can go back to the test and check for the tag in addition to the lifecycle rule:
1 | template.hasResourceProperties("AWS::S3::Bucket", { |
That test is unambiguous, and you can apply tags to auto-generated resources like the bucket created by the Synthetic Canary. This tagging strategy also creates some interesting possibilities for resource groups, but that’s another post.