DEV Community

Kenta Goto for AWS Heroes

Posted on • Edited on

3 1 1 2 1

Using AWS CDK Property Injectors

What are Property Injectors?

Property Injectors is a feature that allows you to centrally modify properties (props) for all instances of "specific types of L2 Constructs (including some L3 Constructs)" within an App, Stack, or Construct. This feature was introduced in AWS CDK v2.196.0.

For detailed information, please refer to this RFC.

Note: This feature was added by this PR. (Actually, it was added by a different PR before this one, but that one was reverted)

For example, suppose you have a compliance requirement to set blockPublicAccess to BLOCK_ALL for all S3 buckets in your stack.

With Property Injectors, you can achieve this as follows.


First, create a MyBucketPropsInjector that implements IPropertyInjector.

Set this.constructUniqueId to the PROPERTY_INJECTION_ID exposed by the target L2 Construct, and implement the inject method to return new props.

export class MyBucketPropsInjector implements IPropertyInjector {
  public readonly constructUniqueId: string;

  constructor() {
    this.constructUniqueId = Bucket.PROPERTY_INJECTION_ID;
  }

  public inject(originalProps: BucketProps, _context: InjectionContext): BucketProps {
    return {
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
      enforceSSL: true,
      ...originalProps,
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Then, specify MyBucketPropsInjector for the scope where you want to apply it using one of several implementation methods as shown below. This will set blockPublicAccess to BLOCK_ALL for all buckets within the specified scope.

const app = new App();
const stack = new Stack(app, 'MyStack', {
  propertyInjectors: [new MyBucketPropsInjector()],
});
Enter fullscreen mode Exit fullscreen mode
const app = new App();

PropertyInjectors.of(app).add(new MyBucketPropsInjector());
Enter fullscreen mode Exit fullscreen mode

Comparison with Aspects

Differences from Aspects

To fulfill such requirements, you might have traditionally used Aspects.

When using Aspects for such purposes, you would modify properties owned by target L1 Constructs within the Aspects.

The newly introduced "Property Injectors" differs from Aspects in that it directly modifies the Construct's props rather than the Construct's properties.

In practice, Property Injectors internally generate and apply new props, so properties can be modified without issues even if they have the readonly modifier in the props definition.

However, since Property Injectors only modify props of L2 and L3 Constructs, they cannot target L1 Constructs unlike Aspects.

Also, Aspects are executed in the second "Prepare phase" of the CDK application lifecycle.

Prepare phase in CDK application lifecycle

In other words, Aspects are executed by traversing the Construct tree after all Constructs have been created.

However, Property Injectors directly modify props, so the value modifications are applied during Construct creation (the first "Construct phase" of the lifecycle).

Therefore, if you apply Property Injectors using PropertyInjectors.of().add after creating the Stack you want to apply them to, the modification process will not be executed.

const app = new cdk.App();

new MyStack(app, 'MyStack', props);

PropertyInjectors.of(app).add(new MyBucketPropsInjector());
Enter fullscreen mode Exit fullscreen mode

Call it before creating the Stack as shown below, or specify it in the propertyInjectors prop of the App or Stack.

const app = new cdk.App();

PropertyInjectors.of(app).add(new MyBucketPropsInjector());

new MyStack(app, 'MyStack', props);

// Or
new MyStack(app, 'MyStack', {
  propertyInjectors: [new MyBucketPropsInjector()],
});
Enter fullscreen mode Exit fullscreen mode

When to Use Property Injectors vs Aspects

In my opinion, the use cases might be divided as follows: (Since I'm not familiar with using Property Injectors yet, different perspectives might emerge later.)

Cases where Property Injectors are suitable:

  • When you want to modify at the granularity of L2 Construct props rather than properties owned by Constructs or CloudFormation properties
  • When you want to modify properties so that built-in validation within L2 Constructs passes
    • = Cases where errors occur before Aspects are executed

Cases where Aspects are suitable:

  • When you want to modify Constructs including L1 Constructs
  • When you want to modify properties at the granularity of CloudFormation properties
  • When you want to centrally inspect (but not modify) resource properties

Avoiding Infinite Loops

For example, suppose you want to create a serverAccessLogsBucket (which is also an S3 bucket type) within an S3 bucket using Property Injectors.

If you implement this naively like the previously introduced implementation, you'll end up with an infinite loop.

To avoid this, you need to implement skip processing as follows:

export class SpecialBucketInjector implements IPropertyInjector {
  public readonly constructUniqueId: string;

  // this variable will track if this Injector should be skipped.
  private _skip: boolean;

  constructor() {
    this._skip = false;
    this.constructUniqueId = Bucket.PROPERTY_INJECTION_ID;
  }

  public inject(originalProps: BucketProps, context: InjectionContext): BucketProps {
    if (this._skip) {
      return originalProps;
    }

    let accessLogBucket = originalProps.serverAccessLogsBucket;
    if (!accessLogBucket) {
      // When creating a new accessLogBucket, disable further Bucket injection.
      this._skip = true;

      // Since injection is disabled, make sure you provide all the necessary props.
      accessLogBucket = new Bucket(context.scope, 'my-access-log', {
        blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
        removalPolicy: originalProps.removalPolicy ?? core.RemovalPolicy.RETAIN,
      });

      // turn on injection for Bucket again.
      this._skip = false;
    }

    return {
      serverAccessLogsBucket: accessLogBucket,
      ...originalProps,
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Heroku

Build AI apps faster with Heroku.

Heroku makes it easy to build with AI, without the complexity of managing your own AI services. Access leading AI models and build faster with Managed Inference and Agents, and extend your AI with MCP.

Get Started

Top comments (0)

👋 Kindness is contagious

Explore this insightful write-up embraced by the inclusive DEV Community. Tech enthusiasts of all skill levels can contribute insights and expand our shared knowledge.

Spreading a simple "thank you" uplifts creators—let them know your thoughts in the discussion below!

At DEV, collaborative learning fuels growth and forges stronger connections. If this piece resonated with you, a brief note of thanks goes a long way.

Okay