Enforcing compliance with AWS CDK Aspects
Tags: aws, cdk, serverless, security
Published: 2022-01-24 03:39:08

---
title: Enforcing compliance with AWS CDK Aspects
published: true
description: Using AWS CDK Aspects for profit.
tags: aws, cdk, serverless, security
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k05sxpvb6bkdgbltlvuj.jpeg
---

Getting the security right in applications is tricky. Most developers did not undergo professional security training and are not adept in such topics. At least I know I'm not.

Luckily, multiple tools can help us achieve a relatively good security posture. [_Snyk_](https://snyk.io/), [_CodeQL_](https://codeql.github.com/) and [_GitGuardian_](https://www.gitguardian.com/) are good examples.

In some cases, even a deployment framework can be helpful. This blog post will expand on this topic, looking at how a particular feature of _AWS CDK_ can help us enforce compliance of various resources we deploy to AWS.

Enter _AWS CDK_ Aspects.

> The code this blog post is based on lives in [this GitHub repository](https://github.com/WojciechMatuszewski/compliance-aws-cdk-aspects)

## What are AWS CDK Aspects

_AWS CDK_ Aspects is a feature of the _AWS CDK_ framework that allows you to perform various operations on each _node_ of the [_AWS CDK Construct tree_](https://docs.aws.amazon.com/cdk/v2/guide/constructs.html#constructs_tree).

> Note that the Aspects code is invoked at the [_prepare phase_](https://docs.aws.amazon.com/cdk/v2/guide/apps.html#lifecycle) of _AWS CDK_ application lifecycle.

![AWS CDK Aspects and the Construct tree](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hacw3cnkm05wi3bqdy7f.png)

Imagine a scenario where you have to ensure that every _AWS S3_ bucket in the _AWS CDK_ application you maintain has the `BucketEncryption` property specified according to your organization's needs.

Instead of going bucket by bucket and applying the correct value for the `BucketEncryption` property, one might use _AWS CDK Aspects_ to programmatically set this property for all buckets deployed by the application.

With a basic overview of Aspects behind us, let us look at examples next.

## Practical examples

What follows is a series of examples showcasing the usefulness of _AWS CDK_ Aspects.

### Enforcing AWS S3 bucket encryption

[_AWS S3_](https://aws.amazon.com/s3/) is an excellent service. Sadly, when misconfigured, it can be a source of a data breach. There are many stories out there regarding publicly accessible _AWS S3_ buckets with plain-text files that hold important data.

Thankfully, with _AWS CDK_ Aspects, it is possible to **reduce** the human error factor regarding _AWS S3_ bucket configuration. The following is an example of ensuring that all the buckets within a `Stack` have `BucketEncryption` specified.

```ts
import { Aspects, aws_s3, IAspect, Stack, StackProps, App } from "aws-cdk-lib";
import { Construct, IConstruct } from "constructs";

export class UnencryptedBucket extends Construct {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const unencryptedBucket = new aws_s3.Bucket(this, "UnencryptedBucket", {
      /**
       * Per documentation: `Kms` if `encryptionKey` is specified, or `Unencrypted` otherwise.
       * The `undefined` is added for the sake of the example.
       */
      encryption: undefined
    });
  }
}

class BucketEncryptionChecker implements IAspect {
  public visit(node: IConstruct): void {
    if (node instanceof aws_s3.CfnBucket) {
      /**
       * Feel free to be more specific in terms of conditions in your code.
       */
      if (!node.bucketEncryption) {
        throw new Error("`bucketEncryption` must be specified.");
      }
    }
  }
}

class BucketStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    new UnencryptedBucket(this, "UnencryptedBucket");
  }
}

const app = new App();
const bucketStack = new BucketStack(app, "BucketStack");

Aspects.of(bucketStack).add(new BucketEncryptionChecker());
```

### Variable DeletionPolicy

The [`DeletionPolicy` attribute](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) instructs [_AWS CloudFormation_](https://aws.amazon.com/cloudformation/) what should happen to the resource when _AWS CloudFormation_ applies the "DELETE" action to that resource.

Imagine a scenario where someone removes the definition of a database from the template and pushes that _Change Set_ to _AWS CloudFormation_ – without the `DeletionPolicy` specified to `Retain`, _AWS CloudFormation_ delete the resource and all the data along with it!

Conversely, imagine deploying _emphermal stacks_ onto your development-only AWS account. You, most likely, would want all the resources gone whenever the stack is deleted – in such scenarios having the `DeletionPolicy` as `Delete` **might** be a wiser choice.

The following is an example of using _AWS CDK_ Aspects to specify the `DeletionPolicy` to `Delete` for all resources when deploying the stack in a dev-only environment and to `Retain` when deploying to a production environment.

```ts
import {
  App,
  Aspects,
  CfnResource,
  IAspect,
  RemovalPolicy,
  Stack,
  StackProps
} from "aws-cdk-lib";
import { Construct, IConstruct } from "constructs";

export class DeletionPolicySetter implements IAspect {
  constructor(private readonly policy: RemovalPolicy) {}
  visit(node: IConstruct): void {
    /**
     * Nothing stops you from adding more conditions here.
     */
    if (node instanceof CfnResource) {
      node.applyRemovalPolicy(this.policy);
    }
  }
}

class MyStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    /**
     * Your code...
     */
  }
}

const app = new App();
const myStack = new MyStack(app, "MyStack");

const removalPolicy =
  process.env.DEPLOYMENT_ENV == "DEV"
    ? RemovalPolicy.DESTROY
    : RemovalPolicy.RETAIN;

Aspects.of(myStack).add(new DeletionPolicySetter(removalPolicy));
```

Please be extra careful here. While, **to my best knowledge**, the change in the `DeletionPolicy` is an ["Update with No Interruption"](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html) the worst thing to do is to forget about setting the `DeletionPolicy` to `Delete` on resources holding production data.

## Closing words

I hope that you found the examples helpful. I've been using Aspects in my personal projects for a while now, and I found them very useful.
Found some neat use-case for Aspects? Please share it! I'm always keen on learning new things from the community.

For more _AWS CDK_ and serverless content, consider following me on Twitter - [@wm_matuszewski](https://twitter.com/wm_matuszewski)

Thank you for your valuable time.