You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Closes#21341
This feature was designed in [RFC648](aws/aws-cdk-rfcs#651)
### Reason for this change
The current algorithm for invoking aspects (see invokeAspects in [synthesis.ts](https://github.com/aws/aws-cdk/blob/8b495f9ec157c0b00674715f62b1bbcabf2096ac/packages/aws-cdk-lib/core/lib/private/synthesis.ts#L217)) does not handle all use cases — specifically, when an Aspect adds a new node to the Construct tree and when Aspects are applied out of order.
### Description of changes
This PR introduces a priority-based ordering system for aspects in the CDK to allow users to control the order in which aspects are applied on the construct tree. This PR also adds a stabilization loop for invoking aspects that can be enabled via the feature flag `@aws-cdk/core:aspectStabilization` - the stabilization loop ensures that newly added Aspects to the construct tree are visited and nested Aspects are invoked.
### Description of how you validated changes
Plenty of unit tests - see `aspects.test.ts`.
### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)
----
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Copy file name to clipboardExpand all lines: packages/aws-cdk-lib/core/README.md
+142
Original file line number
Diff line number
Diff line change
@@ -1629,6 +1629,148 @@ scenarios are (non-exhaustive list):
1629
1629
valid)
1630
1630
- Warn if the user is using a deprecated API
1631
1631
1632
+
## Aspects
1633
+
1634
+
[Aspects](https://docs.aws.amazon.com/cdk/v2/guide/aspects.html) is a feature in CDK that allows you to apply operations or transformations across all
1635
+
constructs in a construct tree. Common use cases include tagging resources, enforcing encryption on S3 Buckets, or applying specific security or
1636
+
compliance rules to all resources in a stack.
1637
+
1638
+
Conceptually, there are two types of Aspects:
1639
+
1640
+
- **Read-only aspects** scan the construct tree but do not make changes to the tree. Common use cases of read-only aspects include performing validations
1641
+
(for example, enforcing that all S3 Buckets have versioning enabled) and logging (for example, collecting information about all deployed resources for
1642
+
audits or compliance).
1643
+
- **Mutating aspects** either (1.) add new nodes or (2.) mutate existing nodes of the tree in-place. One commonly used mutating Aspect is adding Tags to
1644
+
resources. An example of an Aspect that adds a node is one that automatically adds a security group to every EC2 instance in the construct tree if
1645
+
no default is specified.
1646
+
1647
+
Here is a simple example of creating and applying an Aspect on a Stack to enable versioning on all S3 Buckets:
1648
+
1649
+
```ts
1650
+
import { IAspect, IConstruct, Tags, Stack } from 'aws-cdk-lib';
1651
+
1652
+
class EnableBucketVersioning implements IAspect {
1653
+
visit(node: IConstruct) {
1654
+
if (node instanceof CfnBucket) {
1655
+
node.versioningConfiguration = {
1656
+
status: 'Enabled'
1657
+
};
1658
+
}
1659
+
}
1660
+
}
1661
+
1662
+
const app = new App();
1663
+
const stack = new MyStack(app, 'MyStack');
1664
+
1665
+
// Apply the aspect to enable versioning on all S3 Buckets
The modern behavior is that Aspects automatically run on newly added nodes to the construct tree. This is controlled by the
1672
+
flag `@aws-cdk/core:aspectStabilization`, which is default for new projects (since version 2.172.0).
1673
+
1674
+
The old behavior of Aspects (without stabilization) was that Aspect invocation runs once on the entire construct
1675
+
tree. This meant that nested Aspects (Aspects that create new Aspects) are not invoked and nodes created by Aspects at a higher level of the construct tree are not visited.
1676
+
1677
+
To enable the stabilization behavior for older versions, use this feature by putting the following into your `cdk.context.json`:
1678
+
1679
+
```json
1680
+
{
1681
+
"@aws-cdk/core:aspectStabilization": true
1682
+
}
1683
+
```
1684
+
1685
+
### Aspect Priorities
1686
+
1687
+
Users can specify the order in which Aspects are applied on a construct by using the optional priority parameter when applying an Aspect. Priority
1688
+
values must be non-negative integers, where a higher number means the Aspect will be applied later, and a lower number means it will be applied sooner.
1689
+
1690
+
By default, newly created nodes always inherit aspects. Priorities are mainly for ordering between mutating aspects on the construct tree.
1691
+
1692
+
CDK provides standard priority values for mutating and readonly aspects to help ensure consistency across different construct libraries:
1693
+
1694
+
```ts
1695
+
/**
1696
+
* Default Priority values for Aspects.
1697
+
*/
1698
+
export class AspectPriority {
1699
+
/**
1700
+
* Suggested priority for Aspects that mutate the construct tree.
1701
+
*/
1702
+
static readonly MUTATING: number = 200;
1703
+
1704
+
/**
1705
+
* Suggested priority for Aspects that only read the construct tree.
1706
+
*/
1707
+
static readonly READONLY: number = 1000;
1708
+
1709
+
/**
1710
+
* Default priority for Aspects that are applied without a priority.
1711
+
*/
1712
+
static readonly DEFAULT: number = 500;
1713
+
}
1714
+
```
1715
+
1716
+
If no priority is provided, the default value will be 500. This ensures that aspects without a specified priority run after mutating aspects but before
1717
+
any readonly aspects.
1718
+
1719
+
Correctly applying Aspects with priority values ensures that mutating aspects (such as adding tags or resources) run before validation aspects. This allows users to avoid misconfigurations and ensure that the final
1720
+
construct tree is fully validated before being synthesized.
1721
+
1722
+
### Applying Aspects with Priority
1723
+
1724
+
```ts
1725
+
import { Aspects, Stack, IAspect, Tags } from 'aws-cdk-lib';
1726
+
import { Bucket } from 'aws-cdk-lib/aws-s3';
1727
+
1728
+
class MyAspect implements IAspect {
1729
+
visit(node: IConstruct) {
1730
+
// Modifies a resource in some way
1731
+
}
1732
+
}
1733
+
1734
+
class ValidationAspect implements IAspect {
1735
+
visit(node: IConstruct) {
1736
+
// Perform some readonly validation on the cosntruct tree
1737
+
}
1738
+
}
1739
+
1740
+
const stack = new Stack();
1741
+
1742
+
Aspects.of(stack).add(new MyAspect(), { priority: AspectPriority.MUTATING } ); // Run first (mutating aspects)
1743
+
Aspects.of(stack).add(new ValidationAspect(), { priority: AspectPriority.READONLY } ); // Run later (readonly aspects)
1744
+
```
1745
+
1746
+
### Inspecting applied aspects and changing priorities
1747
+
1748
+
We also give customers the ability to view all of their applied aspects and override the priority on these aspects.
1749
+
The `AspectApplication` class represents an Aspect that is applied to a node of the construct tree with a priority.
1750
+
1751
+
Users can access AspectApplications on a node by calling `applied` from the Aspects class as follows:
1752
+
1753
+
```ts
1754
+
const app = new App();
1755
+
const stack = new MyStack(app, 'MyStack');
1756
+
1757
+
Aspects.of(stack).add(new MyAspect());
1758
+
1759
+
let aspectApplications: AspectApplication[] = Aspects.of(root).applied;
1760
+
1761
+
for (const aspectApplication of aspectApplications) {
1762
+
// The aspect we are applying
1763
+
console.log(aspectApplication.aspect);
1764
+
// The construct we are applying the aspect to
1765
+
console.log(aspectApplication.construct);
1766
+
// The priority it was applied with
1767
+
console.log(aspectApplication.priority);
1768
+
1769
+
// Change the priority
1770
+
aspectApplication.priority = 700;
1771
+
}
1772
+
```
1773
+
1632
1774
### Acknowledging Warnings
1633
1775
1634
1776
If you would like to run with `--strict` mode enabled (warnings will throw
0 commit comments