From ff6a90696f07cba3bfd15eb1af67d5d5e720ba93 Mon Sep 17 00:00:00 2001 From: Kyle Laker Date: Sat, 30 Dec 2023 21:22:53 -0500 Subject: [PATCH 1/4] doc(core): update old recommendations in core README `splitArn` is the correct updated version of `parseArn`. It's now necessary to use `ArnFormat` not a literal string for the format of the ARN resource for `splitArn` and `formatArn`. Finally, to fit with the recommendations to use a singleton pattern for custom resource handlers, the docs for Lambda are changed to recommend just using a `SingletonFunction`. --- packages/aws-cdk-lib/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 1aa3f0db1b592..8053fe3512360 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -396,7 +396,7 @@ CloudFormation to re-read the secret. ## ARN manipulation Sometimes you will need to put together or pick apart Amazon Resource Names -(ARNs). The functions `stack.formatArn()` and `stack.parseArn()` exist for +(ARNs). The functions `stack.formatArn()` and `stack.splitArn()` exist for this purpose. `formatArn()` can be used to build an ARN from components. It will automatically @@ -409,12 +409,12 @@ declare const stack: Stack; stack.formatArn({ service: 'lambda', resource: 'function', - sep: ':', + arnFormat: ArnFormat.COLON_RESOURCE_NAME, resourceName: 'MyFunction' }); ``` -`parseArn()` can be used to get a single component from an ARN. `parseArn()` +`splitArn()` can be used to get a single component from an ARN. `splitArn()` will correctly deal with both literal ARNs and deploy-time values (tokens), but in case of a deploy-time value be aware that the result will be another deploy-time value which cannot be inspected in the CDK application. @@ -423,14 +423,13 @@ deploy-time value which cannot be inspected in the CDK application. declare const stack: Stack; // Extracts the function name out of an AWS Lambda Function ARN -const arnComponents = stack.parseArn(arn, ':'); +const arnComponents = stack.splitArn(arn, ArnFormat.COLON_RESOURCE_NAME); const functionName = arnComponents.resourceName; ``` -Note that depending on the service, the resource separator can be either -`:` or `/`, and the resource name can be either the 6th or 7th -component in the ARN. When using these functions, you will need to know -the format of the ARN you are dealing with. +Note that the format of the resource separator depends on the service and +may be any of the values supported by `ArnFormat`. When dealing with these +functions, it is important to know the format of the ARN you are dealing with. For an exhaustive list of ARN formats used in AWS, see [AWS ARNs and Namespaces](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) @@ -611,7 +610,7 @@ response to the CloudFormation service and handle various error cases. Set `serviceToken` to `lambda.functionArn` to use this provider: ```ts -const fn = new lambda.Function(this, 'MyProvider', functionProps); +const fn = new lambda.SingletonFunction(this, 'MyProvider', functionProps); new CustomResource(this, 'MyResource', { serviceToken: fn.functionArn, From 8d150f1af0414c77c6d794c6fa91bf7394cd384b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 2 Jan 2024 19:06:24 +0100 Subject: [PATCH 2/4] update rosetta --- packages/aws-cdk-lib/rosetta/default.ts-fixture | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/aws-cdk-lib/rosetta/default.ts-fixture b/packages/aws-cdk-lib/rosetta/default.ts-fixture index 4b594b61d4233..d55c39d54e439 100644 --- a/packages/aws-cdk-lib/rosetta/default.ts-fixture +++ b/packages/aws-cdk-lib/rosetta/default.ts-fixture @@ -12,6 +12,7 @@ import * as sqs from 'aws-cdk-lib/aws-sqs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import { Annotations, + ArnFormat, App, Aws, CfnCondition, From 62a2878742459e38e31faec41c325e5ea101a699 Mon Sep 17 00:00:00 2001 From: Kyle Laker Date: Tue, 2 Jan 2024 23:50:50 -0500 Subject: [PATCH 3/4] Synchronize READMEs --- packages/aws-cdk-lib/README.md | 89 ++++++++++++++++- packages/aws-cdk-lib/core/README.md | 144 ++++++++++++++++++---------- 2 files changed, 181 insertions(+), 52 deletions(-) diff --git a/packages/aws-cdk-lib/README.md b/packages/aws-cdk-lib/README.md index 8053fe3512360..446cb1a118976 100644 --- a/packages/aws-cdk-lib/README.md +++ b/packages/aws-cdk-lib/README.md @@ -624,7 +624,8 @@ framework designed to implement simple and slim custom resource providers. It currently only supports Node.js-based user handlers, represents permissions as raw JSON blobs instead of `iam.PolicyStatement` objects, and it does not have support for asynchronous waiting (handler cannot exceed the 15min lambda -timeout). +timeout). The `CustomResourceProviderRuntime` supports runtime `nodejs12.x`, +`nodejs14.x`, `nodejs16.x`, `nodejs18.x`. [`@aws-cdk/core.CustomResourceProvider`]: https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.CustomResourceProvider.html @@ -1095,6 +1096,31 @@ declare const regionTable: CfnMapping; regionTable.findInMap(Aws.REGION, 'regionName'); ``` +An optional default value can also be passed to `findInMap`. If either key is not found in the map and the mapping is lazy, `findInMap` will return the default value and not render the mapping. +If the mapping is not lazy or either key is an unresolved token, the call to `findInMap` will return a token that resolves to +`{ "Fn::FindInMap": [ "MapName", "TopLevelKey", "SecondLevelKey", { "DefaultValue": "DefaultValue" } ] }`, and the mapping will be rendered. +Note that the `AWS::LanguageExtentions` transform is added to enable the default value functionality. + +For example, the following code will again not produce anything in the "Mappings" section. The +call to `findInMap` will be able to resolve the value during synthesis and simply return +`'Region not found'`. + +```ts +const regionTable = new CfnMapping(this, 'RegionTable', { + mapping: { + 'us-east-1': { + regionName: 'US East (N. Virginia)', + }, + 'us-east-2': { + regionName: 'US East (Ohio)', + }, + }, + lazy: true, +}); + +regionTable.findInMap('us-west-1', 'regionName', 'Region not found'); +``` + [cfn-mappings]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html ### Dynamic References @@ -1186,6 +1212,13 @@ const stack = new Stack(app, 'StackName', { }); ``` +You can also set termination protection with the setter after you've instantiated the stack. + +```ts +const stack = new Stack(app, 'StackName', {}); +stack.terminationProtection = true; +``` + By default, termination protection is disabled. ### Description @@ -1244,6 +1277,20 @@ It's possible to synthesize the project with more Resources than the allowed (or Set the context key `@aws-cdk/core:stackResourceLimit` with the proper value, being 0 for disable the limit of resources. +### Template Indentation + +The AWS CloudFormation templates generated by CDK include indentation by default. +Indentation makes the templates more readable, but also increases their size, +and CloudFormation templates cannot exceed 1MB. + +It's possible to reduce the size of your templates by suppressing indentation. + +To do this for all templates, set the context key `@aws-cdk/core:suppressTemplateIndentation` to `true`. + +To do this for a specific stack, add a `suppressTemplateIndentation: true` property to the +stack's `StackProps` parameter. You can also set this property to `false` to override +the context key setting. + ## App Context [Context values](https://docs.aws.amazon.com/cdk/v2/guide/context.html) are key-value pairs that can be associated with an app, stack, or construct. @@ -1439,6 +1486,10 @@ class MyPlugin implements IPolicyValidationPluginBeta1 { } ``` +In addition to the name, plugins may optionally report their version (`version` +property ) and a list of IDs of the rules they are going to evaluate (`ruleIds` +property). + Note that plugins are not allowed to modify anything in the cloud assembly. Any attempt to do so will result in synthesis failure. @@ -1451,4 +1502,40 @@ add it to the `postinstall` [script](https://docs.npmjs.com/cli/v9/using-npm/scripts) in the `package.json` file. +## Annotations + +Construct authors can add annotations to constructs to report at three different +levels: `ERROR`, `WARN`, `INFO`. + +Typically warnings are added for things that are important for the user to be +aware of, but will not cause deployment errors in all cases. Some common +scenarios are (non-exhaustive list): + +- Warn when the user needs to take a manual action, e.g. IAM policy should be + added to an referenced resource. +- Warn if the user configuration might not follow best practices (but is still + valid) +- Warn if the user is using a deprecated API + +### Acknowledging Warnings + +If you would like to run with `--strict` mode enabled (warnings will throw +errors) it is possible to `acknowledge` warnings to make the warning go away. + +For example, if > 10 IAM managed policies are added to an IAM Group, a warning +will be created: + +```text +IAM:Group:MaxPoliciesExceeded: You added 11 to IAM Group my-group. The maximum number of managed policies attached to an IAM group is 10. +``` + +If you have requested a [quota increase](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html#reference_iam-quotas-entities) +you may have the ability to add > 10 managed policies which means that this +warning does not apply to you. You can acknowledge this by `acknowledging` the +warning by the `id`. + +```ts +Annotations.of(this).acknowledgeWarning('IAM:Group:MaxPoliciesExceeded', 'Account has quota increased to 20'); +``` + diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 4c7e9ecd309d0..446cb1a118976 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -17,9 +17,37 @@ dependencies. According to the kind of project you are developing: -- For projects that are CDK libraries, declare them both under the `devDependencies` - **and** `peerDependencies` sections. -- For CDK apps, declare them under the `dependencies` section only. +For projects that are CDK libraries in NPM, declare them both under the `devDependencies` **and** `peerDependencies` sections. +To make sure your library is compatible with the widest range of CDK versions: pick the minimum `aws-cdk-lib` version +that your library requires; declare a range dependency with a caret on that version in peerDependencies, and declare a +point version dependency on that version in devDependencies. + +For example, let's say the minimum version your library needs is `2.38.0`. Your `package.json` should look like this: + +```javascript +{ + "peerDependencies": { + "aws-cdk-lib": "^2.38.0", + "constructs": "^10.0.0" + }, + "devDependencies": { + /* Install the oldest version for testing so we don't accidentally use features from a newer version than we declare */ + "aws-cdk-lib": "2.38.0" + } +} +``` + +For CDK apps, declare them under the `dependencies` section. Use a caret so you always get the latest version: + +```json +{ + "dependencies": { + "aws-cdk-lib": "^2.38.0", + "constructs": "^10.0.0" + } +} +``` + ### Use in your code @@ -27,20 +55,27 @@ According to the kind of project you are developing: You can use a classic import to get access to each service namespaces: -```ts -import { aws_s3 as s3 } from 'aws-cdk-lib'; +```ts nofixture +import { Stack, App, aws_s3 as s3 } from 'aws-cdk-lib'; -new s3.Bucket(this, 'TestBucket'); +const app = new App(); +const stack = new Stack(app, 'TestStack'); + +new s3.Bucket(stack, 'TestBucket'); ``` #### Barrel import Alternatively, you can use "barrel" imports: -```ts +```ts nofixture +import { App, Stack } from 'aws-cdk-lib'; import { Bucket } from 'aws-cdk-lib/aws-s3'; -new Bucket(this, 'TestBucket'); +const app = new App(); +const stack = new Stack(app, 'TestStack'); + +new Bucket(stack, 'TestBucket'); ``` @@ -361,7 +396,7 @@ CloudFormation to re-read the secret. ## ARN manipulation Sometimes you will need to put together or pick apart Amazon Resource Names -(ARNs). The functions `stack.formatArn()` and `stack.parseArn()` exist for +(ARNs). The functions `stack.formatArn()` and `stack.splitArn()` exist for this purpose. `formatArn()` can be used to build an ARN from components. It will automatically @@ -374,12 +409,12 @@ declare const stack: Stack; stack.formatArn({ service: 'lambda', resource: 'function', - sep: ':', + arnFormat: ArnFormat.COLON_RESOURCE_NAME, resourceName: 'MyFunction' }); ``` -`parseArn()` can be used to get a single component from an ARN. `parseArn()` +`splitArn()` can be used to get a single component from an ARN. `splitArn()` will correctly deal with both literal ARNs and deploy-time values (tokens), but in case of a deploy-time value be aware that the result will be another deploy-time value which cannot be inspected in the CDK application. @@ -388,14 +423,13 @@ deploy-time value which cannot be inspected in the CDK application. declare const stack: Stack; // Extracts the function name out of an AWS Lambda Function ARN -const arnComponents = stack.parseArn(arn, ':'); +const arnComponents = stack.splitArn(arn, ArnFormat.COLON_RESOURCE_NAME); const functionName = arnComponents.resourceName; ``` -Note that depending on the service, the resource separator can be either -`:` or `/`, and the resource name can be either the 6th or 7th -component in the ARN. When using these functions, you will need to know -the format of the ARN you are dealing with. +Note that the format of the resource separator depends on the service and +may be any of the values supported by `ArnFormat`. When dealing with these +functions, it is important to know the format of the ARN you are dealing with. For an exhaustive list of ARN formats used in AWS, see [AWS ARNs and Namespaces](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) @@ -576,7 +610,7 @@ response to the CloudFormation service and handle various error cases. Set `serviceToken` to `lambda.functionArn` to use this provider: ```ts -const fn = new lambda.Function(this, 'MyProvider', functionProps); +const fn = new lambda.SingletonFunction(this, 'MyProvider', functionProps); new CustomResource(this, 'MyResource', { serviceToken: fn.functionArn, @@ -678,8 +712,8 @@ exports.handler = async (e) => { `sum.ts`: ```ts nofixture +import { Construct } from 'constructs'; import { - Construct, CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, @@ -781,7 +815,7 @@ new CustomResource(this, 'MyResource', { }); ``` -See the [documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/custom-resources-readme.html) for more details. +See the [documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-cdk-lib.custom_resources-readme.html) for more details. ## AWS CloudFormation features @@ -905,7 +939,7 @@ a property of the creationPolicy on the resource options. Setting it to true wil resources that depend on the fleet resource. ```ts -const fleet = new CfnFleet(stack, 'Fleet', { +const fleet = new appstream.CfnFleet(this, 'Fleet', { instanceType: 'stream.standard.small', name: 'Fleet', computeCapacity: { @@ -928,10 +962,14 @@ The format of the timeout is `PT#H#M#S`. In the example below AWS Cloudformation `CREATE_COMPLETE`. ```ts -resource.cfnOptions.resourceSignal = { - count: 3, - timeout: 'PR15M', -} +declare const resource: CfnResource; + +resource.cfnOptions.creationPolicy = { + resourceSignal: { + count: 3, + timeout: 'PR15M', + } +}; ``` [creation-policy]: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-creationpolicy.html @@ -1330,8 +1368,6 @@ to all roles within a specific construct scope. The most common use case would be to apply a permissions boundary at the `Stage` level. ```ts -declare const app: App; - const prodStage = new Stage(app, 'ProdStage', { permissionsBoundary: PermissionsBoundary.fromName('cdk-${Qualifier}-PermissionsBoundary'), }); @@ -1363,11 +1399,11 @@ will be printed to the console or to a file (see below). To use one or more validation plugins in your application, use the `policyValidationBeta1` property of `Stage`: -```ts +```ts fixture=validation-plugin // globally for the entire app (an app is a stage) const app = new App({ policyValidationBeta1: [ - // These hypothetical classes implement IValidationPlugin: + // These hypothetical classes implement IPolicyValidationPluginBeta1: new ThirdPartyPluginX(), new ThirdPartyPluginY(), ], @@ -1375,7 +1411,9 @@ const app = new App({ // only apply to a particular stage const prodStage = new Stage(app, 'ProdStage', { - policyValidationBeta1: [...], + policyValidationBeta1: [ + new ThirdPartyPluginX(), + ], }); ``` @@ -1412,35 +1450,39 @@ the standard output. ### For plugin authors The communication protocol between the CDK core module and your policy tool is -defined by the `IValidationPluginBeta1` interface. To create a new plugin you must +defined by the `IPolicyValidationPluginBeta1` interface. To create a new plugin you must write a class that implements this interface. There are two things you need to implement: the plugin name (by overriding the `name` property), and the `validate()` method. -The framework will call `validate()`, passing an `IValidationContextBeta1` object. +The framework will call `validate()`, passing an `IPolicyValidationContextBeta1` object. The location of the templates to be validated is given by `templatePaths`. The -plugin should return an instance of `ValidationPluginReportBeta1`. This object +plugin should return an instance of `PolicyValidationPluginReportBeta1`. This object represents the report that the user wil receive at the end of the synthesis. -```ts -validate(context: ValidationContextBeta1): ValidationReportBeta1 { - // First read the templates using context.templatePaths... - - // ...then perform the validation, and then compose and return the report. - // Using hard-coded values here for better clarity: - return { - success: false, - violations: [{ - ruleName: 'CKV_AWS_117', - recommendation: 'Ensure that AWS Lambda function is configured inside a VPC', - fix: 'https://docs.bridgecrew.io/docs/ensure-that-aws-lambda-function-is-configured-inside-a-vpc-1', - violatingResources: [{ - resourceName: 'MyFunction3BAA72D1', - templatePath: '/home/johndoe/myapp/cdk.out/MyService.template.json', - locations: 'Properties/VpcConfig', +```ts fixture=validation-plugin +class MyPlugin implements IPolicyValidationPluginBeta1 { + public readonly name = 'MyPlugin'; + + public validate(context: IPolicyValidationContextBeta1): PolicyValidationPluginReportBeta1 { + // First read the templates using context.templatePaths... + + // ...then perform the validation, and then compose and return the report. + // Using hard-coded values here for better clarity: + return { + success: false, + violations: [{ + ruleName: 'CKV_AWS_117', + description: 'Ensure that AWS Lambda function is configured inside a VPC', + fix: 'https://docs.bridgecrew.io/docs/ensure-that-aws-lambda-function-is-configured-inside-a-vpc-1', + violatingResources: [{ + resourceLogicalId: 'MyFunction3BAA72D1', + templatePath: '/home/johndoe/myapp/cdk.out/MyService.template.json', + locations: ['Properties/VpcConfig'], + }], }], - }], - }; + }; + } } ``` @@ -1483,7 +1525,7 @@ errors) it is possible to `acknowledge` warnings to make the warning go away. For example, if > 10 IAM managed policies are added to an IAM Group, a warning will be created: -``` +```text IAM:Group:MaxPoliciesExceeded: You added 11 to IAM Group my-group. The maximum number of managed policies attached to an IAM group is 10. ``` From 6c446aab1b03e33e77454679b8849b349c4897b3 Mon Sep 17 00:00:00 2001 From: Kyle Laker Date: Tue, 2 Jan 2024 23:52:25 -0500 Subject: [PATCH 4/4] update rosetta --- packages/aws-cdk-lib/rosetta/default.ts-fixture | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/rosetta/default.ts-fixture b/packages/aws-cdk-lib/rosetta/default.ts-fixture index d55c39d54e439..8e6da2abe5cee 100644 --- a/packages/aws-cdk-lib/rosetta/default.ts-fixture +++ b/packages/aws-cdk-lib/rosetta/default.ts-fixture @@ -58,7 +58,7 @@ declare const construct: Construct; declare const constructA: Construct; declare const constructB: Construct; declare const constructC: Construct; -declare const functionProps: lambda.FunctionProps; +declare const functionProps: lambda.SingletonFunctionProps; declare const isCompleteHandler: lambda.Function; declare const myBucket: s3.IBucket; declare const myFunction: lambda.IFunction;