aws / aws/aws-cdk

(core): Add facility to express implicit dependencies via non-ref/attribute tokens

Open
#35,873 4 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/core effort/medium feature-request p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
1d 19h
Merged PRs (30d)
74

Description

### Describe the feature

There currently (to my knowledge) exists two main methods to express dependencies between Cloud Formation resources within CDK:
1. Passing Cloud Formation native references between constructs
* This is achieved with tokens that resolve down to the appropriate Cloud Formation intrinsic function
2. Adding an explicit construct-to-construct dependency with `construct.node.addDependency(dependencyConstruct)`
* [During synthesis, these dependencies are promoted](https://github.com/aws/aws-cdk/blob/a7a6b406fb5869bbd063a56f1c1f4d6449586bf1/packages/aws-cdk-lib/core/lib/private/prepare-app.ts#L19-L28) to form explicit dependencies between Cloud Formation resources by finding all dependent `CfnResource` instances and adding all dependency `CfnResource` instances to the `DependsOn` property

This feature aims to add a more approachable facility to the second method by enabling tokens to carry extra dependency information along with the arbitrary value that they represent.

### Use Case

Cloud Formation resources often reflect parts of the input in their returned references or attributes to allow other resources to effectively reference that input value *through* the resource; however, there are situations where this either wasn't possible due to the resource taking in multiple values, wasn't done because it didn't make sense for the resource itself, or it just isn't applicable because its merely an input to the resource and not a unique part of its identity.

These often show up in situations where an resource is primarily allocated in one CFN resource but requires some additional CFN resource(s) to be meaningfully available/active. This proposed feature would be useful anywhere where we currently must use `construct.node.addDependency(otherConstruct)`.

Notable examples I've personally run into:
* IAM Policy/RolePolicy/ManagedPolicy resources can enhance permissions of a role in a manner that isn't a reference but may be necessary in order to have the role actually usable
* Custom resources like `CrossAccountZoneDelegationRecord` which have no return values or attributes exposed at all
* [AWS::OpenSearchServerless::AccessPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-opensearchserverless-accesspolicy.html) and likely other types of resource policies which enhance a principle's access without modifying the role directly

### Proposed Solution

To better allow users of CDK to model situations where explicit `DependsOn` are needed, I propose adding a new `Intrinsic` implementation, called `DependencyToken` (open to naming suggestions), which accepts an arbitrary value and a list of `IConstruct` dependencies. When resolved, this `DependencyToken` will first find and add all transitive `CfnResource` dependencies from the construction dependency list to the `CfnResource` scope which is resolving the value, then return the value it was created with.

[CfnResource's `_toCloudFormation` implementation](https://github.com/aws/aws-cdk/blob/a7a6b406fb5869bbd063a56f1c1f4d6449586bf1/packages/aws-cdk-lib/core/lib/cfn-resource.ts#L432-L456) will need to be updated to move the resolution of `DependsOn` to the postProcess part of the resolution so that the dependencies added by DependencyToken are reflected in the final output.

Usage may look like this test:
```
const app = new core.App();
const stack = new core.Stack(app, 'TestStack');
const resource1 = new core.CfnResource(stack, 'Resource1', { type: 'Test::Resource::Fake1' });
const sharedValue: string = DependencyToken.asString("foo", [resource1]);

const resource2 = new core.CfnResource(stack, 'Resource2', {
type: 'Test::Resource::Fake2',
properties: {
FakeProperty: sharedValue
}
});

expect(app.synth().getStackByName(stack.stackName).template.Resources).toEqual({
Resource1: {
Type: 'Test::Resource::Fake1',
},
Resource2: {
Type: 'Test::Resource::Fake2',
DependsOn: [
'Resource1',
],
Properties: {
FakeProperty: "foo"
}
},
});
```

A basic proof of concept implementation can be seen in my fork's [features/DependentToken branch](https://github.com/aws/aws-cdk/compare/main...brandondahler:aws-cdk:features/DependentToken)

### Other Information

_No response_

### Acknowledgements

- [x] I may be able to implement this feature request
- [ ] This feature might incur a breaking change

### AWS CDK Library version (aws-cdk-lib)

2.221.0

### AWS CDK CLI version

2.1031.0

### Environment details (OS name and version, etc.)

N/A

Contributor guide

Open the contributing guide

Research direction

Start by reading packages/aws-cdk-lib/core/lib/private/prepare-app.ts and packages/aws-cdk-lib/core/lib/cfn-resource.ts, especially the linked dependency promotion and _toCloudFormation areas. Review the proof-of-concept branch and reproduce the issue's synthesis example. Done means the synthesized template carries the expected DependsOn entry while preserving the arbitrary property value.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.