Skip to content
Quest 11Advanced16 min

Build Your Own Construct

Two buckets, two teams, the same checklist every time: versioned, encrypted, private, TLS-only. Copy-paste works until someone forgets one line. A custom construct turns the checklist into code that every team imports.

What you'll learn

  • A construct is a class that extends Construct. Its children use this (or self) as their scope.
  • The construct's id becomes part of its children's path, so two instances never collide: Cards/Bucket and Sprites/Bucket.
  • Expose the pieces callers need (the inner bucket), and keep the defaults you want to enforce inside.

Section 01

Compose

A custom construct is the same pattern you have used all along: (scope, id, props). Inside the constructor, create other constructs and pass this as their scope. The result is a subtree that behaves like one component.

SecureBucket
export class SecureBucket extends Construct {
  public readonly bucket: s3.Bucket;

  constructor(scope: Construct, id: string) {
    super(scope, id);
    this.bucket = new s3.Bucket(this, "Bucket", {
      versioned: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      enforceSSL: true,
    });
  }
}

// In the stack:
new SecureBucket(this, "Cards");
new SecureBucket(this, "Sprites");

Section 02

Paths and logical IDs

Logical IDs come from the construct path, so the buckets are Cards/Bucket and Sprites/Bucket, and their logical IDs look like CardsBucketD3B90174 and SpritesBucket622D65C5. Run the exercise and read both IDs and their aws:cdk:path metadata.

This is the whole reason the id is mandatory: it makes composition safe. A construct can be used ten times in one stack without any author thinking about name clashes. Reusing an id among siblings fails with “There is already a Construct with name …”.

Section 03

Sharing it

A construct library is an ordinary package (npm, PyPI). Teams add the dependency and get the defaults. Keep the surface small: expose props for what truly varies, not every property of the bucket, so the safe choice is also the easy one. Add a test per default (Quest 10) so a refactor can't quietly weaken it.

Practice

Your turn

Finish SecureBucket, a construct every team can reuse. It must create its bucket inside itself (scope this, not the outer scope) with versioning, S3-managed encryption, Block Public Access and enforceSSL. Then use it twice in the stack: Cards and Sprites. Each SecureBucket holds exactly one bucket.

A bucket created with this as scope and the four secure settings, and two SecureBuckets: Cards and Sprites.
Tab inserts two spaces. Press Escape, then Tab, to leave the editor. Control or Command plus Enter runs the code.
Simulated — no AWS calls

The simulator runs the constructs this course teaches, plus simple loops and helper methods. Other CDK code may not be recognised.

Check

Check your understanding

Quick Quiz · one question, as many tries as you need.

Why can new SecureBucket(this, "Cards") and new SecureBucket(this, "Sprites") both create an inner bucket with id "Bucket"?

Choose one answer.