Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC 0693: Property injectors #699

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

pcheungamz
Copy link

This is a request for comments about Set Default Values to Construct Properties using PropertyInjector. See #693 for
additional details.

APIs are signed off by @{rix0rrr}.


By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache-2.0 license


`IPropertyInjector` - An IPropertyInjector defines a way to inject additional properties that are not specified in the props. It is specific to one Construct and operates on that Construct’s properties. This will be called `Injector` for short.

`propertyInjectors` - A collection of Injectors attached to App, Stage, or Stack. Any matching Construct created in this App, Stage, or Stack will have the Injector applied to it.
Copy link

@moelasmar moelasmar Feb 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we also agreed on that the injectors can be also attached to any construct. It can be attached to any node in the scope tree.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have an example of that in the FAQ. The common way is to attach them to App, Stage, or Stack.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write something to clarify that here, it confuses me every time I read it. You can try verbiage like the following:

A collection of injectors attached to the construct tree. Injectors can be attached to any construct, but in practice we expect most of them will be attached to App, Stage or Stack.

That acknowledges both the genericity of the proposed solution as well as the expected use cases.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do. thanks

moelasmar
moelasmar previously approved these changes Feb 22, 2025
@mergify mergify bot dismissed moelasmar’s stale review February 25, 2025 05:48

Pull request has been modified.

@rix0rrr rix0rrr changed the title Blueprint RFC RFC 0693: Property injectors Feb 25, 2025
@rix0rrr rix0rrr changed the title RFC 0693: Property injectors RFC 0693: Property injection and construct blueprints Feb 25, 2025
@rix0rrr rix0rrr changed the title RFC 0693: Property injection and construct blueprints RFC 0693: Property injectors Feb 25, 2025
@@ -0,0 +1,468 @@
# Blueprint via Property Injection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This document is proposing "blueprints" and "property injection" at the same time.

I think the reader would benefit from a small definition of both terms and how they relate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to define that in line 7.

Blueprint is a way for orgs to enforce standards by setting default values to CDK Constructs.  The Blueprint standards can be shared and applied to many development teams.  This is done via injecting default property values at creation time.

What is a better way to communicate this?

Copy link
Contributor

@rix0rrr rix0rrr Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would phrase it as something like:

This document describes the mechanism of property injection, which is ...

This mechanism allows the implementation of blueprints, which are ...

The trick is now to define "blueprints" in a way that is substantially different enough from "applied property injectors" to add descriptive value.

I wonder if that's even possible, and if it's not just distracting. It might come down to "blueprints are a branded term for a particular library of property injectors"... which... fair enough, but is that worth describing in this doc?


`IPropertyInjector` - An IPropertyInjector defines a way to inject additional properties that are not specified in the props. It is specific to one Construct and operates on that Construct’s properties. This will be called `Injector` for short.

`propertyInjectors` - A collection of Injectors attached to App, Stage, or Stack. Any matching Construct created in this App, Stage, or Stack will have the Injector applied to it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write something to clarify that here, it confuses me every time I read it. You can try verbiage like the following:

A collection of injectors attached to the construct tree. Injectors can be attached to any construct, but in practice we expect most of them will be attached to App, Stage or Stack.

That acknowledges both the genericity of the proposed solution as well as the expected use cases.

* We will add Bucket.PROPERTY_INJECTION_ID to unique identify this Construct.
This is a new Property that we will add to Bucket and other supported Constructs.
* This TypeScript code will set blockPublicAccess to BlockPublicAccess.BLOCK_ALL and enforceSSL to true
if these properties are not specified in originalProps.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the properties are specified? It would be great to have a warning in case both the code and injector define a value for the property. Perhaps also the ability to explicitly suppress the warning.

Copy link
Author

@pcheungamz pcheungamz Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is up to the implementor of the Injector. For our org, we allow the builder's values to stand and only provide defaults to properties they don't specify. In this code snippet, the returned props will keep all the props the builder specified.

It is possible to use Annotation to give a warning about some prop value the builder specified. We intend to do this to warn builders that certain prop values are not recommended by our org.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do keep in mind that the current design will not make it possible for the property injector to add warnings on the construct (because when the injector executes, the construct won't exist yet!), so the best it can do is add warnings on its parent scope.

* **Tracking Issue**: [#693](https://github.com/aws/aws-cdk-rfcs/issues/693)
* **API Bar Raiser**: @rix0rrr

`Property injection` is a new mechanism that makes it possible to control the properties that are used to instantiate a construct via an out-of-band mechanism.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some alignment story with tools like cdk-nag?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading through the link you provided, I have a basic understand of cdk-nag. I think cdk-nag and Property Injectors are both trying to prevent builders from creating AWS resources incorrectly. cdk-nag will warn the builders about non-compliant resource creation, but does not provide remediation. Property Injectors will create resources with the correct properties.

Copy link
Contributor

@dbartholomae dbartholomae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this, I would need to check every layer above a construct for possible injectors to understand why behaviour is different. On the other hand, I don't really get anything compared to just having a custom construct library, as injectors still require teams to actually apply them and then not to overwrite the defaults. And if I really need to make sure that e.g. buckets are not set to public, I could instead add an Asepct that modifies all buckets to ensure that they are not public.
So for me, I don't really understand yet what this achieves that would justify the added complexity, especially since it breaks locality of code changes.

Blueprints by themselves are not a compliance enforcement mechanism;
instead they are a mechanism to make it easier for developers to hit compliance targets that are already being enforced via other means.

**Why do we need Blueprints?**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do Property Injectors interact with Aspects? When should I use property injectors to modify resources and when aspects? Based on the RFC so far I don't get yet why I should use injectors at all instead of just modifying the resources via Aspects.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The biggest difference between Aspect and Property Injector is that Aspect visits the Construct after it has been created - after the constructor completes. Property Injector runs as the first line in the constructor so we set all the default properties before the rest of the constructor code runs.

We want to create a L2 Construct with all the default values we want, instead of updating the Properties after the Construct has been created. Sometimes, it is not possible to update a Property. For example, serverAccessLogsBucket can only be specified in BucketProps. We cannot change it once aws_s3.Bucket has been created.


### Organization Level Standardization

As an org with many dev teams, we can standardize how AWS Resources are created by providing dev teams with Injectors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the teams still need to actitvely use the injector, then what's the benefit compared to just giving them a "SecureBucket" class to build upon? In both cases, the org still needs to rely on the devs to actually use the injector/construct, but with a SecureBucket at least the code would be easier to debug (anyone wondering why the bucket isn't publicly accessible needs to look just at the bucket, not go through every single Stack/Stage/App layer above to verify whether an injector exists).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One disadvantage of subclassing is that it breaks other tools like cdk-nag. cdk-nag checks for Bucket, but not SecureBucket subclass. Since Property Injector is native to Bucke, cdk-nag can work with it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another tradeoff to consider when writing your own SecureBucket is the maintenance burden that comes with it. You will constantly need to sync your package that contains SecureBucket with AWS-CDK. AWS-CDK is currently at 2.182.0 and there is usually a new minor version every week. My org currently uses a subclass solution and we are very familiar with this weekly sync'ing. This is why we are moving away from a subclass solution.

Copy link
Author

@pcheungamz pcheungamz Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also want to quote

Finally, keep in mind that writing your own "L2+" constructs might prevent your developers from 
taking advantage of AWS CDK packages such as [AWS Solutions Constructs]
(https://docs.aws.amazon.com/solutions/latest/constructs/welcome.html) or third-party constructs 
from Construct Hub. These packages are typically built on standard AWS CDK constructs and 
won't be able to use your wrapper constructs.

from this doc: https://docs.aws.amazon.com/cdk/v2/guide/best-practices.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants