diff --git a/.github/workflows/auto-approve-v2-merge-forward.yml b/.github/workflows/auto-approve-v2-merge-forward.yml deleted file mode 100644 index f05cd6753316c..0000000000000 --- a/.github/workflows/auto-approve-v2-merge-forward.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Automatically approve PRs that merge master forward to v2-main -# -# Only does approvals! mergify takes care of the actual merge. -name: Auto-approve automated PRs around CDK v2 -on: - pull_request: - types: - - labeled - - opened - - ready_for_review - - reopened - - synchronize - - unlabeled - - unlocked - -jobs: - approve: - runs-on: ubuntu-latest - steps: - - uses: hmarr/auto-approve-action@v2.0.0 - if: > - github.event.pull_request.user.login == 'aws-cdk-automation' - && github.event.pull_request.base.ref == 'v2-main' - && contains(github.event.pull_request.labels.*.name, 'pr/auto-approve') - with: - github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/v2-pull-request.yml b/.github/workflows/v2-pull-request.yml new file mode 100644 index 0000000000000..c4118d3298a00 --- /dev/null +++ b/.github/workflows/v2-pull-request.yml @@ -0,0 +1,46 @@ +# Automated actions for PRs against the v2-main branch +name: v2 +on: + pull_request: + branches: + - v2-main + types: + - labeled + - opened + - ready_for_review + - reopened + - synchronize + - unlabeled + - unlocked + +jobs: + # Run yarn pkglint on merge forward PRs and commit the results + pkglint: + if: contains(github.event.pull_request.labels.*.name, 'pr/forward-merge') + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v2 + with: + branch: ${{ github.event.pull_request.head.ref }} + token: ${{ secrets.GITHUB_TOKEN }} + - name: lint + run: |- + yarn install --frozen-lockfile + yarn pkglint + - name: push + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: 'automatic pkglint fixes' + + # Approve automated PRs + # Only approve! mergify takes care of the actual merge. + auto-approve: + if: > + github.event.pull_request.user.login == 'aws-cdk-automation' + && contains(github.event.pull_request.labels.*.name, 'pr/auto-approve') + runs-on: ubuntu-latest + steps: + - uses: hmarr/auto-approve-action@v2.0.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/yarn-upgrade.yml b/.github/workflows/yarn-upgrade.yml index 586e7f2c9357a..6734719a67184 100644 --- a/.github/workflows/yarn-upgrade.yml +++ b/.github/workflows/yarn-upgrade.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v2 - name: Set up Node - uses: actions/setup-node@v2.1.0 + uses: actions/setup-node@v2.1.4 with: node-version: 10 @@ -55,12 +55,15 @@ jobs: lerna exec --parallel ncu -- --upgrade --filter=@types/node,@types/fs-extra --target=minor lerna exec --parallel ncu -- --upgrade --filter=typescript --target=patch lerna exec --parallel ncu -- --upgrade --reject='@types/node,@types/fs-extra,constructs,typescript,aws-sdk,${{ steps.list-packages.outputs.list }}' --target=minor - # This will create a brand new `yarn.lock` file (this is more efficient than `yarn install && yarn upgrade`) - - name: Run "yarn install --force" - run: yarn install --force + # This will ensure the current lockfile is up-to-date with the dependency specifications (necessary for "yarn update" to run) + - name: Run "yarn install" + run: yarn install + + - name: Run "yarn upgrade" + run: yarn upgrade - name: Make Pull Request - uses: peter-evans/create-pull-request@v2 + uses: peter-evans/create-pull-request@v3 with: # Git commit details branch: automation/yarn-upgrade diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ff1bfa44ad1..8bf7a31e44214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.89.0](https://github.com/aws/aws-cdk/compare/v1.88.0...v1.89.0) (2021-02-09) + + +### Features + +* **cfnspec:** cloudformation spec v26.0.0 ([#12841](https://github.com/aws/aws-cdk/issues/12841)) ([f959b3a](https://github.com/aws/aws-cdk/commit/f959b3a2eeb5a9a9e44ea3f88622f77f7667bfa4)) +* **cloudfront:** add support for TrustedKeyGroups in Distribution and CloudFrontWebDistribution ([#12847](https://github.com/aws/aws-cdk/issues/12847)) ([349a6e2](https://github.com/aws/aws-cdk/commit/349a6e2bfaa72440deb3767fb1e28e38cc4d73ef)), closes [#11791](https://github.com/aws/aws-cdk/issues/11791) +* **core:** configure bundling docker entrypoint ([#12660](https://github.com/aws/aws-cdk/issues/12660)) ([6597a09](https://github.com/aws/aws-cdk/commit/6597a09310fbc13d43389eca91b0e4b26f8ca680)), closes [#11984](https://github.com/aws/aws-cdk/issues/11984) +* **ec2:** can define Launch Templates (not use them yet) ([#12385](https://github.com/aws/aws-cdk/issues/12385)) ([32c0de7](https://github.com/aws/aws-cdk/commit/32c0de74cf40f08a291c8589fd85f3dd636749ea)) +* **lambda:** layer version removal policy ([#12792](https://github.com/aws/aws-cdk/issues/12792)) ([5664480](https://github.com/aws/aws-cdk/commit/5664480a97958263ee7cb903c2aff0276e738dc3)), closes [#12718](https://github.com/aws/aws-cdk/issues/12718) +* **lambda:** nodejs14.x runtime ([#12861](https://github.com/aws/aws-cdk/issues/12861)) ([12c224a](https://github.com/aws/aws-cdk/commit/12c224a0f54230b6226de8defa527f7b53f9bc65)) + + +### Bug Fixes + +* **core:** append file extension to s3 asset key in new style synthesizer ([#12765](https://github.com/aws/aws-cdk/issues/12765)) ([77b9d39](https://github.com/aws/aws-cdk/commit/77b9d3930ec722be3a40e4013cd9335f90b0d945)), closes [#12740](https://github.com/aws/aws-cdk/issues/12740) +* **core:** incorrect GetParameter permissions in nonstandard partitions ([#12813](https://github.com/aws/aws-cdk/issues/12813)) ([be7202f](https://github.com/aws/aws-cdk/commit/be7202fa229435607e81d480726e9ce7f625b85a)) +* **ec2:** MachineImage.genericLinux/Windows don't work in environment-agnostic stacks ([#12546](https://github.com/aws/aws-cdk/issues/12546)) ([fbe7e89](https://github.com/aws/aws-cdk/commit/fbe7e89ba764093ddec9caa7de3ca921f3dc68ac)), closes [#8759](https://github.com/aws/aws-cdk/issues/8759) +* **ec2:** Subnet cidr missing for Vpc.from_lookup() ([#12878](https://github.com/aws/aws-cdk/issues/12878)) ([9028269](https://github.com/aws/aws-cdk/commit/90282693999efdc43330b9526b9d7f4cd0fa5736)), closes [#11821](https://github.com/aws/aws-cdk/issues/11821) +* **ec2:** VpnConnection fails if `ip` is a Token ([#12923](https://github.com/aws/aws-cdk/issues/12923)) ([953957a](https://github.com/aws/aws-cdk/commit/953957a2c3e630b5ad2196e113f943e27ee21067)), closes [#11633](https://github.com/aws/aws-cdk/issues/11633) +* **kms:** cross-environment usage fails when trustAccountIdentities is set ([#12925](https://github.com/aws/aws-cdk/issues/12925)) ([2b917ec](https://github.com/aws/aws-cdk/commit/2b917eceb598b3365123781445df7e2bd8a80b74)), closes [#12921](https://github.com/aws/aws-cdk/issues/12921) [#12741](https://github.com/aws/aws-cdk/issues/12741) +* **lambda-python:** cryptography >= 3.4 is not supported by older pip version ([#12934](https://github.com/aws/aws-cdk/issues/12934)) ([b68acf8](https://github.com/aws/aws-cdk/commit/b68acf828e04841dd7e62b30fe80db8c25e5d96e)), closes [/cryptography.io/en/3.4/changelog.html#v3-4](https://github.com/aws//cryptography.io/en/3.4/changelog.html/issues/v3-4) + ## [1.88.0](https://github.com/aws/aws-cdk/compare/v1.87.1...v1.88.0) (2021-02-03) diff --git a/packages/@aws-cdk/assert/lib/assertions/have-resource.ts b/packages/@aws-cdk/assert/lib/assertions/have-resource.ts index 3e44013188b2c..5a977d8252fd4 100644 --- a/packages/@aws-cdk/assert/lib/assertions/have-resource.ts +++ b/packages/@aws-cdk/assert/lib/assertions/have-resource.ts @@ -59,7 +59,7 @@ export class HaveResourceAssertion extends JestFriendlyAssertion properties === undefined ? anything() : allowValueExtension ? deepObjectLike(properties) : objectLike(properties); - this.part = part !== undefined ? part : ResourcePart.Properties; + this.part = part ?? ResourcePart.Properties; } public assertUsing(inspector: StackInspector): boolean { diff --git a/packages/@aws-cdk/assets/lib/staging.ts b/packages/@aws-cdk/assets/lib/staging.ts index 3a441a0219f55..fe573ce442fdc 100644 --- a/packages/@aws-cdk/assets/lib/staging.ts +++ b/packages/@aws-cdk/assets/lib/staging.ts @@ -1,7 +1,11 @@ -import { AssetStaging, Construct } from '@aws-cdk/core'; +import { AssetStaging } from '@aws-cdk/core'; import { toSymlinkFollow } from './compat'; import { FingerprintOptions } from './fs/options'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Deprecated * @deprecated use `core.AssetStagingProps` diff --git a/packages/@aws-cdk/aws-amplify/lib/app.ts b/packages/@aws-cdk/aws-amplify/lib/app.ts index 8e7217a59cc58..bf1d5bc3d5e89 100644 --- a/packages/@aws-cdk/aws-amplify/lib/app.ts +++ b/packages/@aws-cdk/aws-amplify/lib/app.ts @@ -218,9 +218,9 @@ export class App extends Resource implements IApp, iam.IGrantable { basicAuthConfig: props.autoBranchCreation.basicAuth && props.autoBranchCreation.basicAuth.bind(this, 'BranchBasicAuth'), buildSpec: props.autoBranchCreation.buildSpec && props.autoBranchCreation.buildSpec.toBuildSpec(), enableAutoBranchCreation: true, - enableAutoBuild: props.autoBranchCreation.autoBuild === undefined ? true : props.autoBranchCreation.autoBuild, + enableAutoBuild: props.autoBranchCreation.autoBuild ?? true, environmentVariables: Lazy.any({ produce: () => renderEnvironmentVariables(this.autoBranchEnvironmentVariables ) }, { omitEmptyArray: true }), // eslint-disable-line max-len - enablePullRequestPreview: props.autoBranchCreation.pullRequestPreview === undefined ? true : props.autoBranchCreation.pullRequestPreview, + enablePullRequestPreview: props.autoBranchCreation.pullRequestPreview ?? true, pullRequestEnvironmentName: props.autoBranchCreation.pullRequestEnvironmentName, stage: props.autoBranchCreation.stage, }, diff --git a/packages/@aws-cdk/aws-amplify/lib/branch.ts b/packages/@aws-cdk/aws-amplify/lib/branch.ts index e26fe6c4d46a2..210f27d4831e2 100644 --- a/packages/@aws-cdk/aws-amplify/lib/branch.ts +++ b/packages/@aws-cdk/aws-amplify/lib/branch.ts @@ -139,8 +139,8 @@ export class Branch extends Resource implements IBranch { branchName, buildSpec: props.buildSpec && props.buildSpec.toBuildSpec(), description: props.description, - enableAutoBuild: props.autoBuild === undefined ? true : props.autoBuild, - enablePullRequestPreview: props.pullRequestPreview === undefined ? true : props.pullRequestPreview, + enableAutoBuild: props.autoBuild ?? true, + enablePullRequestPreview: props.pullRequestPreview ?? true, environmentVariables: Lazy.any({ produce: () => renderEnvironmentVariables(this.environmentVariables) }, { omitEmptyArray: true }), pullRequestEnvironmentName: props.pullRequestEnvironmentName, stage: props.stage, diff --git a/packages/@aws-cdk/aws-amplify/lib/domain.ts b/packages/@aws-cdk/aws-amplify/lib/domain.ts index b657ebdefb247..bf6ba7afe8017 100644 --- a/packages/@aws-cdk/aws-amplify/lib/domain.ts +++ b/packages/@aws-cdk/aws-amplify/lib/domain.ts @@ -147,7 +147,7 @@ export class Domain extends Resource { private renderSubDomainSettings() { return this.subDomains.map(s => ({ branchName: s.branch.branchName, - prefix: s.prefix === undefined ? s.branch.branchName : s.prefix, + prefix: s.prefix ?? s.branch.branchName, })); } } diff --git a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts index a286e978fdafb..d44b2ab223f4a 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/deployment.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/deployment.ts @@ -1,10 +1,14 @@ import * as crypto from 'crypto'; -import { Construct as CoreConstruct, Lazy, RemovalPolicy, Resource, CfnResource } from '@aws-cdk/core'; +import { Lazy, RemovalPolicy, Resource, CfnResource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDeployment } from './apigateway.generated'; import { Method } from './method'; import { IRestApi, RestApi, SpecRestApi, RestApiBase } from './restapi'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + export interface DeploymentProps { /** * The Rest API to deploy. diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/http.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/http.ts index fac5bb7cc6800..65f0657e19817 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/http.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/http.ts @@ -38,7 +38,7 @@ export interface HttpIntegrationProps { */ export class HttpIntegration extends Integration { constructor(url: string, props: HttpIntegrationProps = { }) { - const proxy = props.proxy !== undefined ? props.proxy : true; + const proxy = props.proxy ?? true; const method = props.httpMethod || 'GET'; super({ type: proxy ? IntegrationType.HTTP_PROXY : IntegrationType.HTTP, diff --git a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts index 399484510f343..97772fe7eddb5 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts @@ -41,7 +41,7 @@ export class LambdaIntegration extends AwsIntegration { private readonly enableTest: boolean; constructor(handler: lambda.IFunction, options: LambdaIntegrationOptions = { }) { - const proxy = options.proxy === undefined ? true : options.proxy; + const proxy = options.proxy ?? true; super({ proxy, @@ -51,7 +51,7 @@ export class LambdaIntegration extends AwsIntegration { }); this.handler = handler; - this.enableTest = options.allowTestInvoke === undefined ? true : options.allowTestInvoke; + this.enableTest = options.allowTestInvoke ?? true; } public bind(method: Method): IntegrationConfig { diff --git a/packages/@aws-cdk/aws-apigateway/lib/resource.ts b/packages/@aws-cdk/aws-apigateway/lib/resource.ts index 33ec6f1d5f7fd..49d0bf0356d80 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/resource.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/resource.ts @@ -276,7 +276,7 @@ export abstract class ResourceBase extends ResourceConstruct implements IResourc // // statusCode - const statusCode = options.statusCode !== undefined ? options.statusCode : 204; + const statusCode = options.statusCode ?? 204; // // prepare responseParams @@ -522,7 +522,7 @@ export class ProxyResource extends Resource { defaultMethodOptions: props.defaultMethodOptions, }); - const anyMethod = props.anyMethod !== undefined ? props.anyMethod : true; + const anyMethod = props.anyMethod ?? true; if (anyMethod) { this.anyMethod = this.addMethod('ANY'); } diff --git a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts index 0911c92dda94b..32c400c52d23e 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/restapi.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/restapi.ts @@ -504,7 +504,7 @@ export abstract class RestApiBase extends Resource implements IRestApi { } protected configureDeployment(props: RestApiOptions) { - const deploy = props.deploy === undefined ? true : props.deploy; + const deploy = props.deploy ?? true; if (deploy) { this._latestDeployment = new Deployment(this, 'Deployment', { @@ -593,7 +593,7 @@ export class SpecRestApi extends RestApiBase { name: this.restApiName, policy: props.policy, failOnWarnings: props.failOnWarnings, - body: apiDefConfig.inlineDefinition ? apiDefConfig.inlineDefinition : undefined, + body: apiDefConfig.inlineDefinition ?? undefined, bodyS3Location: apiDefConfig.inlineDefinition ? undefined : apiDefConfig.s3Location, endpointConfiguration: this._configureEndpoints(props), parameters: props.parameters, @@ -608,7 +608,7 @@ export class SpecRestApi extends RestApiBase { this.addDomainName('CustomDomain', props.domainName); } - const cloudWatchRole = props.cloudWatchRole !== undefined ? props.cloudWatchRole : true; + const cloudWatchRole = props.cloudWatchRole ?? true; if (cloudWatchRole) { this.configureCloudWatchRole(resource); } @@ -700,13 +700,13 @@ export class RestApi extends RestApiBase { binaryMediaTypes: props.binaryMediaTypes, endpointConfiguration: this._configureEndpoints(props), apiKeySourceType: props.apiKeySourceType, - cloneFrom: props.cloneFrom ? props.cloneFrom.restApiId : undefined, + cloneFrom: props.cloneFrom?.restApiId, parameters: props.parameters, }); this.node.defaultChild = resource; this.restApiId = resource.ref; - const cloudWatchRole = props.cloudWatchRole !== undefined ? props.cloudWatchRole : true; + const cloudWatchRole = props.cloudWatchRole ?? true; if (cloudWatchRole) { this.configureCloudWatchRole(resource); } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/base-scalable-attribute.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/base-scalable-attribute.ts index 762da275c33a5..95d2a19c8a17d 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/base-scalable-attribute.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/base-scalable-attribute.ts @@ -58,7 +58,7 @@ export abstract class BaseScalableAttribute extends CoreConstruct { scalableDimension: this.props.dimension, resourceId: this.props.resourceId, role: this.props.role, - minCapacity: props.minCapacity !== undefined ? props.minCapacity : 1, + minCapacity: props.minCapacity ?? 1, maxCapacity: props.maxCapacity, }); } diff --git a/packages/@aws-cdk/aws-autoscaling-common/lib/interval-utils.ts b/packages/@aws-cdk/aws-autoscaling-common/lib/interval-utils.ts index 39a05beb1e70e..246bfc018507b 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/lib/interval-utils.ts +++ b/packages/@aws-cdk/aws-autoscaling-common/lib/interval-utils.ts @@ -43,7 +43,7 @@ function orderAndCompleteIntervals(intervals: ScalingInterval[]): CompleteScalin intervals = intervals.map(x => ({ ...x })); // Sort by whatever number we have for each interval - intervals.sort(comparatorFromKey((x: ScalingInterval) => x.lower !== undefined ? x.lower : x.upper)); + intervals.sort(comparatorFromKey((x: ScalingInterval) => x.lower ?? x.upper)); // Propagate boundaries until no more change while (propagateBounds(intervals)) { /* Repeat */ } diff --git a/packages/@aws-cdk/aws-autoscaling-hooktargets/lib/lambda-hook.ts b/packages/@aws-cdk/aws-autoscaling-hooktargets/lib/lambda-hook.ts index 6c3c9cd4caae2..dbe170438320e 100644 --- a/packages/@aws-cdk/aws-autoscaling-hooktargets/lib/lambda-hook.ts +++ b/packages/@aws-cdk/aws-autoscaling-hooktargets/lib/lambda-hook.ts @@ -3,9 +3,13 @@ import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as subs from '@aws-cdk/aws-sns-subscriptions'; -import { Construct } from '@aws-cdk/core'; + import { TopicHook } from './topic-hook'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Use a Lambda Function as a hook target * diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index 0d5dd3f147235..4b14a4ea710cb 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -957,9 +957,8 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements // desiredCapacity just reflects what the user has supplied. const desiredCapacity = props.desiredCapacity; - const minCapacity = props.minCapacity !== undefined ? props.minCapacity : 1; - const maxCapacity = props.maxCapacity !== undefined ? props.maxCapacity : - desiredCapacity !== undefined ? desiredCapacity : Math.max(minCapacity, 1); + const minCapacity = props.minCapacity ?? 1; + const maxCapacity = props.maxCapacity ?? desiredCapacity ?? Math.max(minCapacity, 1); withResolved(minCapacity, maxCapacity, (min, max) => { if (min > max) { @@ -1009,7 +1008,7 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements const { subnetIds, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets); const asgProps: CfnAutoScalingGroupProps = { autoScalingGroupName: this.physicalName, - cooldown: props.cooldown !== undefined ? props.cooldown.toSeconds().toString() : undefined, + cooldown: props.cooldown?.toSeconds().toString(), minSize: Tokenization.stringifyNumber(minCapacity), maxSize: Tokenization.stringifyNumber(maxCapacity), desiredCapacity: desiredCapacity !== undefined ? Tokenization.stringifyNumber(desiredCapacity) : undefined, @@ -1509,7 +1508,7 @@ enum HealthCheckType { * Render the rolling update configuration into the appropriate object */ function renderRollingUpdateConfig(config: RollingUpdateConfiguration = {}): CfnAutoScalingRollingUpdate { - const waitOnResourceSignals = config.minSuccessfulInstancesPercent !== undefined ? true : false; + const waitOnResourceSignals = config.minSuccessfulInstancesPercent !== undefined; const pauseTime = config.pauseTime || (waitOnResourceSignals ? Duration.minutes(5) : Duration.seconds(0)); return { diff --git a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook-target.ts b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook-target.ts index b72a3245eb4d5..e15ae3ef081b5 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook-target.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/lifecycle-hook-target.ts @@ -1,6 +1,10 @@ -import { Construct } from '@aws-cdk/core'; + import { ILifecycleHook } from './lifecycle-hook'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Interface for autoscaling lifecycle hook targets */ diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts index 9b9939d740d16..35b397e81dd04 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-action.ts @@ -1,8 +1,12 @@ -import { Construct as CoreConstruct, Duration, Lazy } from '@aws-cdk/core'; +import { Duration, Lazy } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { IAutoScalingGroup } from './auto-scaling-group'; import { CfnScalingPolicy } from './autoscaling.generated'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Properties for a scaling policy */ diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts index a3a417bd126d5..c3b51a892c222 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts @@ -1,10 +1,14 @@ import { findAlarmThresholds, normalizeIntervals } from '@aws-cdk/aws-autoscaling-common'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Construct as CoreConstruct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { IAutoScalingGroup } from './auto-scaling-group'; import { AdjustmentType, MetricAggregationType, StepScalingAction } from './step-scaling-action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + export interface BasicStepScalingPolicyProps { /** * Metric to scale on. diff --git a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts index 5631016066761..9a89faef3b045 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts @@ -1,9 +1,13 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Construct as CoreConstruct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { IAutoScalingGroup } from './auto-scaling-group'; import { CfnScalingPolicy } from './autoscaling.generated'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Base interface for target tracking props * diff --git a/packages/@aws-cdk/aws-batch/lib/compute-environment.ts b/packages/@aws-cdk/aws-batch/lib/compute-environment.ts index 96c356ac13d50..18a2d1a446325 100644 --- a/packages/@aws-cdk/aws-batch/lib/compute-environment.ts +++ b/packages/@aws-cdk/aws-batch/lib/compute-environment.ts @@ -374,7 +374,7 @@ export class ComputeEnvironment extends Resource implements IComputeEnvironment minvCpus: props.computeResources.minvCpus || 0, placementGroup: props.computeResources.placementGroup, securityGroupIds: this.buildSecurityGroupIds(props.computeResources.vpc, props.computeResources.securityGroups), - spotIamFleetRole: spotFleetRole ? spotFleetRole.roleArn : undefined, + spotIamFleetRole: spotFleetRole?.roleArn, subnets: props.computeResources.vpc.selectSubnets(props.computeResources.vpcSubnets).subnetIds, tags: props.computeResources.computeResourcesTags, type: props.computeResources.type || ComputeResourceType.ON_DEMAND, @@ -384,9 +384,8 @@ export class ComputeEnvironment extends Resource implements IComputeEnvironment const computeEnvironment = new CfnComputeEnvironment(this, 'Resource', { computeEnvironmentName: this.physicalName, computeResources, - serviceRole: props.serviceRole - ? props.serviceRole.roleArn - : new iam.Role(this, 'Resource-Service-Instance-Role', { + serviceRole: props.serviceRole?.roleArn + ?? new iam.Role(this, 'Resource-Service-Instance-Role', { managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSBatchServiceRole'), ], @@ -409,7 +408,7 @@ export class ComputeEnvironment extends Resource implements IComputeEnvironment } private isManaged(props: ComputeEnvironmentProps): boolean { - return props.managed === undefined ? true : props.managed; + return props.managed ?? true; } /** diff --git a/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts index 57268c5291bb9..c72d94598ecfc 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/nested-stack.ts @@ -71,7 +71,7 @@ export class NestedStack extends core.NestedStack { super(scope, id, { parameters: props.parameters, timeout: props.timeout, - notificationArns: props.notifications ? props.notifications.map(n => n.topicArn) : undefined, + notificationArns: props.notifications?.map(n => n.topicArn), }); } } diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.core-custom-resources.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.core-custom-resources.ts index 41029448ec1dc..1743444c1ad57 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.core-custom-resources.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.core-custom-resources.ts @@ -7,7 +7,11 @@ * - GetAtt.Attribute1: "foo" * - GetAtt.Attribute2: 1234 */ -import { App, CfnOutput, Construct, CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack, Token } from '@aws-cdk/core'; +import { App, CfnOutput, CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack, Token } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; /* eslint-disable cdk/no-core-construct */ diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts index 650609c0e6d40..b6357115f7667 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stack.ts @@ -2,9 +2,13 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as sns_subscriptions from '@aws-cdk/aws-sns-subscriptions'; import * as sqs from '@aws-cdk/aws-sqs'; -import { App, CfnParameter, Construct, Stack } from '@aws-cdk/core'; +import { App, CfnParameter, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /* eslint-disable cdk/no-core-construct */ interface MyNestedStackProps { @@ -42,7 +46,7 @@ class MyNestedStack extends cfn.NestedStack { code: lambda.Code.inline('console.error("hi")'), handler: 'index.handler', environment: { - TOPIC_ARN: props.siblingTopic ? props.siblingTopic.topicArn : '', + TOPIC_ARN: props.siblingTopic?.topicArn ?? '', QUEUE_URL: props.subscriber.queueUrl, // nested stack references a resource in the parent }, }); diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts index 4a6599c289928..ad7ed4e85d294 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-assets.ts @@ -1,9 +1,13 @@ /// !cdk-integ pragma:ignore-assets import * as path from 'path'; import * as lambda from '@aws-cdk/aws-lambda'; -import { App, Construct, Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /* eslint-disable cdk/no-core-construct */ class NestedStack extends cfn.NestedStack { diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts index 109b151138b19..67f11d0f31878 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-multi.ts @@ -1,8 +1,12 @@ /// !cdk-integ pragma:ignore-assets import * as sns from '@aws-cdk/aws-sns'; -import { App, Construct, Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /* eslint-disable cdk/no-core-construct */ class YourNestedStack extends cfn.NestedStack { diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts index b6ca8b087f7ad..1240d8d39e5b3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs1.ts @@ -5,9 +5,13 @@ /* eslint-disable cdk/no-core-construct */ import * as sns from '@aws-cdk/aws-sns'; -import { App, Construct, Stack } from '@aws-cdk/core'; +import { App, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + class ConsumerNestedStack extends cfn.NestedStack { constructor(scope: Construct, id: string, topic: sns.Topic) { super(scope, id); diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts index 5b884b209786b..712de493fa9a3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs2.ts @@ -1,8 +1,12 @@ /// !cdk-integ * import * as sns from '@aws-cdk/aws-sns'; -import { App, Construct, Fn, Stack } from '@aws-cdk/core'; +import { App, Fn, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + // non-nested non-parent stack consumes a resource from a nested stack /* eslint-disable cdk/no-core-construct */ diff --git a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts index ab1f51be91330..f44eb1b1f731a 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/integ.nested-stacks-refs3.ts @@ -1,8 +1,12 @@ /// !cdk-integ * import * as sns from '@aws-cdk/aws-sns'; -import { App, Construct, Fn, Stack } from '@aws-cdk/core'; +import { App, Fn, Stack } from '@aws-cdk/core'; import * as cfn from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + // references between siblings /* eslint-disable cdk/no-core-construct */ diff --git a/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts index 056849c1dc12b..64656faf0fca3 100644 --- a/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts +++ b/packages/@aws-cdk/aws-cloudformation/test/test.nested-stack.ts @@ -3,10 +3,14 @@ import * as path from 'path'; import { expect, haveResource, matchTemplate, SynthUtils } from '@aws-cdk/assert'; import * as s3_assets from '@aws-cdk/aws-s3-assets'; import * as sns from '@aws-cdk/aws-sns'; -import { App, CfnParameter, CfnResource, Construct, ContextProvider, LegacyStackSynthesizer, Names, Stack } from '@aws-cdk/core'; +import { App, CfnParameter, CfnResource, ContextProvider, LegacyStackSynthesizer, Names, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import { NestedStack } from '../lib/nested-stack'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /* eslint-disable cdk/no-core-construct */ /* eslint-disable max-len */ diff --git a/packages/@aws-cdk/aws-cloudfront/README.md b/packages/@aws-cdk/aws-cloudfront/README.md index 1355d7a64d31d..3a5834d98d51f 100644 --- a/packages/@aws-cdk/aws-cloudfront/README.md +++ b/packages/@aws-cdk/aws-cloudfront/README.md @@ -239,6 +239,34 @@ new cloudfront.Distribution(this, 'myDistCustomPolicy', { }); ``` +### Validating signed URLs or signed cookies with Trusted Key Groups + +CloudFront Distribution now supports validating signed URLs or signed cookies using key groups. When a cache behavior contains trusted key groups, CloudFront requires signed URLs or signed cookies for all requests that match the cache behavior. + +Example: + +```ts +// public key in PEM format +const pubKey = new PublicKey(stack, 'MyPubKey', { + encodedKey: publicKey, +}); + +const keyGroup = new KeyGroup(stack, 'MyKeyGroup', { + items: [ + pubKey, + ], +}); + +new cloudfront.Distribution(stack, 'Dist', { + defaultBehavior: { + origin: new origins.HttpOrigin('www.example.com'), + trustedKeyGroups: [ + keyGroup, + ], + }, +}); +``` + ### Lambda@Edge Lambda@Edge is an extension of AWS Lambda, a compute service that lets you execute functions that customize the content that CloudFront delivers. @@ -274,7 +302,7 @@ new cloudfront.Distribution(this, 'myDist', { > **Note:** Lambda@Edge functions must be created in the `us-east-1` region, regardless of the region of the CloudFront distribution and stack. > To make it easier to request functions for Lambda@Edge, the `EdgeFunction` construct can be used. > The `EdgeFunction` construct will automatically request a function in `us-east-1`, regardless of the region of the current stack. -> `EdgeFunction` has the same interface as `Function` and can be created and used interchangably. +> `EdgeFunction` has the same interface as `Function` and can be created and used interchangeably. > Please note that using `EdgeFunction` requires that the `us-east-1` region has been bootstrapped. > See https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html for more about bootstrapping regions. @@ -289,7 +317,7 @@ const myFunc = new lambda.Function(this, 'MyFunction', { ``` If the stack is not in `us-east-1`, and you need references from different applications on the same account, -you can also set a specific stack ID for each Lamba@Edge. +you can also set a specific stack ID for each Lambda@Edge. ```ts const myFunc1 = new cloudfront.experimental.EdgeFunction(this, 'MyFunction1', { @@ -427,7 +455,7 @@ You can customize the default certificate aliases. This is intended to be used i Example: -[create a distrubution with an default certificiate example](test/example.default-cert-alias.lit.ts) +[create a distribution with an default certificate example](test/example.default-cert-alias.lit.ts) #### ACM certificate @@ -438,7 +466,7 @@ For more information, see [the aws-certificatemanager module documentation](http Example: -[create a distrubution with an acm certificate example](test/example.acm-cert-alias.lit.ts) +[create a distribution with an acm certificate example](test/example.acm-cert-alias.lit.ts) #### IAM certificate @@ -448,7 +476,43 @@ See [Importing an SSL/TLS Certificate](https://docs.aws.amazon.com/AmazonCloudFr Example: -[create a distrubution with an iam certificate example](test/example.iam-cert-alias.lit.ts) +[create a distribution with an iam certificate example](test/example.iam-cert-alias.lit.ts) + +### Trusted Key Groups + +CloudFront Web Distributions supports validating signed URLs or signed cookies using key groups. When a cache behavior contains trusted key groups, CloudFront requires signed URLs or signed cookies for all requests that match the cache behavior. + +Example: + +```ts +const pubKey = new PublicKey(stack, 'MyPubKey', { + encodedKey: publicKey, +}); + +const keyGroup = new KeyGroup(stack, 'MyKeyGroup', { + items: [ + pubKey, + ], +}); + +new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { + originConfigs: [ + { + s3OriginSource: { + s3BucketSource: sourceBucket, + }, + behaviors: [ + { + isDefaultBehavior: true, + trustedKeyGroups: [ + keyGroup, + ], + }, + ], + }, + ], +}); +``` ### Restrictions @@ -505,7 +569,7 @@ new CloudFrontWebDistribution(stack, 'ADistribution', { }, failoverS3OriginSource: { s3BucketSource: s3.Bucket.fromBucketName(stack, 'aBucketFallback', 'myoriginbucketfallback'), - originPath: '/somwhere', + originPath: '/somewhere', originHeaders: { 'myHeader2': '21', }, diff --git a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts index 05534862026ae..02e3d092295f3 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/distribution.ts @@ -6,6 +6,7 @@ import { Construct } from 'constructs'; import { ICachePolicy } from './cache-policy'; import { CfnDistribution } from './cloudfront.generated'; import { GeoRestriction } from './geo-restriction'; +import { IKeyGroup } from './key-group'; import { IOrigin, OriginBindConfig, OriginBindOptions } from './origin'; import { IOriginRequestPolicy } from './origin-request-policy'; import { CacheBehavior } from './private/cache-behavior'; @@ -706,6 +707,14 @@ export interface AddBehaviorOptions { * @see https://aws.amazon.com/lambda/edge */ readonly edgeLambdas?: EdgeLambda[]; + + /** + * A list of Key Groups that CloudFront can use to validate signed URLs or signed cookies. + * + * @default - no KeyGroups are associated with cache behavior + * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html + */ + readonly trustedKeyGroups?: IKeyGroup[]; } /** diff --git a/packages/@aws-cdk/aws-cloudfront/lib/origin.ts b/packages/@aws-cdk/aws-cloudfront/lib/origin.ts index 0722dff17099d..6f5a42e407e01 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/origin.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/origin.ts @@ -1,6 +1,10 @@ -import { Construct, Duration, Token } from '@aws-cdk/core'; +import { Duration, Token } from '@aws-cdk/core'; import { CfnDistribution } from './cloudfront.generated'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * The failover configuration used for Origin Groups, * returned in {@link OriginBindConfig.failoverConfig}. diff --git a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts index 4b700e166cecc..d804dd8465750 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/private/cache-behavior.ts @@ -50,13 +50,12 @@ export class CacheBehavior { originRequestPolicyId: this.props.originRequestPolicy?.originRequestPolicyId, smoothStreaming: this.props.smoothStreaming, viewerProtocolPolicy: this.props.viewerProtocolPolicy ?? ViewerProtocolPolicy.ALLOW_ALL, - lambdaFunctionAssociations: this.props.edgeLambdas - ? this.props.edgeLambdas.map(edgeLambda => ({ - lambdaFunctionArn: edgeLambda.functionVersion.edgeArn, - eventType: edgeLambda.eventType.toString(), - includeBody: edgeLambda.includeBody, - })) - : undefined, + lambdaFunctionAssociations: this.props.edgeLambdas?.map(edgeLambda => ({ + lambdaFunctionArn: edgeLambda.functionVersion.edgeArn, + eventType: edgeLambda.eventType.toString(), + includeBody: edgeLambda.includeBody, + })), + trustedKeyGroups: this.props.trustedKeyGroups?.map(keyGroup => keyGroup.keyGroupId), }; } diff --git a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts index c62e1e09ed3f4..f191813dada2b 100644 --- a/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts +++ b/packages/@aws-cdk/aws-cloudfront/lib/web-distribution.ts @@ -7,6 +7,7 @@ import { Construct } from 'constructs'; import { CfnDistribution } from './cloudfront.generated'; import { HttpVersion, IDistribution, LambdaEdgeEventType, OriginProtocolPolicy, PriceClass, ViewerProtocolPolicy, SSLMethod, SecurityPolicyProtocol } from './distribution'; import { GeoRestriction } from './geo-restriction'; +import { IKeyGroup } from './key-group'; import { IOriginAccessIdentity } from './origin-access-identity'; /** @@ -347,9 +348,18 @@ export interface Behavior { * The signers are the account IDs that are allowed to sign cookies/presigned URLs for this distribution. * * If you pass a non empty value, all requests for this behavior must be signed (no public access will be allowed) + * @deprecated - We recommend using trustedKeyGroups instead of trustedSigners. */ readonly trustedSigners?: string[]; + /** + * A list of Key Groups that CloudFront can use to validate signed URLs or signed cookies. + * + * @default - no KeyGroups are associated with cache behavior + * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html + */ + readonly trustedKeyGroups?: IKeyGroup[]; + /** * * The default amount of time CloudFront will cache an object. @@ -761,10 +771,10 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu let distributionConfig: CfnDistribution.DistributionConfigProperty = { comment: props.comment, enabled: true, - defaultRootObject: props.defaultRootObject !== undefined ? props.defaultRootObject : 'index.html', + defaultRootObject: props.defaultRootObject ?? 'index.html', httpVersion: props.httpVersion || HttpVersion.HTTP2, priceClass: props.priceClass || PriceClass.PRICE_CLASS_100, - ipv6Enabled: (props.enableIpV6 !== undefined) ? props.enableIpV6 : true, + ipv6Enabled: props.enableIpV6 ?? true, // eslint-disable-next-line max-len customErrorResponses: props.errorConfigurations, // TODO: validation : https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-customerrorresponse.html#cfn-cloudfront-distribution-customerrorresponse-errorcachingminttl webAclId: props.webACLId, @@ -932,6 +942,7 @@ export class CloudFrontWebDistribution extends cdk.Resource implements IDistribu forwardedValues: input.forwardedValues || { queryString: false, cookies: { forward: 'none' } }, maxTtl: input.maxTtl && input.maxTtl.toSeconds(), minTtl: input.minTtl && input.minTtl.toSeconds(), + trustedKeyGroups: input.trustedKeyGroups?.map(key => key.keyGroupId), trustedSigners: input.trustedSigners, targetOriginId: input.targetOriginId, viewerProtocolPolicy: protoPolicy || ViewerProtocolPolicy.REDIRECT_TO_HTTPS, diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.expected.json b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.expected.json new file mode 100644 index 0000000000000..afae558dee709 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.expected.json @@ -0,0 +1,57 @@ +{ + "Resources": { + "MyPublicKey78071F3D": { + "Type": "AWS::CloudFront::PublicKey", + "Properties": { + "PublicKeyConfig": { + "CallerReference": "c8752fac3fe06fc93f3fbd12d7e0282d8967409e4d", + "EncodedKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS\nJAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa\ndlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj\n6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e\n0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD\n/3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx\nNQIDAQAB\n-----END PUBLIC KEY-----", + "Name": "integdistributionkeygroupMyPublicKeyC0F3B115" + } + } + }, + "MyKeyGroupAF22FD35": { + "Type": "AWS::CloudFront::KeyGroup", + "Properties": { + "KeyGroupConfig": { + "Items": [ + { + "Ref": "MyPublicKey78071F3D" + } + ], + "Name": "integdistributionkeygroupMyKeyGroupF179E01A" + } + } + }, + "DistB3B78991": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "integdistributionkeygroupDistOrigin1B9677703", + "TrustedKeyGroups": [ + { + "Ref": "MyKeyGroupAF22FD35" + } + ], + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "www.example.com", + "Id": "integdistributionkeygroupDistOrigin1B9677703" + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.ts b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.ts new file mode 100644 index 0000000000000..e0a124b26f904 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudfront/test/integ.distribution-key-group.ts @@ -0,0 +1,32 @@ +import * as cdk from '@aws-cdk/core'; +import * as cloudfront from '../lib'; +import { TestOrigin } from './test-origin'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'integ-distribution-key-group'); +const publicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS +JAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa +dlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj +6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e +0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD +/3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx +NQIDAQAB +-----END PUBLIC KEY-----`; + +new cloudfront.Distribution(stack, 'Dist', { + defaultBehavior: { + origin: new TestOrigin('www.example.com'), + trustedKeyGroups: [ + new cloudfront.KeyGroup(stack, 'MyKeyGroup', { + items: [ + new cloudfront.PublicKey(stack, 'MyPublicKey', { + encodedKey: publicKey, + }), + ], + }), + ], + }, +}); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts b/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts index a1e315fdcdd96..c9500b23eaddf 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/private/cache-behavior.test.ts @@ -1,11 +1,20 @@ import '@aws-cdk/assert/jest'; import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; -import { AllowedMethods, CachedMethods, CachePolicy, LambdaEdgeEventType, OriginRequestPolicy, ViewerProtocolPolicy } from '../../lib'; +import { AllowedMethods, CachedMethods, CachePolicy, KeyGroup, LambdaEdgeEventType, OriginRequestPolicy, PublicKey, ViewerProtocolPolicy } from '../../lib'; import { CacheBehavior } from '../../lib/private/cache-behavior'; let app: App; let stack: Stack; +const publicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS +JAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa +dlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj +6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e +0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD +/3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx +NQIDAQAB +-----END PUBLIC KEY-----`; beforeEach(() => { app = new App(); @@ -30,6 +39,15 @@ test('renders the minimum template with an origin and path specified', () => { test('renders with all properties specified', () => { const fnVersion = lambda.Version.fromVersionArn(stack, 'Version', 'arn:aws:lambda:testregion:111111111111:function:myTestFun:v1'); + const pubKey = new PublicKey(stack, 'MyPublicKey', { + encodedKey: publicKey, + }); + const keyGroup = new KeyGroup(stack, 'MyKeyGroup', { + items: [ + pubKey, + ], + }); + const behavior = new CacheBehavior('origin_id', { pathPattern: '*', allowedMethods: AllowedMethods.ALLOW_ALL, @@ -44,6 +62,7 @@ test('renders with all properties specified', () => { includeBody: true, functionVersion: fnVersion, }], + trustedKeyGroups: [keyGroup], }); expect(behavior._renderBehavior()).toEqual({ @@ -61,6 +80,9 @@ test('renders with all properties specified', () => { eventType: 'origin-request', includeBody: true, }], + trustedKeyGroups: [ + keyGroup.keyGroupId, + ], }); }); diff --git a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts index ea2fbf3f22295..8eea2cad5bc92 100644 --- a/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts +++ b/packages/@aws-cdk/aws-cloudfront/test/web-distribution.test.ts @@ -8,7 +8,9 @@ import { CfnDistribution, CloudFrontWebDistribution, GeoRestriction, + KeyGroup, LambdaEdgeEventType, + PublicKey, SecurityPolicyProtocol, SSLMethod, ViewerCertificate, @@ -17,6 +19,16 @@ import { /* eslint-disable quote-props */ +const publicKey = `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudf8/iNkQgdvjEdm6xYS +JAyxd/kGTbJfQNg9YhInb7TSm0dGu0yx8yZ3fnpmxuRPqJIlaVr+fT4YRl71gEYa +dlhHmnVegyPNjP9dNqZ7zwNqMEPOPnS/NOHbJj1KYKpn1f8pPNycQ5MQCntKGnSj +6fc+nbcC0joDvGz80xuy1W4hLV9oC9c3GT26xfZb2jy9MVtA3cppNuTwqrFi3t6e +0iGpraxZlT5wewjZLpQkngqYr6s3aucPAZVsGTEYPo4nD5mswmtZOm+tgcOrivtD +/3sD/qZLQ6c5siqyS8aTraD6y+VXugujfarTU65IeZ6QAUbLMsWuZOIi5Jn8zAwx +NQIDAQAB +-----END PUBLIC KEY-----`; + nodeunitShim({ 'distribution with custom origin adds custom origin'(test: Test) { @@ -188,6 +200,14 @@ nodeunitShim({ 'distribution with trusted signers on default distribution'(test: Test) { const stack = new cdk.Stack(); const sourceBucket = new s3.Bucket(stack, 'Bucket'); + const pubKey = new PublicKey(stack, 'MyPubKey', { + encodedKey: publicKey, + }); + const keyGroup = new KeyGroup(stack, 'MyKeyGroup', { + items: [ + pubKey, + ], + }); new CloudFrontWebDistribution(stack, 'AnAmazingWebsiteProbably', { originConfigs: [ @@ -199,6 +219,9 @@ nodeunitShim({ { isDefaultBehavior: true, trustedSigners: ['1234'], + trustedKeyGroups: [ + keyGroup, + ], }, ], }, @@ -212,6 +235,29 @@ nodeunitShim({ 'DeletionPolicy': 'Retain', 'UpdateReplacePolicy': 'Retain', }, + 'MyPubKey6ADA4CF5': { + 'Type': 'AWS::CloudFront::PublicKey', + 'Properties': { + 'PublicKeyConfig': { + 'CallerReference': 'c8141e732ea37b19375d4cbef2b2d2c6f613f0649a', + 'EncodedKey': publicKey, + 'Name': 'MyPubKey', + }, + }, + }, + 'MyKeyGroupAF22FD35': { + 'Type': 'AWS::CloudFront::KeyGroup', + 'Properties': { + 'KeyGroupConfig': { + 'Items': [ + { + 'Ref': 'MyPubKey6ADA4CF5', + }, + ], + 'Name': 'MyKeyGroup', + }, + }, + }, 'AnAmazingWebsiteProbablyCFDistribution47E3983B': { 'Type': 'AWS::CloudFront::Distribution', 'Properties': { @@ -250,9 +296,12 @@ nodeunitShim({ 'QueryString': false, 'Cookies': { 'Forward': 'none' }, }, - 'TrustedSigners': [ - '1234', + 'TrustedKeyGroups': [ + { + 'Ref': 'MyKeyGroupAF22FD35', + }, ], + 'TrustedSigners': ['1234'], 'Compress': true, }, 'Enabled': true, diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm-action.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm-action.ts index 7b48d0f055873..fb7afe98d7725 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/alarm-action.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/alarm-action.ts @@ -1,6 +1,10 @@ -import { Construct } from '@aws-cdk/core'; + import { IAlarm } from './alarm-base'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Interface for objects that can be the targets of CloudWatch alarm actions */ diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index f232b5d27edf5..98f8980db4f4b 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -114,7 +114,7 @@ export class AlarmWidget extends ConcreteWidget { alarms: [this.props.alarm.alarmArn], }, yAxis: { - left: this.props.leftYAxis !== undefined ? this.props.leftYAxis : undefined, + left: this.props.leftYAxis ?? undefined, }, }, }]; @@ -271,8 +271,8 @@ export class GraphWidget extends ConcreteWidget { metrics: metrics.length > 0 ? metrics : undefined, annotations: horizontalAnnotations.length > 0 ? { horizontal: horizontalAnnotations } : undefined, yAxis: { - left: this.props.leftYAxis !== undefined ? this.props.leftYAxis : undefined, - right: this.props.rightYAxis !== undefined ? this.props.rightYAxis : undefined, + left: this.props.leftYAxis ?? undefined, + right: this.props.rightYAxis ?? undefined, }, legend: this.props.legendPosition !== undefined ? { position: this.props.legendPosition } : undefined, liveData: this.props.liveData, diff --git a/packages/@aws-cdk/aws-codebuild/lib/source.ts b/packages/@aws-cdk/aws-codebuild/lib/source.ts index 7706328a92bc7..19161ef9b6172 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/source.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/source.ts @@ -515,14 +515,14 @@ abstract class ThirdPartyGitSource extends GitSource { super(props); this.webhook = props.webhook; - this.reportBuildStatus = props.reportBuildStatus === undefined ? true : props.reportBuildStatus; + this.reportBuildStatus = props.reportBuildStatus ?? true; this.webhookFilters = props.webhookFilters || []; this.webhookTriggersBatchBuild = props.webhookTriggersBatchBuild; } public bind(_scope: CoreConstruct, project: IProject): SourceConfig { const anyFilterGroupsProvided = this.webhookFilters.length > 0; - const webhook = this.webhook === undefined ? (anyFilterGroupsProvided ? true : undefined) : this.webhook; + const webhook = this.webhook ?? (anyFilterGroupsProvided ? true : undefined); if (!webhook && anyFilterGroupsProvided) { throw new Error('`webhookFilters` cannot be used when `webhook` is `false`'); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts index 20822a0d2356b..55077fe93f273 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts @@ -99,9 +99,8 @@ export class CustomLambdaDeploymentConfig extends Resource implements ILambdaDep // Generates the name of the deployment config. It's also what you'll see in the AWS console // The name of the config is .LambdaPercentMinutes // Unless the user provides an explicit name - this.deploymentConfigName = props.deploymentConfigName !== undefined - ? props.deploymentConfigName - : `${Names.uniqueId(this)}.Lambda${props.type}${props.percentage}Percent${props.type === CustomLambdaDeploymentConfigType.LINEAR + this.deploymentConfigName = props.deploymentConfigName + ?? `${Names.uniqueId(this)}.Lambda${props.type}${props.percentage}Percent${props.type === CustomLambdaDeploymentConfigType.LINEAR ? 'Every' : ''}${props.interval.toMinutes()}Minutes`; this.deploymentConfigArn = arnForDeploymentConfig(this.deploymentConfigName); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts index 0864d603d2e28..e1108acb5205d 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-group.ts @@ -277,7 +277,7 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase { }); this._autoScalingGroups = props.autoScalingGroups || []; - this.installAgent = props.installAgent === undefined ? true : props.installAgent; + this.installAgent = props.installAgent ?? true; this.codeDeployBucket = s3.Bucket.fromBucketName(this, 'Bucket', `aws-codedeploy-${cdk.Stack.of(this).region}`); for (const asg of this._autoScalingGroups) { this.addCodeDeployAgentInstallUserData(asg); diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/action.ts index b3ce90e2aa793..c2f7ca5ca5305 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/action.ts @@ -1,6 +1,10 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as events from '@aws-cdk/aws-events'; -import { Construct, Lazy } from '@aws-cdk/core'; +import { Lazy } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; /** * Low-level class for generic CodePipeline Actions. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/alexa-ask/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/alexa-ask/deploy-action.ts index ebbe9fd060168..f5eb7c4e64579 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/alexa-ask/deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/alexa-ask/deploy-action.ts @@ -1,7 +1,11 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; -import { Construct, SecretValue } from '@aws-cdk/core'; +import { SecretValue } from '@aws-cdk/core'; import { Action } from '../action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties of the {@link AlexaSkillDeployAction Alexa deploy Action}. */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/bitbucket/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/bitbucket/source-action.ts index 75f34777471e5..7d7625ab4fca4 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/bitbucket/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/bitbucket/source-action.ts @@ -1,9 +1,13 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct } from '@aws-cdk/core'; + import { Action } from '../action'; import { sourceArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties for {@link BitBucketSourceAction}. * diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts index 2b18bb9db6071..602a168487830 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codecommit/source-action.ts @@ -2,10 +2,14 @@ import * as codecommit from '@aws-cdk/aws-codecommit'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as targets from '@aws-cdk/aws-events-targets'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Names, Token } from '@aws-cdk/core'; +import { Names, Token } from '@aws-cdk/core'; import { Action } from '../action'; import { sourceArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * How should the CodeCommit Action detect changes. * This is the type of the {@link CodeCommitSourceAction.trigger} property. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/ecs-deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/ecs-deploy-action.ts index 33bc07c148b1e..dd9ff5247e828 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/ecs-deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/ecs-deploy-action.ts @@ -1,9 +1,13 @@ import * as codedeploy from '@aws-cdk/aws-codedeploy'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Lazy } from '@aws-cdk/core'; +import { Lazy } from '@aws-cdk/core'; import { Action } from '../action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Configuration for replacing a placeholder string in the ECS task * definition template file with an image URI. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts index 07c0662824481..519a47708b74a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/codedeploy/server-deploy-action.ts @@ -1,10 +1,14 @@ import * as codedeploy from '@aws-cdk/aws-codedeploy'; import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct } from '@aws-cdk/core'; + import { Action } from '../action'; import { deployArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties of the {@link CodeDeployServerDeployAction CodeDeploy server deploy CodePipeline Action}. */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/custom-action-registration.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/custom-action-registration.ts index 41e34ac8a0b5d..dad9e84a47094 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/custom-action-registration.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/custom-action-registration.ts @@ -132,7 +132,7 @@ export class CustomActionRegistration extends Construct { entityUrlTemplate: props.entityUrl, executionUrlTemplate: props.executionUrl, }, - configurationProperties: props.actionProperties === undefined ? undefined : props.actionProperties.map((ap) => { + configurationProperties: props.actionProperties?.map((ap) => { return { key: ap.key || false, secret: ap.secret || false, diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts index f788c60d6aeea..6042d701017a5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecr/source-action.ts @@ -2,10 +2,14 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as ecr from '@aws-cdk/aws-ecr'; import * as targets from '@aws-cdk/aws-events-targets'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Names } from '@aws-cdk/core'; +import { Names } from '@aws-cdk/core'; import { Action } from '../action'; import { sourceArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * The CodePipeline variables emitted by the ECR source Action. */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecs/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecs/deploy-action.ts index ec5dfb7ad2999..22c9988ac3ff5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/ecs/deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/ecs/deploy-action.ts @@ -1,10 +1,14 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as ecs from '@aws-cdk/aws-ecs'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { Action } from '../action'; import { deployArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties of {@link EcsDeployAction}. */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/github/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/github/source-action.ts index 82ca0b033120c..96b90a0ecb19b 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/github/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/github/source-action.ts @@ -1,8 +1,12 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; -import { Construct, SecretValue } from '@aws-cdk/core'; +import { SecretValue } from '@aws-cdk/core'; import { Action } from '../action'; import { sourceArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * If and how the GitHub source action should be triggered */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/jenkins/jenkins-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/jenkins/jenkins-action.ts index 5ebb614ea4ee7..81da65206b12a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/jenkins/jenkins-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/jenkins/jenkins-action.ts @@ -1,8 +1,12 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; -import { Construct } from '@aws-cdk/core'; + import { Action } from '../action'; import { IJenkinsProvider, jenkinsArtifactsBounds } from './jenkins-provider'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * The type of the Jenkins Action that determines its CodePipeline Category - * Build, or Test. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/lambda/invoke-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/lambda/invoke-action.ts index 987968728cb4d..f8a49d977f6b4 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/lambda/invoke-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/lambda/invoke-action.ts @@ -1,9 +1,13 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { Action } from '../action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties of the {@link LambdaInvokeAction Lambda invoke CodePipeline Action}. */ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts index aebc8ba2548c8..75998456f4a39 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/deploy-action.ts @@ -1,10 +1,14 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as s3 from '@aws-cdk/aws-s3'; -import { Construct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { kebab as toKebabCase } from 'case'; import { Action } from '../action'; import { deployArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + // Class copied verbatim from the aws-s3-deployment module. // Yes, it sucks that we didn't abstract this properly in a common class, // but having 2 different CacheControl classes that behave differently would be worse I think. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/source-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/source-action.ts index 2470ea6cca25f..cb86bd2591c6a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/s3/source-action.ts @@ -1,10 +1,14 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as targets from '@aws-cdk/aws-events-targets'; import * as s3 from '@aws-cdk/aws-s3'; -import { Construct, Names, Token } from '@aws-cdk/core'; +import { Names, Token } from '@aws-cdk/core'; import { Action } from '../action'; import { sourceArtifactBounds } from '../common'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * How should the S3 Action detect changes. * This is the type of the {@link S3SourceAction.trigger} property. diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action.ts index f55766315c070..9059af1ac0ba1 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/servicecatalog/deploy-action.ts @@ -1,8 +1,12 @@ import * as codepipeline from '@aws-cdk/aws-codepipeline'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct } from '@aws-cdk/core'; + import { Action } from '../action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Construction properties of the {@link ServiceCatalogDeployAction ServiceCatalog deploy CodePipeline Action}. * diff --git a/packages/@aws-cdk/aws-codepipeline/lib/action.ts b/packages/@aws-cdk/aws-codepipeline/lib/action.ts index b2b6e79154699..6b28bcc8a3749 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/action.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/action.ts @@ -1,9 +1,13 @@ import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { Construct, IResource } from '@aws-cdk/core'; +import { IResource } from '@aws-cdk/core'; import { Artifact } from './artifact'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + export enum ActionCategory { SOURCE = 'Source', BUILD = 'Build', diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index ba7fd8d87233d..760e049c6ed73 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -3,7 +3,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as s3 from '@aws-cdk/aws-s3'; import { - App, BootstraplessSynthesizer, Construct as CoreConstruct, DefaultStackSynthesizer, + App, BootstraplessSynthesizer, DefaultStackSynthesizer, IStackSynthesizer, Lazy, Names, PhysicalName, RemovalPolicy, Resource, Stack, Token, } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -15,6 +15,10 @@ import { RichAction } from './private/rich-action'; import { Stage } from './private/stage'; import { validateName, validateNamespaceName, validateSourceAction } from './private/validation'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Allows you to control where to place a new Stage when it's added to the Pipeline. * Note that you can provide only one of the below properties - diff --git a/packages/@aws-cdk/aws-codepipeline/lib/private/full-action-descriptor.ts b/packages/@aws-cdk/aws-codepipeline/lib/private/full-action-descriptor.ts index 6d58bf815d3db..895d3cf15430c 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/private/full-action-descriptor.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/private/full-action-descriptor.ts @@ -36,13 +36,13 @@ export class FullActionDescriptor { this.owner = actionProperties.owner || 'AWS'; this.provider = actionProperties.provider; this.version = actionProperties.version || '1'; - this.runOrder = actionProperties.runOrder === undefined ? 1 : actionProperties.runOrder; + this.runOrder = actionProperties.runOrder ?? 1; this.artifactBounds = actionProperties.artifactBounds; this.namespace = actionProperties.variablesNamespace; this.inputs = deduplicateArtifacts(actionProperties.inputs); this.outputs = deduplicateArtifacts(actionProperties.outputs); this.region = props.actionRegion || actionProperties.region; - this.role = actionProperties.role !== undefined ? actionProperties.role : props.actionRole; + this.role = actionProperties.role ?? props.actionRole; this.configuration = props.actionConfig.configuration; } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/private/rich-action.ts b/packages/@aws-cdk/aws-codepipeline/lib/private/rich-action.ts index f218344da61fa..e2cde045f88a3 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/private/rich-action.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/private/rich-action.ts @@ -1,7 +1,11 @@ import * as events from '@aws-cdk/aws-events'; -import { Construct, ResourceEnvironment, Stack, Token, TokenComparison } from '@aws-cdk/core'; +import { ResourceEnvironment, Stack, Token, TokenComparison } from '@aws-cdk/core'; import { ActionBindOptions, ActionConfig, ActionProperties, IAction, IPipeline, IStage } from '../action'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Helper routines to work with Actions * diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 960d14bb8b426..d85199ee6507f 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -723,7 +723,7 @@ export class UserPool extends UserPoolBase { emailSubject: props.userInvitation?.emailSubject, smsMessage: props.userInvitation?.smsMessage, }; - const selfSignUpEnabled = props.selfSignUpEnabled !== undefined ? props.selfSignUpEnabled : false; + const selfSignUpEnabled = props.selfSignUpEnabled ?? false; const adminCreateUserConfig: CfnUserPool.AdminCreateUserConfigProperty = { allowAdminCreateUserOnly: !selfSignUpEnabled, inviteMessageTemplate: props.userInvitation !== undefined ? inviteMessageTemplate : undefined, diff --git a/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts b/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts index 45182672e45a0..718c0e693e454 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/replica-provider.ts @@ -1,10 +1,14 @@ import * as path from 'path'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct as CoreConstruct, Duration, NestedStack, Stack } from '@aws-cdk/core'; +import { Duration, NestedStack, Stack } from '@aws-cdk/core'; import * as cr from '@aws-cdk/custom-resources'; import { Construct } from 'constructs'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + export class ReplicaProvider extends NestedStack { /** * Creates a stack-singleton resource provider nested stack. diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 792f68f4cca3e..1b12eef42de1f 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -3,7 +3,7 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import { - Aws, CfnCondition, CfnCustomResource, Construct as CoreConstruct, CustomResource, Fn, + Aws, CfnCondition, CfnCustomResource, CustomResource, Fn, IResource, Lazy, Names, RemovalPolicy, Resource, Stack, Token, } from '@aws-cdk/core'; import { Construct } from 'constructs'; @@ -14,6 +14,10 @@ import { ReplicaProvider } from './replica-provider'; import { EnableScalingProps, IScalableTableAttribute } from './scalable-attribute-api'; import { ScalableTableAttribute } from './scalable-table-attribute'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + const HASH_KEY_TYPE = 'HASH'; const RANGE_KEY_TYPE = 'RANGE'; @@ -1387,8 +1391,8 @@ export class Table extends TableBase { } return { - projectionType: props.projectionType ? props.projectionType : ProjectionType.ALL, - nonKeyAttributes: props.nonKeyAttributes ? props.nonKeyAttributes : undefined, + projectionType: props.projectionType ?? ProjectionType.ALL, + nonKeyAttributes: props.nonKeyAttributes ?? undefined, }; } diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index 6ab25bfb78b3f..3b902e5cb8c75 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -538,6 +538,9 @@ examples of things you might want to use: > `cdk.context.json`, or use the `cdk context` command. For more information, see > [Runtime Context](https://docs.aws.amazon.com/cdk/latest/guide/context.html) in the CDK > developer guide. +> +> `MachineImage.genericLinux()`, `MachineImage.genericWindows()` will use `CfnMapping` in +> an agnostic stack. ## Special VPC configurations @@ -999,3 +1002,24 @@ const subnet = Subnet.fromSubnetAttributes(this, 'SubnetFromAttributes', { // Supply only subnet id const subnet = Subnet.fromSubnetId(this, 'SubnetFromId', 's-1234'); ``` + +## Launch Templates + +A Launch Template is a standardized template that contains the configuration information to launch an instance. +They can be used when launching instances on their own, through Amazon EC2 Auto Scaling, EC2 Fleet, and Spot Fleet. +Launch templates enable you to store launch parameters so that you do not have to specify them every time you launch +an instance. For information on Launch Templates please see the +[official documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html). + +The following demonstrates how to create a launch template with an Amazon Machine Image, and security group. + +```ts +const vpc = new ec2.Vpc(...); +// ... +const template = new ec2.LaunchTemplate(this, 'LaunchTemplate', { + machineImage: new ec2.AmazonMachineImage(), + securityGroup: new ec2.SecurityGroup(this, 'LaunchTemplateSG', { + vpc: vpc, + }), +}); +``` diff --git a/packages/@aws-cdk/aws-ec2/lib/cfn-init.ts b/packages/@aws-cdk/aws-ec2/lib/cfn-init.ts index 324847845849b..2388c80736df4 100644 --- a/packages/@aws-cdk/aws-ec2/lib/cfn-init.ts +++ b/packages/@aws-cdk/aws-ec2/lib/cfn-init.ts @@ -1,11 +1,15 @@ import * as crypto from 'crypto'; import * as iam from '@aws-cdk/aws-iam'; -import { Aws, CfnResource, Construct } from '@aws-cdk/core'; +import { Aws, CfnResource } from '@aws-cdk/core'; import { InitElement } from './cfn-init-elements'; import { OperatingSystemType } from './machine-image'; import { InitBindOptions, InitElementConfig, InitElementType, InitPlatform } from './private/cfn-init-internal'; import { UserData } from './user-data'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * A CloudFormation-init configuration */ diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index ca25a02f3f8d1..9f70a4320060d 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -4,6 +4,7 @@ export * from './cfn-init'; export * from './cfn-init-elements'; export * from './instance-types'; export * from './instance'; +export * from './launch-template'; export * from './machine-image'; export * from './nat'; export * from './network-acl'; diff --git a/packages/@aws-cdk/aws-ec2/lib/instance.ts b/packages/@aws-cdk/aws-ec2/lib/instance.ts index 22c4fa7cf880f..82fbb22bea4e3 100644 --- a/packages/@aws-cdk/aws-ec2/lib/instance.ts +++ b/packages/@aws-cdk/aws-ec2/lib/instance.ts @@ -8,9 +8,10 @@ import { Connections, IConnectable } from './connections'; import { CfnInstance } from './ec2.generated'; import { InstanceType } from './instance-types'; import { IMachineImage, OperatingSystemType } from './machine-image'; +import { instanceBlockDeviceMappings } from './private/ebs-util'; import { ISecurityGroup, SecurityGroup } from './security-group'; import { UserData } from './user-data'; -import { BlockDevice, synthesizeBlockDeviceMappings } from './volume'; +import { BlockDevice } from './volume'; import { IVpc, Subnet, SubnetSelection } from './vpc'; /** @@ -362,7 +363,7 @@ export class Instance extends Resource implements IInstance { subnetId: subnet.subnetId, availabilityZone: subnet.availabilityZone, sourceDestCheck: props.sourceDestCheck, - blockDeviceMappings: props.blockDevices !== undefined ? synthesizeBlockDeviceMappings(this, props.blockDevices) : undefined, + blockDeviceMappings: props.blockDevices !== undefined ? instanceBlockDeviceMappings(this, props.blockDevices) : undefined, privateIpAddress: props.privateIpAddress, }); this.instance.node.addDependency(this.role); diff --git a/packages/@aws-cdk/aws-ec2/lib/launch-template.ts b/packages/@aws-cdk/aws-ec2/lib/launch-template.ts new file mode 100644 index 0000000000000..3b5b39f9b6370 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/lib/launch-template.ts @@ -0,0 +1,665 @@ +import * as iam from '@aws-cdk/aws-iam'; + +import { + Annotations, + Duration, + Expiration, + Fn, + IResource, + Lazy, + Resource, + TagManager, + TagType, + Tags, + Token, +} from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { Connections, IConnectable } from './connections'; +import { CfnLaunchTemplate } from './ec2.generated'; +import { InstanceType } from './instance-types'; +import { IMachineImage, MachineImageConfig, OperatingSystemType } from './machine-image'; +import { launchTemplateBlockDeviceMappings } from './private/ebs-util'; +import { ISecurityGroup } from './security-group'; +import { UserData } from './user-data'; +import { BlockDevice } from './volume'; + +/** + * Name tag constant + */ +const NAME_TAG: string = 'Name'; + +/** + * Provides the options for specifying the CPU credit type for burstable EC2 instance types (T2, T3, T3a, etc). + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-how-to.html + */ +// dev-note: This could be used in the Instance L2 +export enum CpuCredits { + /** + * Standard bursting mode. + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-standard-mode.html + */ + STANDARD = 'standard', + + /** + * Unlimited bursting mode. + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances-unlimited-mode.html + */ + UNLIMITED = 'unlimited', +}; + +/** + * Provides the options for specifying the instance initiated shutdown behavior. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior + */ +// dev-note: This could be used in the Instance L2 +export enum InstanceInitiatedShutdownBehavior { + /** + * The instance will stop when it initiates a shutdown. + */ + STOP = 'stop', + + /** + * The instance will be terminated when it initiates a shutdown. + */ + TERMINATE = 'terminate', +}; + +/** + * Interface for LaunchTemplate-like objects. + */ +export interface ILaunchTemplate extends IResource { + /** + * The version number of this launch template to use + * + * @attribute + */ + readonly versionNumber: string; + + /** + * The identifier of the Launch Template + * + * Exactly one of `launchTemplateId` and `launchTemplateName` will be set. + * + * @attribute + */ + readonly launchTemplateId?: string; + + /** + * The name of the Launch Template + * + * Exactly one of `launchTemplateId` and `launchTemplateName` will be set. + * + * @attribute + */ + readonly launchTemplateName?: string; +} + +/** + * Provides the options for the types of interruption for spot instances. + */ +// dev-note: This could be used in a SpotFleet L2 if one gets developed. +export enum SpotInstanceInterruption { + /** + * The instance will stop when interrupted. + */ + STOP = 'stop', + + /** + * The instance will be terminated when interrupted. + */ + TERMINATE = 'terminate', + + /** + * The instance will hibernate when interrupted. + */ + HIBERNATE = 'hibernate', +} + +/** + * The Spot Instance request type. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-requests.html + */ +export enum SpotRequestType { + /** + * A one-time Spot Instance request remains active until Amazon EC2 launches the Spot Instance, + * the request expires, or you cancel the request. If the Spot price exceeds your maximum price + * or capacity is not available, your Spot Instance is terminated and the Spot Instance request + * is closed. + */ + ONE_TIME = 'one-time', + + /** + * A persistent Spot Instance request remains active until it expires or you cancel it, even if + * the request is fulfilled. If the Spot price exceeds your maximum price or capacity is not available, + * your Spot Instance is interrupted. After your instance is interrupted, when your maximum price exceeds + * the Spot price or capacity becomes available again, the Spot Instance is started if stopped or resumed + * if hibernated. + */ + PERSISTENT = 'persistent', +} + +/** + * Interface for the Spot market instance options provided in a LaunchTemplate. + */ +export interface LaunchTemplateSpotOptions { + /** + * Spot Instances with a defined duration (also known as Spot blocks) are designed not to be interrupted and will run continuously for the duration you select. + * You can use a duration of 1, 2, 3, 4, 5, or 6 hours. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-requests.html#fixed-duration-spot-instances + * + * @default Requested spot instances do not have a pre-defined duration. + */ + readonly blockDuration?: Duration; + + /** + * The behavior when a Spot Instance is interrupted. + * + * @default Spot instances will terminate when interrupted. + */ + readonly interruptionBehavior?: SpotInstanceInterruption; + + /** + * Maximum hourly price you're willing to pay for each Spot instance. The value is given + * in dollars. ex: 0.01 for 1 cent per hour, or 0.001 for one-tenth of a cent per hour. + * + * @default Maximum hourly price will default to the on-demand price for the instance type. + */ + readonly maxPrice?: number; + + /** + * The Spot Instance request type. + * + * If you are using Spot Instances with an Auto Scaling group, use one-time requests, as the + * Amazon EC2 Auto Scaling service handles requesting new Spot Instances whenever the group is + * below its desired capacity. + * + * @default One-time spot request. + */ + readonly requestType?: SpotRequestType; + + /** + * The end date of the request. For a one-time request, the request remains active until all instances + * launch, the request is canceled, or this date is reached. If the request is persistent, it remains + * active until it is canceled or this date and time is reached. + * + * @default The default end date is 7 days from the current date. + */ + readonly validUntil?: Expiration; +}; + +/** + * Properties of a LaunchTemplate. + */ +export interface LaunchTemplateProps { + /** + * Name for this launch template. + * + * @default Automatically generated name + */ + readonly launchTemplateName?: string; + + /** + * Type of instance to launch. + * + * @default - This Launch Template does not specify a default Instance Type. + */ + readonly instanceType?: InstanceType; + + /** + * The AMI that will be used by instances. + * + * @default - This Launch Template does not specify a default AMI. + */ + readonly machineImage?: IMachineImage; + + /** + * The AMI that will be used by instances. + * + * @default - This Launch Template creates a UserData based on the type of provided + * machineImage; no UserData is created if a machineImage is not provided + */ + readonly userData?: UserData; + + /** + * An IAM role to associate with the instance profile that is used by instances. + * + * The role must be assumable by the service principal `ec2.amazonaws.com`: + * + * @example + * const role = new iam.Role(this, 'MyRole', { + * assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com') + * }); + * + * @default - No new role is created. + */ + readonly role?: iam.IRole; + + /** + * Specifies how block devices are exposed to the instance. You can specify virtual devices and EBS volumes. + * + * Each instance that is launched has an associated root device volume, + * either an Amazon EBS volume or an instance store volume. + * You can use block device mappings to specify additional EBS volumes or + * instance store volumes to attach to an instance when it is launched. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html + * + * @default - Uses the block device mapping of the AMI + */ + readonly blockDevices?: BlockDevice[]; + + /** + * CPU credit type for burstable EC2 instance types. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances.html + * + * @default - No credit type is specified in the Launch Template. + */ + readonly cpuCredits?: CpuCredits; + + /** + * If you set this parameter to true, you cannot terminate the instances launched with this launch template + * using the Amazon EC2 console, CLI, or API; otherwise, you can. + * + * @default - The API termination setting is not specified in the Launch Template. + */ + readonly disableApiTermination?: boolean; + + /** + * Indicates whether the instances are optimized for Amazon EBS I/O. This optimization provides dedicated throughput + * to Amazon EBS and an optimized configuration stack to provide optimal Amazon EBS I/O performance. This optimization + * isn't available with all instance types. Additional usage charges apply when using an EBS-optimized instance. + * + * @default - EBS optimization is not specified in the launch template. + */ + readonly ebsOptimized?: boolean; + + /** + * If this parameter is set to true, the instance is enabled for AWS Nitro Enclaves; otherwise, it is not enabled for AWS Nitro Enclaves. + * + * @default - Enablement of Nitro enclaves is not specified in the launch template; defaulting to false. + */ + readonly nitroEnclaveEnabled?: boolean; + + /** + * If you set this parameter to true, the instance is enabled for hibernation. + * + * @default - Hibernation configuration is not specified in the launch template; defaulting to false. + */ + readonly hibernationConfigured?: boolean; + + /** + * Indicates whether an instance stops or terminates when you initiate shutdown from the instance (using the operating system command for system shutdown). + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior + * + * @default - Shutdown behavior is not specified in the launch template; defaults to STOP. + */ + readonly instanceInitiatedShutdownBehavior?: InstanceInitiatedShutdownBehavior; + + /** + * If this property is defined, then the Launch Template's InstanceMarketOptions will be + * set to use Spot instances, and the options for the Spot instances will be as defined. + * + * @default - Instance launched with this template will not be spot instances. + */ + readonly spotOptions?: LaunchTemplateSpotOptions; + + /** + * Name of SSH keypair to grant access to instance + * + * @default - No SSH access will be possible. + */ + readonly keyName?: string; + + /** + * If set to true, then detailed monitoring will be enabled on instances created with this + * launch template. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-cloudwatch-new.html + * + * @default False - Detailed monitoring is disabled. + */ + readonly detailedMonitoring?: boolean; + + /** + * Security group to assign to instances created with the launch template. + * + * @default No security group is assigned. + */ + readonly securityGroup?: ISecurityGroup; +} + +/** + * A class that provides convenient access to special version tokens for LaunchTemplate + * versions. + */ +export class LaunchTemplateSpecialVersions { + /** + * The special value that denotes that users of a Launch Template should + * reference the LATEST version of the template. + */ + public static readonly LATEST_VERSION: string = '$Latest'; + + /** + * The special value that denotes that users of a Launch Template should + * reference the DEFAULT version of the template. + */ + public static readonly DEFAULT_VERSION: string = '$Default'; +} + +/** + * Attributes for an imported LaunchTemplate. + */ +export interface LaunchTemplateAttributes { + /** + * The version number of this launch template to use + * + * @default Version: "$Default" + */ + readonly versionNumber?: string; + + /** + * The identifier of the Launch Template + * + * Exactly one of `launchTemplateId` and `launchTemplateName` may be set. + * + * @default None + */ + readonly launchTemplateId?: string; + + /** + * The name of the Launch Template + * + * Exactly one of `launchTemplateId` and `launchTemplateName` may be set. + * + * @default None + */ + readonly launchTemplateName?: string; +} + +/** + * This represents an EC2 LaunchTemplate. + * + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html + */ +export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGrantable, IConnectable { + /** + * Import an existing LaunchTemplate. + */ + public static fromLaunchTemplateAttributes(scope: Construct, id: string, attrs: LaunchTemplateAttributes): ILaunchTemplate { + const haveId = Boolean(attrs.launchTemplateId); + const haveName = Boolean(attrs.launchTemplateName); + if (haveId == haveName) { + throw new Error('LaunchTemplate.fromLaunchTemplateAttributes() requires exactly one of launchTemplateId or launchTemplateName be provided.'); + } + + class Import extends Resource implements ILaunchTemplate { + public readonly versionNumber = attrs.versionNumber ?? LaunchTemplateSpecialVersions.DEFAULT_VERSION; + public readonly launchTemplateId? = attrs.launchTemplateId; + public readonly launchTemplateName? = attrs.launchTemplateName; + } + return new Import(scope, id); + } + + // ============================================ + // Members for ILaunchTemplate interface + + public readonly versionNumber: string; + public readonly launchTemplateId?: string; + public readonly launchTemplateName?: string; + + // ============================================= + // Data members + + /** + * The default version for the launch template. + * + * @attribute + */ + public readonly defaultVersionNumber: string; + + /** + * The latest version of the launch template. + * + * @attribute + */ + public readonly latestVersionNumber: string; + + /** + * The type of OS the instance is running. + * + * @attribute + */ + public readonly osType?: OperatingSystemType; + + /** + * IAM Role assumed by instances that are launched from this template. + * + * @attribute + */ + public readonly role?: iam.IRole; + + /** + * UserData executed by instances that are launched from this template. + * + * @attribute + */ + public readonly userData?: UserData; + + // ============================================= + // Private/protected data members + + /** + * Principal to grant permissions to. + * @internal + */ + protected readonly _grantPrincipal?: iam.IPrincipal; + + /** + * Allows specifying security group connections for the instance. + * @internal + */ + protected readonly _connections?: Connections; + + /** + * TagManager for tagging support. + */ + protected readonly tags: TagManager; + + // ============================================= + + constructor(scope: Construct, id: string, props: LaunchTemplateProps = {}) { + super(scope, id); + + // Basic validation of the provided spot block duration + const spotDuration = props?.spotOptions?.blockDuration?.toHours({ integral: true }); + if (spotDuration !== undefined && (spotDuration < 1 || spotDuration > 6)) { + // See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-requests.html#fixed-duration-spot-instances + Annotations.of(this).addError('Spot block duration must be exactly 1, 2, 3, 4, 5, or 6 hours.'); + } + + this.role = props.role; + this._grantPrincipal = this.role; + const iamProfile: iam.CfnInstanceProfile | undefined = this.role ? new iam.CfnInstanceProfile(this, 'Profile', { + roles: [this.role!.roleName], + }) : undefined; + + if (props.securityGroup) { + this._connections = new Connections({ securityGroups: [props.securityGroup] }); + } + const securityGroupsToken = Lazy.list({ + produce: () => { + if (this._connections && this._connections.securityGroups.length > 0) { + return this._connections.securityGroups.map(sg => sg.securityGroupId); + } + return undefined; + }, + }); + + if (props.userData) { + this.userData = props.userData; + } + const userDataToken = Lazy.string({ + produce: () => { + if (this.userData) { + return Fn.base64(this.userData.render()); + } + return undefined; + }, + }); + + const imageConfig: MachineImageConfig | undefined = props.machineImage?.getImage(this); + if (imageConfig) { + this.osType = imageConfig.osType; + } + + let marketOptions: any = undefined; + if (props?.spotOptions) { + marketOptions = { + marketType: 'spot', + spotOptions: { + blockDurationMinutes: spotDuration !== undefined ? spotDuration * 60 : undefined, + instanceInterruptionBehavior: props.spotOptions.interruptionBehavior, + maxPrice: props.spotOptions.maxPrice?.toString(), + spotInstanceType: props.spotOptions.requestType, + validUntil: props.spotOptions.validUntil?.date.toUTCString(), + }, + }; + // Remove SpotOptions if there are none. + if (Object.keys(marketOptions.spotOptions).filter(k => marketOptions.spotOptions[k]).length == 0) { + marketOptions.spotOptions = undefined; + } + } + + this.tags = new TagManager(TagType.KEY_VALUE, 'AWS::EC2::LaunchTemplate'); + const tagsToken = Lazy.any({ + produce: () => { + if (this.tags.hasTags()) { + const renderedTags = this.tags.renderTags(); + const lowerCaseRenderedTags = renderedTags.map( (tag: { [key: string]: string}) => { + return { + key: tag.Key, + value: tag.Value, + }; + }); + return [ + { + resourceType: 'instance', + tags: lowerCaseRenderedTags, + }, + { + resourceType: 'volume', + tags: lowerCaseRenderedTags, + }, + ]; + } + return undefined; + }, + }); + + const resource = new CfnLaunchTemplate(this, 'Resource', { + launchTemplateName: props?.launchTemplateName, + launchTemplateData: { + blockDeviceMappings: props?.blockDevices !== undefined ? launchTemplateBlockDeviceMappings(this, props.blockDevices) : undefined, + creditSpecification: props?.cpuCredits !== undefined ? { + cpuCredits: props.cpuCredits, + } : undefined, + disableApiTermination: props?.disableApiTermination, + ebsOptimized: props?.ebsOptimized, + enclaveOptions: props?.nitroEnclaveEnabled !== undefined ? { + enabled: props.nitroEnclaveEnabled, + } : undefined, + hibernationOptions: props?.hibernationConfigured !== undefined ? { + configured: props.hibernationConfigured, + } : undefined, + iamInstanceProfile: iamProfile !== undefined ? { + arn: iamProfile.getAtt('Arn').toString(), + } : undefined, + imageId: imageConfig?.imageId, + instanceType: props?.instanceType?.toString(), + instanceInitiatedShutdownBehavior: props?.instanceInitiatedShutdownBehavior, + instanceMarketOptions: marketOptions, + keyName: props?.keyName, + monitoring: props?.detailedMonitoring !== undefined ? { + enabled: props.detailedMonitoring, + } : undefined, + securityGroupIds: securityGroupsToken, + tagSpecifications: tagsToken, + userData: userDataToken, + + // Fields not yet implemented: + // ========================== + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-capacityreservationspecification.html + // Will require creating an L2 for AWS::EC2::CapacityReservation + // capacityReservationSpecification: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-cpuoptions.html + // cpuOptions: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-elasticgpuspecification.html + // elasticGpuSpecifications: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-elasticinferenceaccelerators + // elasticInferenceAccelerators: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-kernelid + // kernelId: undefined, + // ramDiskId: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-licensespecifications + // Also not implemented in Instance L2 + // licenseSpecifications: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions + // metadataOptions: undefined, + + // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-tagspecifications + // Should be implemented via the Tagging aspect in CDK core. Complication will be that this tagging interface is very unique to LaunchTemplates. + // tagSpecification: undefined + + // CDK has no abstraction for Network Interfaces yet. + // networkInterfaces: undefined, + + // CDK has no abstraction for Placement yet. + // placement: undefined, + + }, + }); + + Tags.of(this).add(NAME_TAG, this.node.path); + + this.defaultVersionNumber = resource.attrDefaultVersionNumber; + this.latestVersionNumber = resource.attrLatestVersionNumber; + this.launchTemplateId = resource.ref; + this.versionNumber = Token.asString(resource.getAtt('LatestVersionNumber')); + } + + /** + * Allows specifying security group connections for the instance. + * + * @note Only available if you provide a securityGroup when constructing the LaunchTemplate. + */ + public get connections(): Connections { + if (!this._connections) { + throw new Error('LaunchTemplate can only be used as IConnectable if a securityGroup is provided when contructing it.'); + } + return this._connections; + } + + /** + * Principal to grant permissions to. + * + * @note Only available if you provide a role when constructing the LaunchTemplate. + */ + public get grantPrincipal(): iam.IPrincipal { + if (!this._grantPrincipal) { + throw new Error('LaunchTemplate can only be used as IGrantable if a role is provided when constructing it.'); + } + return this._grantPrincipal; + } +} diff --git a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts index 34405181774a1..df4a1eece07e0 100644 --- a/packages/@aws-cdk/aws-ec2/lib/machine-image.ts +++ b/packages/@aws-cdk/aws-ec2/lib/machine-image.ts @@ -1,10 +1,14 @@ import * as ssm from '@aws-cdk/aws-ssm'; import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { Construct, ContextProvider, Stack, Token } from '@aws-cdk/core'; +import { ContextProvider, CfnMapping, Aws, Stack, Token } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { UserData } from './user-data'; import { WindowsVersion } from './windows-versions'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Interface for classes that can select an appropriate machine image to use */ @@ -363,24 +367,33 @@ export interface GenericWindowsImageProps { * manually specify an AMI map. */ export class GenericLinuxImage implements IMachineImage { - constructor(private readonly amiMap: {[region: string]: string}, private readonly props: GenericLinuxImageProps = {}) { + constructor(private readonly amiMap: { [region: string]: string }, private readonly props: GenericLinuxImageProps = {}) { } public getImage(scope: Construct): MachineImageConfig { + const userData = this.props.userData ?? UserData.forLinux(); + const osType = OperatingSystemType.LINUX; const region = Stack.of(scope).region; if (Token.isUnresolved(region)) { - throw new Error('Unable to determine AMI from AMI map since stack is region-agnostic'); + const mapping: { [k1: string]: { [k2: string]: any } } = {}; + for (const [rgn, ami] of Object.entries(this.amiMap)) { + mapping[rgn] = { ami }; + } + const amiMap = new CfnMapping(scope, 'AmiMap', { mapping }); + return { + imageId: amiMap.findInMap(Aws.REGION, 'ami'), + userData, + osType, + }; } - - const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345'; - if (!ami) { + const imageId = region !== 'test-region' ? this.amiMap[region] : 'ami-12345'; + if (!imageId) { throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`); } - return { - imageId: ami, - userData: this.props.userData ?? UserData.forLinux(), - osType: OperatingSystemType.LINUX, + imageId, + userData, + osType, }; } } @@ -395,20 +408,29 @@ export class GenericWindowsImage implements IMachineImage { } public getImage(scope: Construct): MachineImageConfig { + const userData = this.props.userData ?? UserData.forWindows(); + const osType = OperatingSystemType.WINDOWS; const region = Stack.of(scope).region; if (Token.isUnresolved(region)) { - throw new Error('Unable to determine AMI from AMI map since stack is region-agnostic'); + const mapping: { [k1: string]: { [k2: string]: any } } = {}; + for (const [rgn, ami] of Object.entries(this.amiMap)) { + mapping[rgn] = { ami }; + } + const amiMap = new CfnMapping(scope, 'AmiMap', { mapping }); + return { + imageId: amiMap.findInMap(Aws.REGION, 'ami'), + userData, + osType, + }; } - - const ami = region !== 'test-region' ? this.amiMap[region] : 'ami-12345'; - if (!ami) { + const imageId = region !== 'test-region' ? this.amiMap[region] : 'ami-12345'; + if (!imageId) { throw new Error(`Unable to find AMI in AMI map: no AMI specified for region '${region}'`); } - return { - imageId: ami, - userData: this.props.userData ?? UserData.forWindows(), - osType: OperatingSystemType.WINDOWS, + imageId, + userData, + osType, }; } } diff --git a/packages/@aws-cdk/aws-ec2/lib/network-acl.ts b/packages/@aws-cdk/aws-ec2/lib/network-acl.ts index bea1b030d2c9a..911948e8df39c 100644 --- a/packages/@aws-cdk/aws-ec2/lib/network-acl.ts +++ b/packages/@aws-cdk/aws-ec2/lib/network-acl.ts @@ -277,7 +277,7 @@ export class NetworkAclEntry extends NetworkAclEntryBase { new CfnNetworkAclEntry(this, 'Resource', { networkAclId: this.networkAcl.networkAclId, ruleNumber: props.ruleNumber, - ruleAction: props.ruleAction !== undefined ? props.ruleAction : Action.ALLOW, + ruleAction: props.ruleAction ?? Action.ALLOW, egress: props.direction !== undefined ? props.direction === TrafficDirection.EGRESS : undefined, ...props.traffic.toTrafficConfig(), ...props.cidr.toCidrConfig(), diff --git a/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts b/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts new file mode 100644 index 0000000000000..dc91f6d795011 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/lib/private/ebs-util.ts @@ -0,0 +1,42 @@ +import { Annotations } from '@aws-cdk/core'; +import { CfnInstance, CfnLaunchTemplate } from '../ec2.generated'; +import { BlockDevice, EbsDeviceVolumeType } from '../volume'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +export function instanceBlockDeviceMappings(construct: Construct, blockDevices: BlockDevice[]): CfnInstance.BlockDeviceMappingProperty[] { + return synthesizeBlockDeviceMappings(construct, blockDevices, {}); +} + +export function launchTemplateBlockDeviceMappings(construct: Construct, blockDevices: BlockDevice[]): CfnLaunchTemplate.BlockDeviceMappingProperty[] { + return synthesizeBlockDeviceMappings(construct, blockDevices, ''); +} + +/** + * Synthesize an array of block device mappings from a list of block device + * + * @param construct the instance/asg construct, used to host any warning + * @param blockDevices list of block devices + */ +function synthesizeBlockDeviceMappings(construct: Construct, blockDevices: BlockDevice[], noDeviceValue: NDT): RT[] { + return blockDevices.map(({ deviceName, volume, mappingEnabled }): RT => { + const { virtualName, ebsDevice: ebs } = volume; + + if (ebs) { + const { iops, volumeType } = ebs; + + if (!iops) { + if (volumeType === EbsDeviceVolumeType.IO1) { + throw new Error('iops property is required with volumeType: EbsDeviceVolumeType.IO1'); + } + } else if (volumeType !== EbsDeviceVolumeType.IO1) { + Annotations.of(construct).addWarning('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1'); + } + } + + const noDevice = mappingEnabled === false ? noDeviceValue : undefined; + return { deviceName, ebs, virtualName, noDevice } as any; + }); +} diff --git a/packages/@aws-cdk/aws-ec2/lib/user-data.ts b/packages/@aws-cdk/aws-ec2/lib/user-data.ts index 21727212948c0..20061bd609636 100644 --- a/packages/@aws-cdk/aws-ec2/lib/user-data.ts +++ b/packages/@aws-cdk/aws-ec2/lib/user-data.ts @@ -147,7 +147,7 @@ class LinuxUserData extends UserData { } public render(): string { - const shebang = this.props.shebang !== undefined ? this.props.shebang : '#!/bin/bash'; + const shebang = this.props.shebang ?? '#!/bin/bash'; return [shebang, ...(this.renderOnExitLines()), ...this.lines].join('\n'); } diff --git a/packages/@aws-cdk/aws-ec2/lib/volume.ts b/packages/@aws-cdk/aws-ec2/lib/volume.ts index 65d84b30eed55..36fb86faa2bac 100644 --- a/packages/@aws-cdk/aws-ec2/lib/volume.ts +++ b/packages/@aws-cdk/aws-ec2/lib/volume.ts @@ -2,9 +2,9 @@ import * as crypto from 'crypto'; import { AccountRootPrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; import { IKey, ViaServicePrincipal } from '@aws-cdk/aws-kms'; -import { Annotations, IResource, Resource, Size, SizeRoundingBehavior, Stack, Token, Tags, Names } from '@aws-cdk/core'; +import { IResource, Resource, Size, SizeRoundingBehavior, Stack, Token, Tags, Names } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnInstance, CfnVolume } from './ec2.generated'; +import { CfnVolume } from './ec2.generated'; import { IInstance } from './instance'; // v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch. @@ -164,37 +164,6 @@ export class BlockDeviceVolume { } } -/** - * Synthesize an array of block device mappings from a list of block device - * - * @param construct the instance/asg construct, used to host any warning - * @param blockDevices list of block devices - */ -export function synthesizeBlockDeviceMappings(construct: Construct, blockDevices: BlockDevice[]): CfnInstance.BlockDeviceMappingProperty[] { - return blockDevices.map(({ deviceName, volume, mappingEnabled }) => { - const { virtualName, ebsDevice: ebs } = volume; - - if (ebs) { - const { iops, volumeType } = ebs; - - if (!iops) { - if (volumeType === EbsDeviceVolumeType.IO1) { - throw new Error('iops property is required with volumeType: EbsDeviceVolumeType.IO1'); - } - } else if (volumeType !== EbsDeviceVolumeType.IO1) { - Annotations.of(construct).addWarning('iops will be ignored without volumeType: EbsDeviceVolumeType.IO1'); - } - } - - return { - deviceName, - ebs, - virtualName, - noDevice: mappingEnabled === false ? {} : undefined, - }; - }); -} - /** * Supported EBS volume types for blockDevices */ diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts index d17e56aab202c..87f67391f8c9b 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc-endpoint-service.ts @@ -86,8 +86,8 @@ export class VpcEndpointService extends Resource implements IVpcEndpointService } this.vpcEndpointServiceLoadBalancers = props.vpcEndpointServiceLoadBalancers; - this.acceptanceRequired = props.acceptanceRequired !== undefined ? props.acceptanceRequired : true; - this.whitelistedPrincipals = props.whitelistedPrincipals !== undefined ? props.whitelistedPrincipals : []; + this.acceptanceRequired = props.acceptanceRequired ?? true; + this.whitelistedPrincipals = props.whitelistedPrincipals ?? []; this.endpointService = new CfnVPCEndpointService(this, id, { networkLoadBalancerArns: this.vpcEndpointServiceLoadBalancers.map(lb => lb.loadBalancerArn), @@ -98,8 +98,7 @@ export class VpcEndpointService extends Resource implements IVpcEndpointService const { region } = Stack.of(this); const serviceNamePrefix = !Token.isUnresolved(region) ? - RegionInfo.get(region).vpcEndpointServiceNamePrefix ?? - Default.VPC_ENDPOINT_SERVICE_NAME_PREFIX : + (RegionInfo.get(region).vpcEndpointServiceNamePrefix ?? Default.VPC_ENDPOINT_SERVICE_NAME_PREFIX) : Default.VPC_ENDPOINT_SERVICE_NAME_PREFIX; this.vpcEndpointServiceName = Fn.join('.', [serviceNamePrefix, Aws.REGION, this.vpcEndpointServiceId]); diff --git a/packages/@aws-cdk/aws-ec2/lib/vpc.ts b/packages/@aws-cdk/aws-ec2/lib/vpc.ts index d23433b2637d8..2a4e524fc5150 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpc.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpc.ts @@ -1213,7 +1213,7 @@ export class Vpc extends VpcBase { this.availabilityZones = stack.availabilityZones; - const maxAZs = props.maxAzs !== undefined ? props.maxAzs : 3; + const maxAZs = props.maxAzs ?? 3; this.availabilityZones = this.availabilityZones.slice(0, maxAZs); this.vpcId = this.resource.ref; @@ -1788,7 +1788,7 @@ export class PrivateSubnet extends Subnet implements IPrivateSubnet { } function ifUndefined(value: T | undefined, defaultValue: T): T { - return value !== undefined ? value : defaultValue; + return value ?? defaultValue; } class ImportedVpc extends VpcBase { @@ -1886,6 +1886,7 @@ class LookedUpVpc extends VpcBase { availabilityZone: vpcSubnet.availabilityZone, subnetId: vpcSubnet.subnetId, routeTableId: vpcSubnet.routeTableId, + ipv4CidrBlock: vpcSubnet.cidr, })); } return ret; diff --git a/packages/@aws-cdk/aws-ec2/lib/vpn.ts b/packages/@aws-cdk/aws-ec2/lib/vpn.ts index 4683180d2af84..19fb4f963acd3 100644 --- a/packages/@aws-cdk/aws-ec2/lib/vpn.ts +++ b/packages/@aws-cdk/aws-ec2/lib/vpn.ts @@ -224,7 +224,7 @@ export class VpnConnection extends Resource implements IVpnConnection { }); } - if (!net.isIPv4(props.ip)) { + if (!Token.isUnresolved(props.ip) && !net.isIPv4(props.ip)) { throw new Error(`The \`ip\` ${props.ip} is not a valid IPv4 address.`); } diff --git a/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts b/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts new file mode 100644 index 0000000000000..882cd69ed282f --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/test/launch-template.test.ts @@ -0,0 +1,685 @@ +import { + countResources, + expect as expectCDK, + haveResource, + haveResourceLike, + stringLike, +} from '@aws-cdk/assert'; +import { + CfnInstanceProfile, + Role, + ServicePrincipal, +} from '@aws-cdk/aws-iam'; +import { + App, + Duration, + Expiration, + Stack, + Tags, +} from '@aws-cdk/core'; +import { + AmazonLinuxImage, + BlockDevice, + BlockDeviceVolume, + CpuCredits, + EbsDeviceVolumeType, + InstanceInitiatedShutdownBehavior, + InstanceType, + LaunchTemplate, + OperatingSystemType, + SecurityGroup, + SpotInstanceInterruption, + SpotRequestType, + UserData, + Vpc, + WindowsImage, + WindowsVersion, +} from '../lib'; + +/* eslint-disable jest/expect-expect */ + +describe('LaunchTemplate', () => { + let app: App; + let stack: Stack; + + beforeEach(() => { + app = new App(); + stack = new Stack(app); + }); + + test('Empty props', () => { + // WHEN + const template = new LaunchTemplate(stack, 'Template'); + + // THEN + // Note: The following is intentionally a haveResource instead of haveResourceLike + // to ensure that only the bare minimum of properties have values when no properties + // are given to a LaunchTemplate. + expectCDK(stack).to(haveResource('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + TagSpecifications: [ + { + ResourceType: 'instance', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + ], + }, + { + ResourceType: 'volume', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + ], + }, + ], + }, + })); + expectCDK(stack).notTo(haveResource('AWS::IAM::InstanceProfile')); + expect(() => { template.grantPrincipal; }).toThrow(); + expect(() => { template.connections; }).toThrow(); + expect(template.osType).toBeUndefined(); + expect(template.role).toBeUndefined(); + expect(template.userData).toBeUndefined(); + }); + + test('Import from attributes with name', () => { + // WHEN + const template = LaunchTemplate.fromLaunchTemplateAttributes(stack, 'Template', { + launchTemplateName: 'TestName', + versionNumber: 'TestVersion', + }); + + // THEN + expect(template.launchTemplateId).toBeUndefined(); + expect(template.launchTemplateName).toBe('TestName'); + expect(template.versionNumber).toBe('TestVersion'); + }); + + test('Import from attributes with id', () => { + // WHEN + const template = LaunchTemplate.fromLaunchTemplateAttributes(stack, 'Template', { + launchTemplateId: 'TestId', + versionNumber: 'TestVersion', + }); + + // THEN + expect(template.launchTemplateId).toBe('TestId'); + expect(template.launchTemplateName).toBeUndefined(); + expect(template.versionNumber).toBe('TestVersion'); + }); + + test('Import from attributes fails with name and id', () => { + expect(() => { + LaunchTemplate.fromLaunchTemplateAttributes(stack, 'Template', { + launchTemplateName: 'TestName', + launchTemplateId: 'TestId', + versionNumber: 'TestVersion', + }); + }).toThrow(); + }); + + test('Given name', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + launchTemplateName: 'LTName', + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateName: 'LTName', + })); + }); + + test('Given instanceType', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + instanceType: new InstanceType('tt.test'), + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceType: 'tt.test', + }, + })); + }); + + test('Given machineImage (Linux)', () => { + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + machineImage: new AmazonLinuxImage(), + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + ImageId: { + Ref: stringLike('SsmParameterValueawsserviceamiamazonlinuxlatestamznami*Parameter'), + }, + }, + })); + expect(template.osType).toBe(OperatingSystemType.LINUX); + expect(template.userData).toBeUndefined(); + }); + + test('Given machineImage (Windows)', () => { + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + machineImage: new WindowsImage(WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE), + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + ImageId: { + Ref: stringLike('SsmParameterValueawsserviceamiwindowslatestWindowsServer2019EnglishFullBase*Parameter'), + }, + }, + })); + expect(template.osType).toBe(OperatingSystemType.WINDOWS); + expect(template.userData).toBeUndefined(); + }); + + test('Given userData', () => { + // GIVEN + const userData = UserData.forLinux(); + userData.addCommands('echo Test'); + + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + userData, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + UserData: { + 'Fn::Base64': '#!/bin/bash\necho Test', + }, + }, + })); + expect(template.userData).toBeDefined(); + }); + + test('Given role', () => { + // GIVEN + const role = new Role(stack, 'TestRole', { + assumedBy: new ServicePrincipal('ec2.amazonaws.com'), + }); + + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + role, + }); + + // THEN + expectCDK(stack).to(countResources('AWS::IAM::Role', 1)); + expectCDK(stack).to(haveResourceLike('AWS::IAM::InstanceProfile', { + Roles: [ + { + Ref: 'TestRole6C9272DF', + }, + ], + })); + expectCDK(stack).to(haveResource('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + IamInstanceProfile: { + Arn: stack.resolve((template.node.findChild('Profile') as CfnInstanceProfile).getAtt('Arn')), + }, + TagSpecifications: [ + { + ResourceType: 'instance', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + ], + }, + { + ResourceType: 'volume', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + ], + }, + ], + }, + })); + expect(template.role).toBeDefined(); + expect(template.grantPrincipal).toBeDefined(); + }); + + test('Given blockDeviceMapping', () => { + // GIVEN + const blockDevices: BlockDevice[] = [ + { + deviceName: 'ebs', + mappingEnabled: true, + volume: BlockDeviceVolume.ebs(15, { + deleteOnTermination: true, + encrypted: true, + volumeType: EbsDeviceVolumeType.IO1, + iops: 5000, + }), + }, { + deviceName: 'ebs-snapshot', + mappingEnabled: false, + volume: BlockDeviceVolume.ebsFromSnapshot('snapshot-id', { + volumeSize: 500, + deleteOnTermination: false, + volumeType: EbsDeviceVolumeType.SC1, + }), + }, { + deviceName: 'ephemeral', + volume: BlockDeviceVolume.ephemeral(0), + }, + ]; + + // WHEN + new LaunchTemplate(stack, 'Template', { + blockDevices, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + BlockDeviceMappings: [ + { + DeviceName: 'ebs', + Ebs: { + DeleteOnTermination: true, + Encrypted: true, + Iops: 5000, + VolumeSize: 15, + VolumeType: 'io1', + }, + }, + { + DeviceName: 'ebs-snapshot', + Ebs: { + DeleteOnTermination: false, + SnapshotId: 'snapshot-id', + VolumeSize: 500, + VolumeType: 'sc1', + }, + NoDevice: '', + }, + { + DeviceName: 'ephemeral', + VirtualName: 'ephemeral0', + }, + ], + }, + })); + }); + + test.each([ + [CpuCredits.STANDARD, 'standard'], + [CpuCredits.UNLIMITED, 'unlimited'], + ])('Given cpuCredits %p', (given: CpuCredits, expected: string) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + cpuCredits: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + CreditSpecification: { + CpuCredits: expected, + }, + }, + })); + }); + + test.each([ + [true, true], + [false, false], + ])('Given disableApiTermination %p', (given: boolean, expected: boolean) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + disableApiTermination: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + DisableApiTermination: expected, + }, + })); + }); + + test.each([ + [true, true], + [false, false], + ])('Given ebsOptimized %p', (given: boolean, expected: boolean) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + ebsOptimized: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + EbsOptimized: expected, + }, + })); + }); + + test.each([ + [true, true], + [false, false], + ])('Given nitroEnclaveEnabled %p', (given: boolean, expected: boolean) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + nitroEnclaveEnabled: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + EnclaveOptions: { + Enabled: expected, + }, + }, + })); + }); + + test.each([ + [InstanceInitiatedShutdownBehavior.STOP, 'stop'], + [InstanceInitiatedShutdownBehavior.TERMINATE, 'terminate'], + ])('Given instanceInitiatedShutdownBehavior %p', (given: InstanceInitiatedShutdownBehavior, expected: string) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + instanceInitiatedShutdownBehavior: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceInitiatedShutdownBehavior: expected, + }, + })); + }); + + test('Given keyName', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + keyName: 'TestKeyname', + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + KeyName: 'TestKeyname', + }, + })); + }); + + test.each([ + [true, true], + [false, false], + ])('Given detailedMonitoring %p', (given: boolean, expected: boolean) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + detailedMonitoring: given, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + Monitoring: { + Enabled: expected, + }, + }, + })); + }); + + test('Given securityGroup', () => { + // GIVEN + const vpc = new Vpc(stack, 'VPC'); + const sg = new SecurityGroup(stack, 'SG', { vpc }); + + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + securityGroup: sg, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + SecurityGroupIds: [ + { + 'Fn::GetAtt': [ + 'SGADB53937', + 'GroupId', + ], + }, + ], + }, + })); + expect(template.connections).toBeDefined(); + expect(template.connections.securityGroups).toHaveLength(1); + expect(template.connections.securityGroups[0]).toBe(sg); + }); + + test('Adding tags', () => { + // GIVEN + const template = new LaunchTemplate(stack, 'Template'); + + // WHEN + Tags.of(template).add('TestKey', 'TestValue'); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + TagSpecifications: [ + { + ResourceType: 'instance', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + { + Key: 'TestKey', + Value: 'TestValue', + }, + ], + }, + { + ResourceType: 'volume', + Tags: [ + { + Key: 'Name', + Value: 'Default/Template', + }, + { + Key: 'TestKey', + Value: 'TestValue', + }, + ], + }, + ], + }, + })); + }); +}); + +describe('LaunchTemplate marketOptions', () => { + let app: App; + let stack: Stack; + + beforeEach(() => { + app = new App(); + stack = new Stack(app); + }); + + test('given spotOptions', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: {}, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + }, + }, + })); + }); + + test.each([ + [0, 1], + [1, 0], + [6, 0], + [7, 1], + ])('for range duration errors: %p', (duration: number, expectedErrors: number) => { + // WHEN + const template = new LaunchTemplate(stack, 'Template', { + spotOptions: { + blockDuration: Duration.hours(duration), + }, + }); + + // THEN + expect(template.node.metadata).toHaveLength(expectedErrors); + }); + + test('for bad duration', () => { + expect(() => { + new LaunchTemplate(stack, 'Template', { + spotOptions: { + // Duration must be an integral number of hours. + blockDuration: Duration.minutes(61), + }, + }); + }).toThrow(); + }); + + test('given blockDuration', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: { + blockDuration: Duration.hours(1), + }, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + SpotOptions: { + BlockDurationMinutes: 60, + }, + }, + }, + })); + }); + + test.each([ + [SpotInstanceInterruption.STOP, 'stop'], + [SpotInstanceInterruption.TERMINATE, 'terminate'], + [SpotInstanceInterruption.HIBERNATE, 'hibernate'], + ])('given interruptionBehavior %p', (given: SpotInstanceInterruption, expected: string) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: { + interruptionBehavior: given, + }, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + SpotOptions: { + InstanceInterruptionBehavior: expected, + }, + }, + }, + })); + }); + + test.each([ + [0.001, '0.001'], + [1, '1'], + [2.5, '2.5'], + ])('given maxPrice %p', (given: number, expected: string) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: { + maxPrice: given, + }, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + SpotOptions: { + MaxPrice: expected, + }, + }, + }, + })); + }); + + test.each([ + [SpotRequestType.ONE_TIME, 'one-time'], + [SpotRequestType.PERSISTENT, 'persistent'], + ])('given requestType %p', (given: SpotRequestType, expected: string) => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: { + requestType: given, + }, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + SpotOptions: { + SpotInstanceType: expected, + }, + }, + }, + })); + }); + + test('given validUntil', () => { + // WHEN + new LaunchTemplate(stack, 'Template', { + spotOptions: { + validUntil: Expiration.atTimestamp(0), + }, + }); + + // THEN + expectCDK(stack).to(haveResourceLike('AWS::EC2::LaunchTemplate', { + LaunchTemplateData: { + InstanceMarketOptions: { + MarketType: 'spot', + SpotOptions: { + ValidUntil: 'Thu, 01 Jan 1970 00:00:00 GMT', + }, + }, + }, + })); + }); +}); diff --git a/packages/@aws-cdk/aws-ec2/test/machine-image.test.ts b/packages/@aws-cdk/aws-ec2/test/machine-image.test.ts index 2fcf7e2980be9..43e6dbdcd88cf 100644 --- a/packages/@aws-cdk/aws-ec2/test/machine-image.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/machine-image.test.ts @@ -1,3 +1,4 @@ +import { expect as cdkExpect, matchTemplate, MatchStyle } from '@aws-cdk/assert'; import { App, Stack } from '@aws-cdk/core'; import * as ec2 from '../lib'; @@ -11,6 +12,43 @@ beforeEach(() => { }); }); +test('can make and use a Linux image', () => { + // WHEN + const image = new ec2.GenericLinuxImage({ + testregion: 'ami-1234', + }); + + // THEN + const details = image.getImage(stack); + expect(details.imageId).toEqual('ami-1234'); + expect(details.osType).toEqual(ec2.OperatingSystemType.LINUX); +}); + +test('can make and use a Linux image in agnostic stack', () => { + // WHEN + app = new App(); + stack = new Stack(app, 'Stack'); + const image = new ec2.GenericLinuxImage({ + testregion: 'ami-1234', + }); + + // THEN + const details = image.getImage(stack); + const expected = { + Mappings: { + AmiMap: { + testregion: { + ami: 'ami-1234', + }, + }, + }, + }; + + cdkExpect(stack).to(matchTemplate(expected, MatchStyle.EXACT)); + expect(stack.resolve(details.imageId)).toEqual({ 'Fn::FindInMap': ['AmiMap', { Ref: 'AWS::Region' }, 'ami'] }); + expect(details.osType).toEqual(ec2.OperatingSystemType.LINUX); +}); + test('can make and use a Windows image', () => { // WHEN const image = new ec2.GenericWindowsImage({ @@ -23,6 +61,31 @@ test('can make and use a Windows image', () => { expect(details.osType).toEqual(ec2.OperatingSystemType.WINDOWS); }); +test('can make and use a Windows image in agnostic stack', () => { + // WHEN + app = new App(); + stack = new Stack(app, 'Stack'); + const image = new ec2.GenericWindowsImage({ + testregion: 'ami-1234', + }); + + // THEN + const details = image.getImage(stack); + const expected = { + Mappings: { + AmiMap: { + testregion: { + ami: 'ami-1234', + }, + }, + }, + }; + + cdkExpect(stack).to(matchTemplate(expected, MatchStyle.EXACT)); + expect(stack.resolve(details.imageId)).toEqual({ 'Fn::FindInMap': ['AmiMap', { Ref: 'AWS::Region' }, 'ami'] }); + expect(details.osType).toEqual(ec2.OperatingSystemType.WINDOWS); +}); + test('can make and use a Generic SSM image', () => { // WHEN const image = new ec2.GenericSSMParameterImage('testParam', ec2.OperatingSystemType.LINUX); diff --git a/packages/@aws-cdk/aws-ec2/test/vpc.from-lookup.test.ts b/packages/@aws-cdk/aws-ec2/test/vpc.from-lookup.test.ts index 5555dc3fa9ed7..12ed7d05329d5 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpc.from-lookup.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpc.from-lookup.test.ts @@ -210,6 +210,47 @@ nodeunitShim({ test.done(); }, + 'subnets in imported VPC has all expected attributes'(test: Test) { + const previous = mockVpcContextProviderWith(test, { + vpcId: 'vpc-1234', + subnetGroups: [ + { + name: 'Public', + type: cxapi.VpcSubnetGroupType.PUBLIC, + subnets: [ + { + subnetId: 'pub-sub-in-us-east-1a', + availabilityZone: 'us-east-1a', + routeTableId: 'rt-123', + cidr: '10.100.0.0/24', + }, + ], + }, + ], + }, options => { + test.deepEqual(options.filter, { + isDefault: 'true', + }); + + test.equal(options.subnetGroupNameTag, undefined); + }); + + const stack = new Stack(); + const vpc = Vpc.fromLookup(stack, 'Vpc', { + isDefault: true, + }); + + let subnet = vpc.publicSubnets[0]; + + test.equal(subnet.availabilityZone, 'us-east-1a'); + test.equal(subnet.subnetId, 'pub-sub-in-us-east-1a'); + test.equal(subnet.routeTable.routeTableId, 'rt-123'); + test.equal(subnet.ipv4CidrBlock, '10.100.0.0/24'); + + + restoreContextProvider(previous); + test.done(); + }, }, }); diff --git a/packages/@aws-cdk/aws-ec2/test/vpn.test.ts b/packages/@aws-cdk/aws-ec2/test/vpn.test.ts index 72be541bf45e2..4cf8880c3b0ab 100644 --- a/packages/@aws-cdk/aws-ec2/test/vpn.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/vpn.test.ts @@ -1,5 +1,5 @@ import { expect, haveResource } from '@aws-cdk/assert'; -import { Duration, Stack } from '@aws-cdk/core'; +import { Duration, Stack, Token } from '@aws-cdk/core'; import { nodeunitShim, Test } from 'nodeunit-shim'; import { PublicSubnet, Vpc, VpnConnection } from '../lib'; @@ -322,4 +322,24 @@ nodeunitShim({ })); test.done(); }, + 'can add a vpn connection with a Token as customer gateway ip'(test:Test) { + // GIVEN + const stack = new Stack(); + const token = Token.asAny('192.0.2.1'); + + // WHEN + new Vpc(stack, 'VpcNetwork', { + vpnConnections: { + VpnConnection: { + ip: token as any, + }, + }, + }); + + // THEN + expect(stack).to(haveResource('AWS::EC2::CustomerGateway', { + IpAddress: '192.0.2.1', + })); + test.done(); + }, }); diff --git a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts index 2f6f5ff436baa..26a3a40f35335 100644 --- a/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts +++ b/packages/@aws-cdk/aws-ecr-assets/lib/image-asset.ts @@ -2,10 +2,14 @@ import * as fs from 'fs'; import * as path from 'path'; import * as assets from '@aws-cdk/assets'; import * as ecr from '@aws-cdk/aws-ecr'; -import { Annotations, Construct as CoreConstruct, FeatureFlags, IgnoreMode, Stack, Token } from '@aws-cdk/core'; +import { Annotations, FeatureFlags, IgnoreMode, Stack, Token } from '@aws-cdk/core'; import * as cxapi from '@aws-cdk/cx-api'; import { Construct } from 'constructs'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Options for DockerImageAsset */ diff --git a/packages/@aws-cdk/aws-ecr/lib/repository.ts b/packages/@aws-cdk/aws-ecr/lib/repository.ts index 36e14cf861adc..83c05ba1ed308 100644 --- a/packages/@aws-cdk/aws-ecr/lib/repository.ts +++ b/packages/@aws-cdk/aws-ecr/lib/repository.ts @@ -202,7 +202,7 @@ export abstract class RepositoryBase extends Resource implements IRepository { detail: { 'repository-name': [this.repositoryName], 'scan-status': ['COMPLETE'], - 'image-tags': options.imageTags ? options.imageTags : undefined, + 'image-tags': options.imageTags ?? undefined, }, }); return rule; @@ -526,7 +526,7 @@ export class Repository extends RepositoryBase { for (const rule of prioritizedRules.concat(autoPrioritizedRules).concat(anyRules)) { ret.push({ ...rule, - rulePriority: rule.rulePriority !== undefined ? rule.rulePriority : autoPrio++, + rulePriority: rule.rulePriority ?? autoPrio++, }); } @@ -557,7 +557,7 @@ function renderLifecycleRule(rule: LifecycleRule) { tagStatus: rule.tagStatus || TagStatus.ANY, tagPrefixList: rule.tagPrefixList, countType: rule.maxImageAge !== undefined ? CountType.SINCE_IMAGE_PUSHED : CountType.IMAGE_COUNT_MORE_THAN, - countNumber: rule.maxImageAge !== undefined ? rule.maxImageAge.toDays() : rule.maxImageCount, + countNumber: rule.maxImageAge?.toDays() ?? rule.maxImageCount, countUnit: rule.maxImageAge !== undefined ? 'days' : undefined, }, action: { diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts index 769756999ad0c..74411bb217558 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-load-balanced-service-base.ts @@ -370,21 +370,19 @@ export abstract class ApplicationLoadBalancedServiceBase extends CoreConstruct { } this.desiredCount = props.desiredCount || 1; - const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true; + const internetFacing = props.publicLoadBalancer ?? true; const lbProps = { vpc: this.cluster.vpc, internetFacing, }; - const loadBalancer = props.loadBalancer !== undefined ? props.loadBalancer - : new ApplicationLoadBalancer(this, 'LB', lbProps); + const loadBalancer = props.loadBalancer ?? new ApplicationLoadBalancer(this, 'LB', lbProps); if (props.certificate !== undefined && props.protocol !== undefined && props.protocol !== ApplicationProtocol.HTTPS) { throw new Error('The HTTPS protocol must be used when a certificate is given'); } - const protocol = props.protocol !== undefined ? props.protocol : - (props.certificate ? ApplicationProtocol.HTTPS : ApplicationProtocol.HTTP); + const protocol = props.protocol ?? (props.certificate ? ApplicationProtocol.HTTPS : ApplicationProtocol.HTTP); if (protocol !== ApplicationProtocol.HTTPS && props.redirectHTTP === true) { throw new Error('The HTTPS protocol must be used when redirecting HTTP traffic'); diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-multiple-target-groups-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-multiple-target-groups-service-base.ts index 67ce9a02e77b6..f3eb132e934ae 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-multiple-target-groups-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/application-multiple-target-groups-service-base.ts @@ -478,10 +478,8 @@ export abstract class ApplicationMultipleTargetGroupsServiceBase extends CoreCon * Create log driver if logging is enabled. */ private createLogDriver(enableLoggingProp?: boolean, logDriverProp?: LogDriver): LogDriver | undefined { - const enableLogging = enableLoggingProp !== undefined ? enableLoggingProp : true; - const logDriver = logDriverProp !== undefined - ? logDriverProp : enableLogging - ? this.createAWSLogDriver(this.node.id) : undefined; + const enableLogging = enableLoggingProp ?? true; + const logDriver = logDriverProp ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); return logDriver; } @@ -522,7 +520,7 @@ export abstract class ApplicationMultipleTargetGroupsServiceBase extends CoreCon } private createLoadBalancer(name: string, publicLoadBalancer?: boolean): ApplicationLoadBalancer { - const internetFacing = publicLoadBalancer !== undefined ? publicLoadBalancer : true; + const internetFacing = publicLoadBalancer ?? true; const lbProps = { vpc: this.cluster.vpc, internetFacing, @@ -532,7 +530,7 @@ export abstract class ApplicationMultipleTargetGroupsServiceBase extends CoreCon } private createListenerProtocol(listenerProtocol?: ApplicationProtocol, certificate?: ICertificate): ApplicationProtocol { - return listenerProtocol !== undefined ? listenerProtocol : (certificate ? ApplicationProtocol.HTTPS : ApplicationProtocol.HTTP); + return listenerProtocol ?? (certificate ? ApplicationProtocol.HTTPS : ApplicationProtocol.HTTP); } private createListenerCertificate(listenerName: string, certificate?: ICertificate, domainName?: string, domainZone?: IHostedZone): ICertificate { diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts index ffdcb3b75912a..656cc19d19d43 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-load-balanced-service-base.ts @@ -308,18 +308,15 @@ export abstract class NetworkLoadBalancedServiceBase extends CoreConstruct { } this.desiredCount = props.desiredCount || 1; - const internetFacing = props.publicLoadBalancer !== undefined ? props.publicLoadBalancer : true; + const internetFacing = props.publicLoadBalancer ?? true; const lbProps = { vpc: this.cluster.vpc, internetFacing, }; - const loadBalancer = props.loadBalancer !== undefined ? props.loadBalancer : - new NetworkLoadBalancer(this, 'LB', lbProps); - - const listenerPort = props.listenerPort !== undefined ? props.listenerPort : 80; - + const loadBalancer = props.loadBalancer ?? new NetworkLoadBalancer(this, 'LB', lbProps); + const listenerPort = props.listenerPort ?? 80; const targetProps = { port: 80, }; diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-multiple-target-groups-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-multiple-target-groups-service-base.ts index d3f74588fd341..d34a6b548076d 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-multiple-target-groups-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/network-multiple-target-groups-service-base.ts @@ -384,10 +384,8 @@ export abstract class NetworkMultipleTargetGroupsServiceBase extends CoreConstru * Create log driver if logging is enabled. */ private createLogDriver(enableLoggingProp?: boolean, logDriverProp?: LogDriver): LogDriver | undefined { - const enableLogging = enableLoggingProp !== undefined ? enableLoggingProp : true; - const logDriver = logDriverProp !== undefined - ? logDriverProp : enableLogging - ? this.createAWSLogDriver(this.node.id) : undefined; + const enableLogging = enableLoggingProp ?? true; + const logDriver = logDriverProp ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); return logDriver; } @@ -413,7 +411,7 @@ export abstract class NetworkMultipleTargetGroupsServiceBase extends CoreConstru } private createLoadBalancer(name: string, publicLoadBalancer?: boolean): NetworkLoadBalancer { - const internetFacing = publicLoadBalancer !== undefined ? publicLoadBalancer : true; + const internetFacing = publicLoadBalancer ?? true; const lbProps = { vpc: this.cluster.vpc, internetFacing, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/base/queue-processing-service-base.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/base/queue-processing-service-base.ts index 66bdea9619629..3248514931f4d 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/base/queue-processing-service-base.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/base/queue-processing-service-base.ts @@ -262,22 +262,18 @@ export abstract class QueueProcessingServiceBase extends CoreConstruct { // Setup autoscaling scaling intervals const defaultScalingSteps = [{ upper: 0, change: -1 }, { lower: 100, change: +1 }, { lower: 500, change: +5 }]; - this.scalingSteps = props.scalingSteps !== undefined ? props.scalingSteps : defaultScalingSteps; + this.scalingSteps = props.scalingSteps ?? defaultScalingSteps; // Create log driver if logging is enabled - const enableLogging = props.enableLogging !== undefined ? props.enableLogging : true; - this.logDriver = props.logDriver !== undefined - ? props.logDriver - : enableLogging - ? this.createAWSLogDriver(this.node.id) - : undefined; + const enableLogging = props.enableLogging ?? true; + this.logDriver = props.logDriver ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); // Add the queue name to environment variables this.environment = { ...(props.environment || {}), QUEUE_NAME: this.sqsQueue.queueName }; this.secrets = props.secrets; // Determine the desired task count (minimum) and maximum scaling capacity - this.desiredCount = props.desiredTaskCount !== undefined ? props.desiredTaskCount : 1; + this.desiredCount = props.desiredTaskCount ?? 1; this.maxCapacity = props.maxScalingCapacity || (2 * this.desiredCount); if (!this.desiredCount && !this.maxCapacity) { diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts index 9cec9e1d9e083..b9bdcf2d100ad 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-load-balanced-ecs-service.ts @@ -97,12 +97,10 @@ export class ApplicationLoadBalancedEc2Service extends ApplicationLoadBalancedSe }); // Create log driver if logging is enabled - const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; - const logDriver = taskImageOptions.logDriver !== undefined - ? taskImageOptions.logDriver : enableLogging - ? this.createAWSLogDriver(this.node.id) : undefined; + const enableLogging = taskImageOptions.enableLogging ?? true; + const logDriver = taskImageOptions.logDriver ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, cpu: props.cpu, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-multiple-target-groups-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-multiple-target-groups-ecs-service.ts index 4e6fa5c04b6f3..6ed6b6b71802f 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-multiple-target-groups-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/application-multiple-target-groups-ecs-service.ts @@ -94,7 +94,7 @@ export class ApplicationMultipleTargetGroupsEc2Service extends ApplicationMultip taskRole: taskImageOptions.taskRole, }); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, cpu: props.cpu, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts index 2d2c00fcf345e..fae46b68e7380 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-load-balanced-ecs-service.ts @@ -95,12 +95,10 @@ export class NetworkLoadBalancedEc2Service extends NetworkLoadBalancedServiceBas }); // Create log driver if logging is enabled - const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; - const logDriver = taskImageOptions.logDriver !== undefined - ? taskImageOptions.logDriver : enableLogging - ? this.createAWSLogDriver(this.node.id) : undefined; + const enableLogging = taskImageOptions.enableLogging ?? true; + const logDriver = taskImageOptions.logDriver ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, cpu: props.cpu, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts index a283c9ab55dec..eb8392d3b2148 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/network-multiple-target-groups-ecs-service.ts @@ -93,7 +93,7 @@ export class NetworkMultipleTargetGroupsEc2Service extends NetworkMultipleTarget taskRole: taskImageOptions.taskRole, }); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, cpu: props.cpu, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts index 7350ae82bb2cc..7f64a18df9ff8 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/ecs/scheduled-ecs-task.ts @@ -107,7 +107,7 @@ export class ScheduledEc2Task extends ScheduledTaskBase { command: taskImageOptions.command, environment: taskImageOptions.environment, secrets: taskImageOptions.secrets, - logging: taskImageOptions.logDriver !== undefined ? taskImageOptions.logDriver : this.createAWSLogDriver(this.node.id), + logging: taskImageOptions.logDriver ?? this.createAWSLogDriver(this.node.id), }); } else { throw new Error('You must specify a taskDefinition or image'); diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts index 00049e662ee0f..2ae468bcae558 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.ts @@ -117,7 +117,7 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc constructor(scope: Construct, id: string, props: ApplicationLoadBalancedFargateServiceProps = {}) { super(scope, id, props); - this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; + this.assignPublicIp = props.assignPublicIp ?? false; if (props.taskDefinition && props.taskImageOptions) { throw new Error('You must specify either a taskDefinition or an image, not both.'); @@ -134,12 +134,12 @@ export class ApplicationLoadBalancedFargateService extends ApplicationLoadBalanc }); // Create log driver if logging is enabled - const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; + const enableLogging = taskImageOptions.enableLogging ?? true; const logDriver = taskImageOptions.logDriver !== undefined ? taskImageOptions.logDriver : enableLogging ? this.createAWSLogDriver(this.node.id) : undefined; - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, logging: logDriver, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-multiple-target-groups-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-multiple-target-groups-fargate-service.ts index a5840bc2f2e76..495049dfccfa8 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-multiple-target-groups-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/application-multiple-target-groups-fargate-service.ts @@ -113,7 +113,7 @@ export class ApplicationMultipleTargetGroupsFargateService extends ApplicationMu constructor(scope: Construct, id: string, props: ApplicationMultipleTargetGroupsFargateServiceProps = {}) { super(scope, id, props); - this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; + this.assignPublicIp = props.assignPublicIp ?? false; if (props.taskDefinition && props.taskImageOptions) { throw new Error('You must specify only one of TaskDefinition or TaskImageOptions.'); @@ -129,7 +129,7 @@ export class ApplicationMultipleTargetGroupsFargateService extends ApplicationMu family: taskImageOptions.family, }); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, logging: this.logDriver, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts index 10dcfebdc2f80..4aad4b31e7efe 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-load-balanced-fargate-service.ts @@ -106,7 +106,7 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic constructor(scope: Construct, id: string, props: NetworkLoadBalancedFargateServiceProps = {}) { super(scope, id, props); - this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; + this.assignPublicIp = props.assignPublicIp ?? false; if (props.taskDefinition && props.taskImageOptions) { throw new Error('You must specify either a taskDefinition or an image, not both.'); @@ -123,12 +123,10 @@ export class NetworkLoadBalancedFargateService extends NetworkLoadBalancedServic }); // Create log driver if logging is enabled - const enableLogging = taskImageOptions.enableLogging !== undefined ? taskImageOptions.enableLogging : true; - const logDriver = taskImageOptions.logDriver !== undefined - ? taskImageOptions.logDriver : enableLogging - ? this.createAWSLogDriver(this.node.id) : undefined; + const enableLogging = taskImageOptions.enableLogging ?? true; + const logDriver = taskImageOptions.logDriver ?? (enableLogging ? this.createAWSLogDriver(this.node.id) : undefined); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, logging: logDriver, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts index 0d97d730daeab..dab033b1938ce 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/network-multiple-target-groups-fargate-service.ts @@ -113,7 +113,7 @@ export class NetworkMultipleTargetGroupsFargateService extends NetworkMultipleTa constructor(scope: Construct, id: string, props: NetworkMultipleTargetGroupsFargateServiceProps = {}) { super(scope, id, props); - this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false; + this.assignPublicIp = props.assignPublicIp ?? false; if (props.taskDefinition && props.taskImageOptions) { throw new Error('You must specify only one of TaskDefinition or TaskImageOptions.'); @@ -129,7 +129,7 @@ export class NetworkMultipleTargetGroupsFargateService extends NetworkMultipleTa family: taskImageOptions.family, }); - const containerName = taskImageOptions.containerName !== undefined ? taskImageOptions.containerName : 'web'; + const containerName = taskImageOptions.containerName ?? 'web'; const container = this.taskDefinition.addContainer(containerName, { image: taskImageOptions.image, logging: this.logDriver, diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts index 27b9d8b6ad224..ab46883fa9c90 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts +++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/scheduled-fargate-task.ts @@ -115,7 +115,7 @@ export class ScheduledFargateTask extends ScheduledTaskBase { command: taskImageOptions.command, environment: taskImageOptions.environment, secrets: taskImageOptions.secrets, - logging: taskImageOptions.logDriver !== undefined ? taskImageOptions.logDriver : this.createAWSLogDriver(this.node.id), + logging: taskImageOptions.logDriver ?? this.createAWSLogDriver(this.node.id), }); } else { throw new Error('You must specify one of: taskDefinition or image'); diff --git a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts index 6704ab8d79ca9..5c5160a849835 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/base-service.ts @@ -234,8 +234,7 @@ class ApplicationListenerConfig extends ListenerConfig { public addTargets(id: string, target: LoadBalancerTargetOptions, service: BaseService) { const props = this.props || {}; const protocol = props.protocol; - const port = props.port !== undefined ? props.port : (protocol === undefined ? 80 : - (protocol === elbv2.ApplicationProtocol.HTTPS ? 443 : 80)); + const port = props.port ?? (protocol === elbv2.ApplicationProtocol.HTTPS ? 443 : 80); this.listener.addTargets(id, { ... props, targets: [ @@ -260,7 +259,7 @@ class NetworkListenerConfig extends ListenerConfig { * Create and attach a target group to listener. */ public addTargets(id: string, target: LoadBalancerTargetOptions, service: BaseService) { - const port = this.props !== undefined ? this.props.port : 80; + const port = this.props?.port ?? 80; this.listener.addTargets(id, { ... this.props, targets: [ @@ -370,7 +369,7 @@ export abstract class BaseService extends Resource } : undefined, }, propagateTags: props.propagateTags === PropagatedTagSource.NONE ? undefined : props.propagateTags, - enableEcsManagedTags: props.enableECSManagedTags === undefined ? false : props.enableECSManagedTags, + enableEcsManagedTags: props.enableECSManagedTags ?? false, deploymentController: props.deploymentController, launchType: props.deploymentController?.type === DeploymentControllerType.EXTERNAL ? undefined : props.launchType, healthCheckGracePeriodSeconds: this.evaluateHealthGracePeriod(props.healthCheckGracePeriod), @@ -525,7 +524,7 @@ export abstract class BaseService extends Resource * @returns The created CloudMap service */ public enableCloudMap(options: CloudMapOptions): cloudmap.Service { - const sdNamespace = options.cloudMapNamespace !== undefined ? options.cloudMapNamespace : this.cluster.defaultCloudMapNamespace; + const sdNamespace = options.cloudMapNamespace ?? this.cluster.defaultCloudMapNamespace; if (sdNamespace === undefined) { throw new Error('Cannot enable service discovery if a Cloudmap Namespace has not been created in the cluster.'); } @@ -739,9 +738,7 @@ export abstract class BaseService extends Resource */ private evaluateHealthGracePeriod(providedHealthCheckGracePeriod?: Duration): IResolvable { return Lazy.any({ - produce: () => providedHealthCheckGracePeriod !== undefined ? providedHealthCheckGracePeriod.toSeconds() : - this.loadBalancers.length > 0 ? 60 : - undefined, + produce: () => providedHealthCheckGracePeriod?.toSeconds() ?? (this.loadBalancers.length > 0 ? 60 : undefined), }); } } diff --git a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts index ff27f00cb79a8..f7b0f05c92800 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/task-definition.ts @@ -284,8 +284,7 @@ export class TaskDefinition extends TaskDefinitionBase { props.volumes.forEach(v => this.addVolume(v)); } - this.networkMode = props.networkMode !== undefined ? props.networkMode : - this.isFargateCompatible ? NetworkMode.AWS_VPC : NetworkMode.BRIDGE; + this.networkMode = props.networkMode ?? (this.isFargateCompatible ? NetworkMode.AWS_VPC : NetworkMode.BRIDGE); if (this.isFargateCompatible && this.networkMode !== NetworkMode.AWS_VPC) { throw new Error(`Fargate tasks can only have AwsVpc network mode, got: ${this.networkMode}`); } diff --git a/packages/@aws-cdk/aws-ecs/lib/cluster.ts b/packages/@aws-cdk/aws-ecs/lib/cluster.ts index fcc28784229e5..ca84679e7b970 100644 --- a/packages/@aws-cdk/aws-ecs/lib/cluster.ts +++ b/packages/@aws-cdk/aws-ecs/lib/cluster.ts @@ -790,7 +790,7 @@ class ImportedCluster extends Resource implements ICluster { this.hasEc2Capacity = props.hasEc2Capacity !== false; this._defaultCloudMapNamespace = props.defaultCloudMapNamespace; - this.clusterArn = props.clusterArn !== undefined ? props.clusterArn : Stack.of(this).formatArn({ + this.clusterArn = props.clusterArn ?? Stack.of(this).formatArn({ service: 'ecs', resource: 'cluster', resourceName: props.clusterName, diff --git a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts index b4d31c56133fc..983489743482f 100644 --- a/packages/@aws-cdk/aws-ecs/lib/container-definition.ts +++ b/packages/@aws-cdk/aws-ecs/lib/container-definition.ts @@ -399,7 +399,7 @@ export class ContainerDefinition extends CoreConstruct { throw new Error('MemoryLimitMiB should not be less than MemoryReservationMiB.'); } } - this.essential = props.essential !== undefined ? props.essential : true; + this.essential = props.essential ?? true; this.taskDefinition = props.taskDefinition; this.memoryLimitSpecified = props.memoryLimitMiB !== undefined || props.memoryReservationMiB !== undefined; this.linuxParameters = props.linuxParameters; @@ -705,10 +705,10 @@ function renderEnvironmentFiles(environmentFiles: EnvironmentFileConfig[]): any[ function renderHealthCheck(hc: HealthCheck): CfnTaskDefinition.HealthCheckProperty { return { command: getHealthCheckCommand(hc), - interval: hc.interval != null ? hc.interval.toSeconds() : 30, - retries: hc.retries !== undefined ? hc.retries : 3, - startPeriod: hc.startPeriod && hc.startPeriod.toSeconds(), - timeout: hc.timeout !== undefined ? hc.timeout.toSeconds() : 5, + interval: hc.interval?.toSeconds() ?? 30, + retries: hc.retries ?? 3, + startPeriod: hc.startPeriod?.toSeconds(), + timeout: hc.timeout?.toSeconds() ?? 5, }; } diff --git a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts index a119e445e6571..18c6df350fb4e 100644 --- a/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts @@ -181,8 +181,7 @@ export class Ec2Service extends BaseService implements IEc2Service { throw new Error('Only one of SecurityGroup or SecurityGroups can be populated.'); } - const propagateTagsFromSource = props.propagateTaskTagsFrom !== undefined ? props.propagateTaskTagsFrom - : (props.propagateTags !== undefined ? props.propagateTags : PropagatedTagSource.NONE); + const propagateTagsFromSource = props.propagateTaskTagsFrom ?? props.propagateTags ?? PropagatedTagSource.NONE; super(scope, id, { ...props, diff --git a/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts b/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts index f70ca796ed3f6..01a9f75665c57 100644 --- a/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts +++ b/packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts @@ -147,8 +147,7 @@ export class FargateService extends BaseService implements IFargateService { throw new Error(`The task definition of this service uses at least one container that references a secret JSON field. This feature requires platform version ${FargatePlatformVersion.VERSION1_4} or later.`); } - const propagateTagsFromSource = props.propagateTaskTagsFrom !== undefined ? props.propagateTaskTagsFrom - : (props.propagateTags !== undefined ? props.propagateTags : PropagatedTagSource.NONE); + const propagateTagsFromSource = props.propagateTaskTagsFrom ?? props.propagateTags ?? PropagatedTagSource.NONE; super(scope, id, { ...props, diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/aws-auth.ts b/packages/@aws-cdk/aws-eks-legacy/lib/aws-auth.ts index 276937847d558..933ccbb144ea1 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/aws-auth.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/aws-auth.ts @@ -1,9 +1,13 @@ import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Lazy, Stack } from '@aws-cdk/core'; +import { Lazy, Stack } from '@aws-cdk/core'; import { Mapping } from './aws-auth-mapping'; import { Cluster } from './cluster'; import { KubernetesResource } from './k8s-resource'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + export interface AwsAuthProps { /** * The EKS cluster to apply this configuration to. diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/cluster-resource.ts b/packages/@aws-cdk/aws-eks-legacy/lib/cluster-resource.ts index 8c7600d3a1d34..81c04ef56653a 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/cluster-resource.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/cluster-resource.ts @@ -2,10 +2,14 @@ import * as path from 'path'; import * as cfn from '@aws-cdk/aws-cloudformation'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct, Duration, Token } from '@aws-cdk/core'; +import { Duration, Token } from '@aws-cdk/core'; import { CfnClusterProps } from './eks.generated'; import { KubectlLayer } from './kubectl-layer'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * A low-level CFN resource Amazon EKS cluster implemented through a custom * resource. diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts b/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts index 61f3ccc01fe3d..1f8c699180aad 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/cluster.ts @@ -4,7 +4,7 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as ssm from '@aws-cdk/aws-ssm'; -import { Annotations, CfnOutput, Construct, Duration, IResource, Resource, Stack, Token, Tags } from '@aws-cdk/core'; +import { Annotations, CfnOutput, Duration, IResource, Resource, Stack, Token, Tags } from '@aws-cdk/core'; import { AwsAuth } from './aws-auth'; import { ClusterResource } from './cluster-resource'; import { CfnCluster, CfnClusterProps } from './eks.generated'; @@ -14,6 +14,10 @@ import { KubectlLayer } from './kubectl-layer'; import { spotInterruptHandler } from './spot-interrupt-handler'; import { renderUserData } from './user-data'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + // defaults are based on https://eksctl.io const DEFAULT_CAPACITY_COUNT = 2; const DEFAULT_CAPACITY_TYPE = ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE); @@ -378,7 +382,7 @@ export class Cluster extends Resource implements ICluster { }; let resource; - this.kubectlEnabled = props.kubectlEnabled === undefined ? true : props.kubectlEnabled; + this.kubectlEnabled = props.kubectlEnabled ?? true; if (this.kubectlEnabled) { resource = new ClusterResource(this, 'Resource', clusterProps); this._defaultMastersRole = resource.creationRole; @@ -425,13 +429,13 @@ export class Cluster extends Resource implements ICluster { } // allocate default capacity if non-zero (or default). - const desiredCapacity = props.defaultCapacity === undefined ? DEFAULT_CAPACITY_COUNT : props.defaultCapacity; + const desiredCapacity = props.defaultCapacity ?? DEFAULT_CAPACITY_COUNT; if (desiredCapacity > 0) { const instanceType = props.defaultCapacityInstance || DEFAULT_CAPACITY_TYPE; this.defaultCapacity = this.addCapacity('DefaultCapacity', { instanceType, desiredCapacity }); } - const outputConfigCommand = props.outputConfigCommand === undefined ? true : props.outputConfigCommand; + const outputConfigCommand = props.outputConfigCommand ?? true; if (outputConfigCommand) { const postfix = commonCommandOptions.join(' '); new CfnOutput(this, 'ConfigCommand', { value: `${updateConfigCommandPrefix} ${postfix}` }); @@ -508,7 +512,7 @@ export class Cluster extends Resource implements ICluster { autoScalingGroup.connections.allowToAnyIpv4(ec2.Port.allUdp()); autoScalingGroup.connections.allowToAnyIpv4(ec2.Port.allIcmp()); - const bootstrapEnabled = options.bootstrapEnabled !== undefined ? options.bootstrapEnabled : true; + const bootstrapEnabled = options.bootstrapEnabled ?? true; if (options.bootstrapOptions && !bootstrapEnabled) { throw new Error('Cannot specify "bootstrapOptions" if "bootstrapEnabled" is false'); } @@ -533,7 +537,7 @@ export class Cluster extends Resource implements ICluster { // do not attempt to map the role if `kubectl` is not enabled for this // cluster or if `mapRole` is set to false. By default this should happen. - const mapRole = options.mapRole === undefined ? true : options.mapRole; + const mapRole = options.mapRole ?? true; if (mapRole && this.kubectlEnabled) { // see https://docs.aws.amazon.com/en_us/eks/latest/userguide/add-user-role.html this.awsAuth.addRoleMapping(autoScalingGroup.role, { diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/helm-chart.ts b/packages/@aws-cdk/aws-eks-legacy/lib/helm-chart.ts index 9be42e435123c..a427353e9f6c7 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/helm-chart.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/helm-chart.ts @@ -1,10 +1,14 @@ import * as path from 'path'; import { CustomResource, CustomResourceProvider } from '@aws-cdk/aws-cloudformation'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct, Duration, Names, Stack } from '@aws-cdk/core'; +import { Duration, Names, Stack } from '@aws-cdk/core'; import { Cluster } from './cluster'; import { KubectlLayer } from './kubectl-layer'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Helm Chart options. */ diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/k8s-resource.ts b/packages/@aws-cdk/aws-eks-legacy/lib/k8s-resource.ts index d5fd29d272903..14aa566d50406 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/k8s-resource.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/k8s-resource.ts @@ -1,7 +1,11 @@ import * as cfn from '@aws-cdk/aws-cloudformation'; -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { Cluster } from './cluster'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + export interface KubernetesResourceProps { /** * The EKS cluster to apply this configuration to. diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/kubectl-layer.ts b/packages/@aws-cdk/aws-eks-legacy/lib/kubectl-layer.ts index 33aebc224d6ce..7121b34a277d6 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/kubectl-layer.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/kubectl-layer.ts @@ -1,6 +1,10 @@ import * as crypto from 'crypto'; import * as lambda from '@aws-cdk/aws-lambda'; -import { CfnResource, Construct, Resource, Stack, Token } from '@aws-cdk/core'; +import { CfnResource, Resource, Stack, Token } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; const KUBECTL_APP_ARN = 'arn:aws:serverlessrepo:us-east-1:903779448426:applications/lambda-layer-kubectl'; const KUBECTL_APP_VERSION = '1.13.7'; diff --git a/packages/@aws-cdk/aws-eks-legacy/lib/user-data.ts b/packages/@aws-cdk/aws-eks-legacy/lib/user-data.ts index e889663520f32..75d6e009d657a 100644 --- a/packages/@aws-cdk/aws-eks-legacy/lib/user-data.ts +++ b/packages/@aws-cdk/aws-eks-legacy/lib/user-data.ts @@ -12,7 +12,7 @@ export function renderUserData(clusterName: string, autoScalingGroup: autoscalin const extraArgs = new Array(); - extraArgs.push(`--use-max-pods ${options.useMaxPods === undefined ? true : options.useMaxPods}`); + extraArgs.push(`--use-max-pods ${options.useMaxPods ?? true}`); if (options.awsApiRetryAttempts) { extraArgs.push(`--aws-api-retry-attempts ${options.awsApiRetryAttempts}`); diff --git a/packages/@aws-cdk/aws-eks/lib/cluster.ts b/packages/@aws-cdk/aws-eks/lib/cluster.ts index 6737ac1c7f604..706a23720c157 100644 --- a/packages/@aws-cdk/aws-eks/lib/cluster.ts +++ b/packages/@aws-cdk/aws-eks/lib/cluster.ts @@ -1105,7 +1105,7 @@ export class Cluster extends ClusterBase { commonCommandOptions.push(`--role-arn ${mastersRole.roleArn}`); // allocate default capacity if non-zero (or default). - const minCapacity = props.defaultCapacity === undefined ? DEFAULT_CAPACITY_COUNT : props.defaultCapacity; + const minCapacity = props.defaultCapacity ?? DEFAULT_CAPACITY_COUNT; if (minCapacity > 0) { const instanceType = props.defaultCapacityInstance || DEFAULT_CAPACITY_TYPE; this.defaultCapacity = props.defaultCapacityType === DefaultCapacityType.EC2 ? @@ -1115,7 +1115,7 @@ export class Cluster extends ClusterBase { this.addNodegroupCapacity('DefaultCapacity', { instanceTypes: [instanceType], minSize: minCapacity }) : undefined; } - const outputConfigCommand = props.outputConfigCommand === undefined ? true : props.outputConfigCommand; + const outputConfigCommand = props.outputConfigCommand ?? true; if (outputConfigCommand) { const postfix = commonCommandOptions.join(' '); new CfnOutput(this, 'ConfigCommand', { value: `${updateConfigCommandPrefix} ${postfix}` }); @@ -1251,7 +1251,7 @@ export class Cluster extends ClusterBase { // allow traffic to/from managed node groups (eks attaches this security group to the managed nodes) autoScalingGroup.addSecurityGroup(this.clusterSecurityGroup); - const bootstrapEnabled = options.bootstrapEnabled !== undefined ? options.bootstrapEnabled : true; + const bootstrapEnabled = options.bootstrapEnabled ?? true; if (options.bootstrapOptions && !bootstrapEnabled) { throw new Error('Cannot specify "bootstrapOptions" if "bootstrapEnabled" is false'); } @@ -1278,7 +1278,7 @@ export class Cluster extends ClusterBase { // do not attempt to map the role if `kubectl` is not enabled for this // cluster or if `mapRole` is set to false. By default this should happen. - const mapRole = options.mapRole === undefined ? true : options.mapRole; + const mapRole = options.mapRole ?? true; if (mapRole) { // see https://docs.aws.amazon.com/en_us/eks/latest/userguide/add-user-role.html this.awsAuth.addRoleMapping(autoScalingGroup.role, { diff --git a/packages/@aws-cdk/aws-eks/lib/user-data.ts b/packages/@aws-cdk/aws-eks/lib/user-data.ts index cf38cf7ee9761..3b8d997535771 100644 --- a/packages/@aws-cdk/aws-eks/lib/user-data.ts +++ b/packages/@aws-cdk/aws-eks/lib/user-data.ts @@ -13,7 +13,7 @@ export function renderAmazonLinuxUserData(clusterName: string, autoScalingGroup: const extraArgs = new Array(); - extraArgs.push(`--use-max-pods ${options.useMaxPods === undefined ? true : options.useMaxPods}`); + extraArgs.push(`--use-max-pods ${options.useMaxPods ?? true}`); if (options.awsApiRetryAttempts) { extraArgs.push(`--aws-api-retry-attempts ${options.awsApiRetryAttempts}`); diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts index 55c423f157a55..eaed2daee58d2 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancing/lib/load-balancer.ts @@ -248,7 +248,7 @@ export class LoadBalancer extends Resource implements IConnectable { listeners: Lazy.any({ produce: () => this.listeners }), scheme: props.internetFacing ? 'internet-facing' : 'internal', healthCheck: props.healthCheck && healthCheckToJSON(props.healthCheck), - crossZone: (props.crossZone === undefined || props.crossZone) ? true : false, + crossZone: props.crossZone ?? true, }); if (props.internetFacing) { this.elb.node.addDependency(selectedSubnets.internetConnectivityEstablished); diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-action.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-action.ts index 09ae095c46a92..91d839a605d74 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-action.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-listener-action.ts @@ -1,9 +1,13 @@ -import { Construct, Duration, IConstruct, SecretValue, Tokenization } from '@aws-cdk/core'; +import { Duration, IConstruct, SecretValue, Tokenization } from '@aws-cdk/core'; import { CfnListener } from '../elasticloadbalancingv2.generated'; import { IListenerAction } from '../shared/listener-action'; import { IApplicationListener } from './application-listener'; import { IApplicationTargetGroup } from './application-target-group'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * What to do when a client makes a request to a listener * diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts index a457878c63bb9..a1d3de25bf82d 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/alb/application-target-group.ts @@ -1,6 +1,6 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; -import { Annotations, Construct as CoreConstruct, Duration } from '@aws-cdk/core'; +import { Annotations, Duration } from '@aws-cdk/core'; import { IConstruct, Construct } from 'constructs'; import { ApplicationELBMetrics } from '../elasticloadbalancingv2-canned-metrics.generated'; import { @@ -13,6 +13,10 @@ import { determineProtocolAndPort } from '../shared/util'; import { IApplicationListener } from './application-listener'; import { HttpCodeTarget } from './application-load-balancer'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Properties for defining an Application Target Group */ diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener-action.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener-action.ts index 81b45a5e3b146..cc86ca0458ef7 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener-action.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/nlb/network-listener-action.ts @@ -1,9 +1,13 @@ -import { Construct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { CfnListener } from '../elasticloadbalancingv2.generated'; import { IListenerAction } from '../shared/listener-action'; import { INetworkListener } from './network-listener'; import { INetworkTargetGroup } from './network-target-group'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * What to do when a client makes a request to a listener * diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts index 17170f4402b1a..db52671e5a368 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-listener.ts @@ -108,7 +108,7 @@ export abstract class BaseListener extends Resource { const resource = new CfnListener(this, 'Resource', { ...additionalProps, - defaultActions: Lazy.any({ produce: () => this.defaultAction ? this.defaultAction.renderActions() : [] }), + defaultActions: Lazy.any({ produce: () => this.defaultAction?.renderActions() ?? [] }), }); this.listenerArn = resource.ref; diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts index 0ff63805aab90..5e569fd8213ca 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts @@ -246,20 +246,20 @@ export abstract class TargetGroupBase extends CoreConstruct implements ITargetGr vpcId: cdk.Lazy.string({ produce: () => this.vpc && this.targetType !== TargetType.LAMBDA ? this.vpc.vpcId : undefined }), // HEALTH CHECK - healthCheckEnabled: cdk.Lazy.any({ produce: () => this.healthCheck && this.healthCheck.enabled }), + healthCheckEnabled: cdk.Lazy.any({ produce: () => this.healthCheck?.enabled }), healthCheckIntervalSeconds: cdk.Lazy.number({ - produce: () => this.healthCheck && this.healthCheck.interval && this.healthCheck.interval.toSeconds(), + produce: () => this.healthCheck?.interval?.toSeconds(), }), - healthCheckPath: cdk.Lazy.string({ produce: () => this.healthCheck && this.healthCheck.path }), - healthCheckPort: cdk.Lazy.string({ produce: () => this.healthCheck && this.healthCheck.port }), - healthCheckProtocol: cdk.Lazy.string({ produce: () => this.healthCheck && this.healthCheck.protocol }), + healthCheckPath: cdk.Lazy.string({ produce: () => this.healthCheck?.path }), + healthCheckPort: cdk.Lazy.string({ produce: () => this.healthCheck?.port }), + healthCheckProtocol: cdk.Lazy.string({ produce: () => this.healthCheck?.protocol }), healthCheckTimeoutSeconds: cdk.Lazy.number({ - produce: () => this.healthCheck && this.healthCheck.timeout && this.healthCheck.timeout.toSeconds(), + produce: () => this.healthCheck?.timeout?.toSeconds(), }), - healthyThresholdCount: cdk.Lazy.number({ produce: () => this.healthCheck && this.healthCheck.healthyThresholdCount }), - unhealthyThresholdCount: cdk.Lazy.number({ produce: () => this.healthCheck && this.healthCheck.unhealthyThresholdCount }), + healthyThresholdCount: cdk.Lazy.number({ produce: () => this.healthCheck?.healthyThresholdCount }), + unhealthyThresholdCount: cdk.Lazy.number({ produce: () => this.healthCheck?.unhealthyThresholdCount }), matcher: cdk.Lazy.any({ - produce: () => this.healthCheck && this.healthCheck.healthyHttpCodes !== undefined ? { + produce: () => this.healthCheck?.healthyHttpCodes !== undefined ? { httpCode: this.healthCheck.healthyHttpCodes, } : undefined, }), diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts index 2ecc35c365694..a6d2692a59e83 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/util.ts @@ -67,7 +67,7 @@ export function determineProtocolAndPort(protocol: ApplicationProtocol | undefin * Helper function to default undefined input props */ export function ifUndefined(x: T | undefined, def: T) { - return x !== undefined ? x : def; + return x ?? def; } /** diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/security-group.test.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/security-group.test.ts index e4d45eb609335..67d14cd4f721e 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/security-group.test.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/alb/security-group.test.ts @@ -272,7 +272,7 @@ class TestFixture { }); this.lb = new elbv2.ApplicationLoadBalancer(this.stack, 'LB', { vpc: this.vpc }); - createListener = createListener === undefined ? true : createListener; + createListener = createListener ?? true; if (createListener) { this._listener = this.lb.addListener('Listener', { port: 80, open: false }); } diff --git a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts index a4b19981dd0b1..baff4c76cde66 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/ecs-task.ts @@ -115,7 +115,7 @@ export class EcsTask implements events.IRuleTarget { this.cluster = props.cluster; this.taskDefinition = props.taskDefinition; - this.taskCount = props.taskCount !== undefined ? props.taskCount : 1; + this.taskCount = props.taskCount ?? 1; this.platformVersion = props.platformVersion; if (props.role) { diff --git a/packages/@aws-cdk/aws-events-targets/lib/util.ts b/packages/@aws-cdk/aws-events-targets/lib/util.ts index fe41154b6037c..74465558bb3f9 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/util.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/util.ts @@ -1,7 +1,11 @@ import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct, ConstructNode, IConstruct, Names } from '@aws-cdk/core'; +import { ConstructNode, IConstruct, Names } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; /** * Obtain the Role for the EventBridge event diff --git a/packages/@aws-cdk/aws-events/lib/rule.ts b/packages/@aws-cdk/aws-events/lib/rule.ts index 0079954e73558..969965021f75d 100644 --- a/packages/@aws-cdk/aws-events/lib/rule.ts +++ b/packages/@aws-cdk/aws-events/lib/rule.ts @@ -166,7 +166,7 @@ export class Rule extends Resource implements IRule { const targetProps = target.bind(this, autoGeneratedId); const inputProps = targetProps.input && targetProps.input.bind(this); - const roleArn = targetProps.role ? targetProps.role.roleArn : undefined; + const roleArn = targetProps.role?.roleArn; const id = targetProps.id || autoGeneratedId; if (targetProps.targetResource) { @@ -298,7 +298,7 @@ export class Rule extends Resource implements IRule { sqsParameters: targetProps.sqsParameters, input: inputProps && inputProps.input, inputPath: inputProps && inputProps.inputPath, - inputTransformer: inputProps && inputProps.inputTemplate !== undefined ? { + inputTransformer: inputProps?.inputTemplate !== undefined ? { inputTemplate: inputProps.inputTemplate, inputPathsMap: inputProps.inputPathsMap, } : undefined, diff --git a/packages/@aws-cdk/aws-events/lib/schedule.ts b/packages/@aws-cdk/aws-events/lib/schedule.ts index 1e0a391e2b911..8c2c04481c0d2 100644 --- a/packages/@aws-cdk/aws-events/lib/schedule.ts +++ b/packages/@aws-cdk/aws-events/lib/schedule.ts @@ -115,7 +115,7 @@ class LiteralSchedule extends Schedule { } function fallback(x: T | undefined, def: T): T { - return x === undefined ? def : x; + return x ?? def; } /** diff --git a/packages/@aws-cdk/aws-globalaccelerator/lib/accelerator-security-group.ts b/packages/@aws-cdk/aws-globalaccelerator/lib/accelerator-security-group.ts index 793ae6b3aceca..9197613d69b61 100644 --- a/packages/@aws-cdk/aws-globalaccelerator/lib/accelerator-security-group.ts +++ b/packages/@aws-cdk/aws-globalaccelerator/lib/accelerator-security-group.ts @@ -1,8 +1,12 @@ import { ISecurityGroup, SecurityGroup, IVpc } from '@aws-cdk/aws-ec2'; -import { Construct } from '@aws-cdk/core'; + import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; import { EndpointGroup } from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * The security group used by a Global Accelerator to send traffic to resources in a VPC. */ diff --git a/packages/@aws-cdk/aws-glue/lib/table.ts b/packages/@aws-cdk/aws-glue/lib/table.ts index 859bb0b2ee325..635fe67a68d35 100644 --- a/packages/@aws-cdk/aws-glue/lib/table.ts +++ b/packages/@aws-cdk/aws-glue/lib/table.ts @@ -243,7 +243,7 @@ export class Table extends Resource implements ITable { this.columns = props.columns; this.partitionKeys = props.partitionKeys; - this.compressed = props.compressed === undefined ? false : props.compressed; + this.compressed = props.compressed ?? false; const { bucket, encryption, encryptionKey } = createBucket(this, props); this.bucket = bucket; this.encryption = encryption; @@ -267,7 +267,7 @@ export class Table extends Resource implements ITable { storageDescriptor: { location: `s3://${this.bucket.bucketName}/${this.s3Prefix}`, compressed: this.compressed, - storedAsSubDirectories: props.storedAsSubDirectories === undefined ? false : props.storedAsSubDirectories, + storedAsSubDirectories: props.storedAsSubDirectories ?? false, columns: renderColumns(props.columns), inputFormat: props.dataFormat.inputFormat.className, outputFormat: props.dataFormat.outputFormat.className, diff --git a/packages/@aws-cdk/aws-iam/lib/policy.ts b/packages/@aws-cdk/aws-iam/lib/policy.ts index 795049a1cc163..60862dca07c56 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy.ts @@ -160,7 +160,7 @@ export class Policy extends Resource implements IPolicy { }); this._policyName = this.physicalName!; - this.force = props.force !== undefined ? props.force : false; + this.force = props.force ?? false; if (props.users) { props.users.forEach(u => this.attachToUser(u)); diff --git a/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts b/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts index 0a14afee947a5..343c437c494e4 100644 --- a/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts +++ b/packages/@aws-cdk/aws-iam/test/immutable-role.test.ts @@ -1,7 +1,11 @@ import '@aws-cdk/assert/jest'; -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import * as iam from '../lib'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /* eslint-disable quote-props */ describe('ImmutableRole', () => { diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index d8de804e61a12..4e18d228fe38e 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -156,7 +156,7 @@ abstract class KeyBase extends Resource implements IKey { resourceArns: [this.keyArn], resourceSelfArns: crossEnvironment ? undefined : ['*'], }; - if (this.trustAccountIdentities) { + if (this.trustAccountIdentities && !crossEnvironment) { return iam.Grant.addToPrincipalOrResource(grantOptions); } else { return iam.Grant.addToPrincipalAndResource({ diff --git a/packages/@aws-cdk/aws-kms/test/alias.test.ts b/packages/@aws-cdk/aws-kms/test/alias.test.ts index b39ea8bf5b232..6c4356a7e8f36 100644 --- a/packages/@aws-cdk/aws-kms/test/alias.test.ts +++ b/packages/@aws-cdk/aws-kms/test/alias.test.ts @@ -1,9 +1,13 @@ import '@aws-cdk/assert/jest'; import { ArnPrincipal, PolicyStatement } from '@aws-cdk/aws-iam'; -import { App, CfnOutput, Construct, Stack } from '@aws-cdk/core'; +import { App, CfnOutput, Stack } from '@aws-cdk/core'; import { Alias } from '../lib/alias'; import { IKey, Key } from '../lib/key'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + test('default alias', () => { const app = new App(); const stack = new Stack(app, 'Test'); diff --git a/packages/@aws-cdk/aws-kms/test/key.test.ts b/packages/@aws-cdk/aws-kms/test/key.test.ts index 42461faece6f4..b2d37496d00ec 100644 --- a/packages/@aws-cdk/aws-kms/test/key.test.ts +++ b/packages/@aws-cdk/aws-kms/test/key.test.ts @@ -245,6 +245,103 @@ describe('key policies', () => { }); }); + testFutureBehavior('grant for a principal in a different region', flags, cdk.App, (app) => { + const principalStack = new cdk.Stack(app, 'PrincipalStack', { env: { region: 'testregion1' } }); + const principal = new iam.Role(principalStack, 'Role', { + assumedBy: new iam.AnyPrincipal(), + roleName: 'MyRolePhysicalName', + }); + + const keyStack = new cdk.Stack(app, 'KeyStack', { env: { region: 'testregion2' } }); + const key = new kms.Key(keyStack, 'Key'); + + key.grantEncrypt(principal); + + expect(keyStack).toHaveResourceLike('AWS::KMS::Key', { + KeyPolicy: { + Statement: arrayWith( + { + Action: [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::', { Ref: 'AWS::AccountId' }, ':role/MyRolePhysicalName']] } }, + Resource: '*', + }, + ), + Version: '2012-10-17', + }, + }); + expect(principalStack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + }); + + testFutureBehavior('grant for a principal in a different account', flags, cdk.App, (app) => { + const principalStack = new cdk.Stack(app, 'PrincipalStack', { env: { account: '0123456789012' } }); + const principal = new iam.Role(principalStack, 'Role', { + assumedBy: new iam.AnyPrincipal(), + roleName: 'MyRolePhysicalName', + }); + + const keyStack = new cdk.Stack(app, 'KeyStack', { env: { account: '111111111111' } }); + const key = new kms.Key(keyStack, 'Key'); + + key.grantEncrypt(principal); + + expect(keyStack).toHaveResourceLike('AWS::KMS::Key', { + KeyPolicy: { + Statement: [ + { + // Default policy, unmodified + }, + { + Action: [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + Effect: 'Allow', + Principal: { AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::0123456789012:role/MyRolePhysicalName']] } }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + expect(principalStack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: [ + 'kms:Encrypt', + 'kms:ReEncrypt*', + 'kms:GenerateDataKey*', + ], + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + }); + testFutureBehavior('additional key admins can be specified (with imported/immutable principal)', flags, cdk.App, (app) => { const stack = new cdk.Stack(app); const adminRole = iam.Role.fromRoleArn(stack, 'Admin', 'arn:aws:iam::123456789012:role/TrustedAdmin'); diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts index 893a0096f92fd..f61d8409da7bd 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/event-bridge.ts @@ -1,6 +1,10 @@ import * as events from '@aws-cdk/aws-events'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; /** * Use an Event Bridge event bus as a Lambda destination. diff --git a/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts b/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts index eaa6d020de3e7..319546471473d 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/lib/lambda.ts @@ -1,9 +1,13 @@ import * as events from '@aws-cdk/aws-events'; import * as targets from '@aws-cdk/aws-events-targets'; import * as lambda from '@aws-cdk/aws-lambda'; -import { Construct } from '@aws-cdk/core'; + import { EventBridgeDestination } from './event-bridge'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Options for a Lambda destination */ diff --git a/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile.dependencies b/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile.dependencies index bfdb9e093ed4a..536887fd69a9a 100644 --- a/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile.dependencies +++ b/packages/@aws-cdk/aws-lambda-python/lib/Dockerfile.dependencies @@ -6,6 +6,9 @@ FROM $IMAGE # Ensure rsync is installed RUN yum -q list installed rsync &>/dev/null || yum install -y rsync +# Upgrade pip (required by cryptography v3.4 and above, which is a dependency of poetry) +RUN pip install --upgrade pip + # Install pipenv and poetry so we can create a requirements.txt if we detect pipfile or poetry.lock respectively RUN pip install pipenv poetry diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json index c3adcd34e3e95..3690005685439 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3Bucket035B0B74" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3Bucket414E0E30" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098" } ] } @@ -72,13 +72,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerServiceRole77891068", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.6" }, "DependsOn": [ @@ -87,17 +87,17 @@ } }, "Parameters": { - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3Bucket035B0B74": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3Bucket414E0E30": { "Type": "String", - "Description": "S3 bucket for asset \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "S3 bucket for asset \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" }, - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098": { "Type": "String", - "Description": "S3 key for asset version \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "S3 key for asset version \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" }, - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57ArtifactHash70AD5A1E": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353ArtifactHashECA6C88C": { "Type": "String", - "Description": "Artifact hash for asset \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "Artifact hash for asset \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json index 0c310b5f52e7e..ef1f355e528c3 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.pipenv.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3BucketDF70124D" + "Ref": "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cS3BucketA501FC08" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0" + "Ref": "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cS3VersionKey1C3AFB39" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0" + "Ref": "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cS3VersionKey1C3AFB39" } ] } @@ -72,13 +72,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerinlineServiceRole10C681F6", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.6" }, "DependsOn": [ @@ -121,7 +121,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3BucketB5A59BD8" + "Ref": "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28S3Bucket7DE4D4D5" }, "S3Key": { "Fn::Join": [ @@ -134,7 +134,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C" + "Ref": "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28S3VersionKeyAEB67E87" } ] } @@ -147,7 +147,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C" + "Ref": "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28S3VersionKeyAEB67E87" } ] } @@ -157,13 +157,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerpython27ServiceRole2ED49C06", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python2.7" }, "DependsOn": [ @@ -206,7 +206,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3Bucket31144813" + "Ref": "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009S3BucketA66E9035" }, "S3Key": { "Fn::Join": [ @@ -219,7 +219,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383" + "Ref": "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009S3VersionKeyAFEB5FDA" } ] } @@ -232,7 +232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383" + "Ref": "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009S3VersionKeyAFEB5FDA" } ] } @@ -242,13 +242,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerpython38ServiceRole2049AFF7", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.8" }, "DependsOn": [ @@ -257,41 +257,41 @@ } }, "Parameters": { - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3BucketDF70124D": { + "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cS3BucketA501FC08": { "Type": "String", - "Description": "S3 bucket for asset \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "S3 bucket for asset \"94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45c\"" }, - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0": { + "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cS3VersionKey1C3AFB39": { "Type": "String", - "Description": "S3 key for asset version \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "S3 key for asset version \"94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45c\"" }, - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bArtifactHashEE8E0CE9": { + "AssetParameters94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45cArtifactHash99DC751A": { "Type": "String", - "Description": "Artifact hash for asset \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "Artifact hash for asset \"94972df8a01484c56b50bec3793ac6c4302bc044db29d3502007bdc0f83db45c\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3BucketB5A59BD8": { + "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28S3Bucket7DE4D4D5": { "Type": "String", - "Description": "S3 bucket for asset \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "S3 bucket for asset \"3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C": { + "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28S3VersionKeyAEB67E87": { "Type": "String", - "Description": "S3 key for asset version \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "S3 key for asset version \"3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014ArtifactHash7768674B": { + "AssetParameters3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28ArtifactHashE51CE860": { "Type": "String", - "Description": "Artifact hash for asset \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "Artifact hash for asset \"3b0b0f3cd46ea1490006d6cefca359385ec059bb00a0fbee4de2eecf48038e28\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3Bucket31144813": { + "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009S3BucketA66E9035": { "Type": "String", - "Description": "S3 bucket for asset \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "S3 bucket for asset \"876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383": { + "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009S3VersionKeyAFEB5FDA": { "Type": "String", - "Description": "S3 key for asset version \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "S3 key for asset version \"876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aArtifactHash652F614E": { + "AssetParameters876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009ArtifactHashB9A1080D": { "Type": "String", - "Description": "Artifact hash for asset \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "Artifact hash for asset \"876959f777c5a23bf4408991959c55c91810329d159608feb7ede69418b35009\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json index 0c310b5f52e7e..5ea17bca31920 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.poetry.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3BucketDF70124D" + "Ref": "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efS3BucketD53ED9C5" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0" + "Ref": "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efS3VersionKey3C218A3E" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0" + "Ref": "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efS3VersionKey3C218A3E" } ] } @@ -72,13 +72,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerinlineServiceRole10C681F6", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.6" }, "DependsOn": [ @@ -121,7 +121,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3BucketB5A59BD8" + "Ref": "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dS3BucketFDE171D0" }, "S3Key": { "Fn::Join": [ @@ -134,7 +134,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C" + "Ref": "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dS3VersionKey6209E240" } ] } @@ -147,7 +147,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C" + "Ref": "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dS3VersionKey6209E240" } ] } @@ -157,13 +157,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerpython27ServiceRole2ED49C06", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python2.7" }, "DependsOn": [ @@ -206,7 +206,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3Bucket31144813" + "Ref": "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68S3BucketA23E6312" }, "S3Key": { "Fn::Join": [ @@ -219,7 +219,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383" + "Ref": "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68S3VersionKey1E21AF83" } ] } @@ -232,7 +232,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383" + "Ref": "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68S3VersionKey1E21AF83" } ] } @@ -242,13 +242,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerpython38ServiceRole2049AFF7", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.8" }, "DependsOn": [ @@ -257,41 +257,41 @@ } }, "Parameters": { - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3BucketDF70124D": { + "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efS3BucketD53ED9C5": { "Type": "String", - "Description": "S3 bucket for asset \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "S3 bucket for asset \"61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175ef\"" }, - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bS3VersionKey530C68B0": { + "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efS3VersionKey3C218A3E": { "Type": "String", - "Description": "S3 key for asset version \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "S3 key for asset version \"61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175ef\"" }, - "AssetParameterseef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255bArtifactHashEE8E0CE9": { + "AssetParameters61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175efArtifactHash6A1881A8": { "Type": "String", - "Description": "Artifact hash for asset \"eef17c074659b655f9b413019323db3976d06067e78d53c4e609ebe177ce255b\"" + "Description": "Artifact hash for asset \"61d8d26f10d1d73dee2732bec7ed381d2c987fc2912a339f2f119f3b0ea175ef\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3BucketB5A59BD8": { + "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dS3BucketFDE171D0": { "Type": "String", - "Description": "S3 bucket for asset \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "S3 bucket for asset \"1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956d\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014S3VersionKey7657015C": { + "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dS3VersionKey6209E240": { "Type": "String", - "Description": "S3 key for asset version \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "S3 key for asset version \"1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956d\"" }, - "AssetParametersf37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014ArtifactHash7768674B": { + "AssetParameters1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956dArtifactHash02B929EC": { "Type": "String", - "Description": "Artifact hash for asset \"f37a4de97ca8831930cd2d0dc3f0962e653d756a118ce33271752a745489c014\"" + "Description": "Artifact hash for asset \"1d66b06c3b3ee86b3126fb58d7a06ff055d366d8aeeb4dfbaf28d40f0930956d\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3Bucket31144813": { + "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68S3BucketA23E6312": { "Type": "String", - "Description": "S3 bucket for asset \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "S3 bucket for asset \"96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aS3VersionKeyB48E8383": { + "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68S3VersionKey1E21AF83": { "Type": "String", - "Description": "S3 key for asset version \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "S3 key for asset version \"96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68\"" }, - "AssetParameters3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846aArtifactHash652F614E": { + "AssetParameters96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68ArtifactHash0043D2A0": { "Type": "String", - "Description": "Artifact hash for asset \"3eb927f8df31281e22c710f842018fa10b0dde86f74f89313c9a27db6e75846a\"" + "Description": "Artifact hash for asset \"96a447e468bf9d3b52d13213757160cd43f28737a29b8682c281fde388762e68\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json index 5bc285e5c5769..9a81c901d7451 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.project.expected.json @@ -5,7 +5,7 @@ "Properties": { "Content": { "S3Bucket": { - "Ref": "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cS3Bucket6D2DF2A1" + "Ref": "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aS3BucketCCD07444" }, "S3Key": { "Fn::Join": [ @@ -18,7 +18,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cS3VersionKey897AD818" + "Ref": "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aS3VersionKeyA8B74284" } ] } @@ -31,7 +31,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cS3VersionKey897AD818" + "Ref": "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aS3VersionKeyA8B74284" } ] } @@ -118,19 +118,19 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerServiceRole77891068", "Arn" ] }, - "Runtime": "python3.6", + "Handler": "index.handler", "Layers": [ { "Ref": "SharedDACC02AA" } - ] + ], + "Runtime": "python3.6" }, "DependsOn": [ "myhandlerServiceRole77891068" @@ -138,17 +138,17 @@ } }, "Parameters": { - "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cS3Bucket6D2DF2A1": { + "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aS3BucketCCD07444": { "Type": "String", - "Description": "S3 bucket for asset \"314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4c\"" + "Description": "S3 bucket for asset \"6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4a\"" }, - "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cS3VersionKey897AD818": { + "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aS3VersionKeyA8B74284": { "Type": "String", - "Description": "S3 key for asset version \"314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4c\"" + "Description": "S3 key for asset version \"6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4a\"" }, - "AssetParameters314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4cArtifactHashF8341E5E": { + "AssetParameters6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4aArtifactHashB3093591": { "Type": "String", - "Description": "Artifact hash for asset \"314dd9f824ae895011cd7bb81d52a0ba316c902995491d7f4072c5aefccb6e4c\"" + "Description": "Artifact hash for asset \"6a4b9ce26d3228c4effd7b46ed51ab439e79a530934ad9bde7d77d7f6b6ebd4a\"" }, "AssetParameters71de8786d26e9f9205375b6cea9342e92d8a622a97d01d7e7d2f7661f056f218S3Bucket89C9DB12": { "Type": "String", diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json index 2f0a607ecaecb..b5b137205752f 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.py38.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cS3BucketEEA58FD6" + "Ref": "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adS3BucketA9379638" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cS3VersionKey5B5DB95F" + "Ref": "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adS3VersionKey4376B462" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cS3VersionKey5B5DB95F" + "Ref": "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adS3VersionKey4376B462" } ] } @@ -72,13 +72,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerServiceRole77891068", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.8" }, "DependsOn": [ @@ -87,17 +87,17 @@ } }, "Parameters": { - "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cS3BucketEEA58FD6": { + "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adS3BucketA9379638": { "Type": "String", - "Description": "S3 bucket for asset \"428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381c\"" + "Description": "S3 bucket for asset \"1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148ad\"" }, - "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cS3VersionKey5B5DB95F": { + "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adS3VersionKey4376B462": { "Type": "String", - "Description": "S3 key for asset version \"428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381c\"" + "Description": "S3 key for asset version \"1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148ad\"" }, - "AssetParameters428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381cArtifactHash239A9708": { + "AssetParameters1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148adArtifactHashB9B928DC": { "Type": "String", - "Description": "Artifact hash for asset \"428642c68731bb90502dd14a13320f1f31db649ea6f770f56acdc6f4bb1b381c\"" + "Description": "Artifact hash for asset \"1482f01217b8bed41000ca172724dc762f68208d3faa315bd6e8e07bbea148ad\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json index ba8fee87727b4..6b3b8230c2874 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.requirements.removed.expected.json @@ -72,13 +72,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "functionServiceRoleEF216095", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python2.7" }, "DependsOn": [ diff --git a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json index 63e6ba74ccfe9..63fad4c61de14 100644 --- a/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json +++ b/packages/@aws-cdk/aws-lambda-python/test/integ.function.vpc.expected.json @@ -296,7 +296,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3Bucket035B0B74" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3Bucket414E0E30" }, "S3Key": { "Fn::Join": [ @@ -309,7 +309,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098" } ] } @@ -322,7 +322,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F" + "Ref": "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098" } ] } @@ -332,13 +332,13 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "myhandlerServiceRole77891068", "Arn" ] }, + "Handler": "index.handler", "Runtime": "python3.6", "VpcConfig": { "SecurityGroupIds": [ @@ -368,17 +368,17 @@ } }, "Parameters": { - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3Bucket035B0B74": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3Bucket414E0E30": { "Type": "String", - "Description": "S3 bucket for asset \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "S3 bucket for asset \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" }, - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57S3VersionKey781CC06F": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353S3VersionKey5ABC9098": { "Type": "String", - "Description": "S3 key for asset version \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "S3 key for asset version \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" }, - "AssetParametersc677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57ArtifactHash70AD5A1E": { + "AssetParameters4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353ArtifactHashECA6C88C": { "Type": "String", - "Description": "Artifact hash for asset \"c677eb7e524b9819a25fefd7267be6618341cd2b2d81f4b4aaa40911d698db57\"" + "Description": "Artifact hash for asset \"4ee6ce8b6ee4bd51743dc0c39d6e52baebaeafff9c9dfea0ff84de98d1dbf353\"" } }, "Outputs": { diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index a59dc1a311936..de6940cd249e8 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -189,6 +189,17 @@ granting permissions to other AWS accounts or organizations. [Example of Lambda Layer usage](test/integ.layer-version.lit.ts) +By default, updating a layer creates a new layer version, and CloudFormation will delete the old version as part of the stack update. + +Alternatively, a removal policy can be used to retain the old version: + +```ts +import { LayerVersion } from '@aws-cdk/aws-lambda'; +new LayerVersion(this, 'MyLayer', { + removalPolicy: RemovalPolicy.RETAIN +}); +``` + ## Event Rule Target You can use an AWS Lambda function as a target for an Amazon CloudWatch event diff --git a/packages/@aws-cdk/aws-lambda/lib/destination.ts b/packages/@aws-cdk/aws-lambda/lib/destination.ts index 8fb6ab956db6d..8e2917ab827fc 100644 --- a/packages/@aws-cdk/aws-lambda/lib/destination.ts +++ b/packages/@aws-cdk/aws-lambda/lib/destination.ts @@ -1,6 +1,10 @@ -import { Construct } from '@aws-cdk/core'; + import { IFunction } from './function-base'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * A destination configuration */ diff --git a/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts b/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts index 6c2419c6f8fd5..52e1021cd6878 100644 --- a/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts +++ b/packages/@aws-cdk/aws-lambda/lib/event-invoke-config.ts @@ -90,7 +90,7 @@ export class EventInvokeConfig extends Resource { : undefined, functionName: props.function.functionName, maximumEventAgeInSeconds: props.maxEventAge && props.maxEventAge.toSeconds(), - maximumRetryAttempts: props.retryAttempts !== undefined ? props.retryAttempts : undefined, + maximumRetryAttempts: props.retryAttempts ?? undefined, qualifier: props.qualifier || '$LATEST', }); } diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index 8b8dd585c21c2..92ca396cafbb2 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -326,7 +326,7 @@ export abstract class FunctionBase extends Resource implements IFunction { const permissionNode = this._functionNode().tryFindChild(identifier); if (!permissionNode) { throw new Error('Cannot modify permission to lambda function. Function is either imported or $LATEST version. ' - + 'If the function is imported from the same account use `fromFunctionAttributes()` API with the `allowPermissions` flag.'); + + 'If the function is imported from the same account use `fromFunctionAttributes()` API with the `sameEnvironment` flag.'); } return { statementAdded: true, policyDependable: permissionNode }; }, diff --git a/packages/@aws-cdk/aws-lambda/lib/layers.ts b/packages/@aws-cdk/aws-lambda/lib/layers.ts index 92de56f57e7a8..babf91079b8b6 100644 --- a/packages/@aws-cdk/aws-lambda/lib/layers.ts +++ b/packages/@aws-cdk/aws-lambda/lib/layers.ts @@ -1,4 +1,4 @@ -import { IResource, Resource } from '@aws-cdk/core'; +import { IResource, RemovalPolicy, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { Code } from './code'; import { CfnLayerVersion, CfnLayerVersionPermission } from './lambda.generated'; @@ -28,6 +28,14 @@ export interface LayerVersionOptions { * @default - A name will be generated. */ readonly layerVersionName?: string; + + /** + * Whether to retain this version of the layer when a new version is added + * or when the stack is deleted. + * + * @default RemovalPolicy.DESTROY + */ + readonly removalPolicy?: RemovalPolicy; } export interface LayerVersionProps extends LayerVersionOptions { @@ -198,6 +206,10 @@ export class LayerVersion extends LayerVersionBase { licenseInfo: props.license, }); + if (props.removalPolicy) { + resource.applyRemovalPolicy(props.removalPolicy); + } + props.code.bindToResource(resource, { resourceProperty: 'Content', }); diff --git a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts index 3e1c2e634e0b1..34ceb28ad861f 100644 --- a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts @@ -1,7 +1,11 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import { Construct, Token } from '@aws-cdk/core'; +import { Token } from '@aws-cdk/core'; import { IScalableFunctionAttribute, UtilizationScalingOptions } from '../scalable-attribute-api'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * A scalable lambda alias attribute */ diff --git a/packages/@aws-cdk/aws-lambda/lib/runtime.ts b/packages/@aws-cdk/aws-lambda/lib/runtime.ts index 05d4996ff5e4f..3bbd8bcc43f6e 100644 --- a/packages/@aws-cdk/aws-lambda/lib/runtime.ts +++ b/packages/@aws-cdk/aws-lambda/lib/runtime.ts @@ -78,6 +78,11 @@ export class Runtime { */ public static readonly NODEJS_12_X = new Runtime('nodejs12.x', RuntimeFamily.NODEJS, { supportsInlineCode: true }); + /** + * The NodeJS 14.x runtime (nodejs14.x) + */ + public static readonly NODEJS_14_X = new Runtime('nodejs14.x', RuntimeFamily.NODEJS, { supportsInlineCode: false }); + /** * The Python 2.7 runtime (python2.7) */ diff --git a/packages/@aws-cdk/aws-lambda/test/layers.test.ts b/packages/@aws-cdk/aws-lambda/test/layers.test.ts index 0806f6d823dba..4a8f0e94ed6cb 100644 --- a/packages/@aws-cdk/aws-lambda/test/layers.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/layers.test.ts @@ -86,4 +86,21 @@ describe('layers', () => { }, }, ResourcePart.CompleteDefinition); }); + + test('creating a layer with a removal policy', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + new lambda.LayerVersion(stack, 'layer', { + code: lambda.Code.fromAsset(path.join(__dirname, 'layer-code')), + removalPolicy: cdk.RemovalPolicy.RETAIN, + }); + + // THEN + expect(canonicalizeTemplate(SynthUtils.toCloudFormation(stack))).toHaveResource('AWS::Lambda::LayerVersion', { + UpdateReplacePolicy: 'Retain', + DeletionPolicy: 'Retain', + }, ResourcePart.CompleteDefinition); + }); }); diff --git a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts index 8dda5f1728fd3..e9b076b55c8a3 100644 --- a/packages/@aws-cdk/aws-logs/lib/metric-filter.ts +++ b/packages/@aws-cdk/aws-logs/lib/metric-filter.ts @@ -42,7 +42,7 @@ export class MetricFilter extends Resource { metricTransformations: [{ metricNamespace: props.metricNamespace, metricName: props.metricName, - metricValue: props.metricValue !== undefined ? props.metricValue : '1', + metricValue: props.metricValue ?? '1', defaultValue: props.defaultValue, }], }); diff --git a/packages/@aws-cdk/aws-lookoutvision/.eslintrc.js b/packages/@aws-cdk/aws-lookoutvision/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-lookoutvision/.gitignore b/packages/@aws-cdk/aws-lookoutvision/.gitignore new file mode 100644 index 0000000000000..62ebc95d75ce6 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/.gitignore @@ -0,0 +1,19 @@ +*.js +*.js.map +*.d.ts +tsconfig.json +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk +nyc.config.js +!.eslintrc.js +!jest.config.js +junit.xml diff --git a/packages/@aws-cdk/aws-lookoutvision/.npmignore b/packages/@aws-cdk/aws-lookoutvision/.npmignore new file mode 100644 index 0000000000000..e4486030fcb17 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json + +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ diff --git a/packages/@aws-cdk/aws-lookoutvision/LICENSE b/packages/@aws-cdk/aws-lookoutvision/LICENSE new file mode 100644 index 0000000000000..28e4bdcec77ec --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-lookoutvision/NOTICE b/packages/@aws-cdk/aws-lookoutvision/NOTICE new file mode 100644 index 0000000000000..5fc3826926b5b --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-lookoutvision/README.md b/packages/@aws-cdk/aws-lookoutvision/README.md new file mode 100644 index 0000000000000..b0082462251d2 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/README.md @@ -0,0 +1,20 @@ +# AWS::LookoutVision Construct Library + + +--- + +![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge) + +> All classes with the `Cfn` prefix in this module ([CFN Resources]) are always stable and safe to use. +> +> [CFN Resources]: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib + +--- + + + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +```ts +import lookoutvision = require('@aws-cdk/aws-lookoutvision'); +``` diff --git a/packages/@aws-cdk/aws-lookoutvision/jest.config.js b/packages/@aws-cdk/aws-lookoutvision/jest.config.js new file mode 100644 index 0000000000000..54e28beb9798b --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-lookoutvision/lib/index.ts b/packages/@aws-cdk/aws-lookoutvision/lib/index.ts new file mode 100644 index 0000000000000..1be824f5b354f --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/lib/index.ts @@ -0,0 +1,2 @@ +// AWS::LookoutVision CloudFormation Resources: +export * from './lookoutvision.generated'; diff --git a/packages/@aws-cdk/aws-lookoutvision/package.json b/packages/@aws-cdk/aws-lookoutvision/package.json new file mode 100644 index 0000000000000..9f7d59fd4c321 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/package.json @@ -0,0 +1,97 @@ +{ + "name": "@aws-cdk/aws-lookoutvision", + "version": "0.0.0", + "description": "The CDK Construct Library for AWS::LookoutVision", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "projectReferences": true, + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.LookoutVision", + "packageId": "Amazon.CDK.AWS.LookoutVision", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.lookoutvision", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "lookoutvision" + } + }, + "python": { + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ], + "distName": "aws-cdk.aws-lookoutvision", + "module": "aws_cdk.aws_lookoutvision" + } + } + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-lookoutvision" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "integ": "cdk-integ", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "cfn2ts": "cfn2ts", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "gen": "cfn2ts", + "rosetta:extract": "yarn --silent jsii-rosetta extract" + }, + "cdk-build": { + "cloudformation": "AWS::LookoutVision", + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": "true" + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "AWS::LookoutVision", + "aws-lookoutvision" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "cdk-build-tools": "0.0.0", + "cfn2ts": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/core": "0.0.0" + }, + "peerDependencies": { + "@aws-cdk/core": "0.0.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "cfn-only", + "awscdkio": { + "announce": false + } +} diff --git a/packages/@aws-cdk/aws-lookoutvision/test/lookoutvision.test.ts b/packages/@aws-cdk/aws-lookoutvision/test/lookoutvision.test.ts new file mode 100644 index 0000000000000..e394ef336bfb4 --- /dev/null +++ b/packages/@aws-cdk/aws-lookoutvision/test/lookoutvision.test.ts @@ -0,0 +1,6 @@ +import '@aws-cdk/assert/jest'; +import {} from '../lib'; + +test('No tests are specified for this package', () => { + expect(true).toBe(true); +}); diff --git a/packages/@aws-cdk/aws-rds/lib/instance.ts b/packages/@aws-cdk/aws-rds/lib/instance.ts index 74a28666496d2..cb4c3d3487a8c 100644 --- a/packages/@aws-cdk/aws-rds/lib/instance.ts +++ b/packages/@aws-cdk/aws-rds/lib/instance.ts @@ -690,8 +690,8 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData this.newCfnProps = { autoMinorVersionUpgrade: props.autoMinorVersionUpgrade, availabilityZone: props.multiAz ? undefined : props.availabilityZone, - backupRetentionPeriod: props.backupRetention ? props.backupRetention.toDays() : undefined, - copyTagsToSnapshot: props.copyTagsToSnapshot !== undefined ? props.copyTagsToSnapshot : true, + backupRetentionPeriod: props.backupRetention?.toDays(), + copyTagsToSnapshot: props.copyTagsToSnapshot ?? true, dbInstanceClass: Lazy.string({ produce: () => `db.${this.instanceType}` }), dbInstanceIdentifier: props.instanceIdentifier, dbSubnetGroupName: subnetGroup.subnetGroupName, @@ -701,15 +701,15 @@ abstract class DatabaseInstanceNew extends DatabaseInstanceBase implements IData enableIamDatabaseAuthentication: Lazy.any({ produce: () => this.enableIamAuthentication }), enablePerformanceInsights: enablePerformanceInsights || props.enablePerformanceInsights, // fall back to undefined if not set, iops, - monitoringInterval: props.monitoringInterval && props.monitoringInterval.toSeconds(), - monitoringRoleArn: monitoringRole && monitoringRole.roleArn, + monitoringInterval: props.monitoringInterval?.toSeconds(), + monitoringRoleArn: monitoringRole?.roleArn, multiAz: props.multiAz, optionGroupName: props.optionGroup?.optionGroupName, performanceInsightsKmsKeyId: props.performanceInsightEncryptionKey?.keyArn, performanceInsightsRetentionPeriod: enablePerformanceInsights ? (props.performanceInsightRetention || PerformanceInsightRetention.DEFAULT) : undefined, - port: props.port ? props.port.toString() : undefined, + port: props.port?.toString(), preferredBackupWindow: props.preferredBackupWindow, preferredMaintenanceWindow: props.preferredMaintenanceWindow, processorFeatures: props.processorFeatures && renderProcessorFeatures(props.processorFeatures), @@ -849,7 +849,7 @@ abstract class DatabaseInstanceSource extends DatabaseInstanceNew implements IDa ...this.newCfnProps, associatedRoles: instanceAssociatedRoles.length > 0 ? instanceAssociatedRoles : undefined, optionGroupName: engineConfig.optionGroup?.optionGroupName, - allocatedStorage: props.allocatedStorage ? props.allocatedStorage.toString() : '100', + allocatedStorage: props.allocatedStorage?.toString() ?? '100', allowMajorVersionUpgrade: props.allowMajorVersionUpgrade, dbName: props.databaseName, dbParameterGroupName: instanceParameterGroupConfig?.parameterGroupName, @@ -1041,9 +1041,7 @@ export class DatabaseInstanceFromSnapshot extends DatabaseInstanceSource impleme const instance = new CfnDBInstance(this, 'Resource', { ...this.sourceCfnProps, dbSnapshotIdentifier: props.snapshotIdentifier, - masterUserPassword: secret - ? secret.secretValueFromJson('password').toString() - : credentials?.password?.toString(), + masterUserPassword: secret?.secretValueFromJson('password')?.toString() ?? credentials?.password?.toString(), }); this.instanceIdentifier = instance.ref; @@ -1117,7 +1115,7 @@ export class DatabaseInstanceReadReplica extends DatabaseInstanceNew implements ...this.newCfnProps, // this must be ARN, not ID, because of https://github.com/terraform-providers/terraform-provider-aws/issues/528#issuecomment-391169012 sourceDbInstanceIdentifier: props.sourceDatabaseInstance.instanceArn, - kmsKeyId: props.storageEncryptionKey && props.storageEncryptionKey.keyArn, + kmsKeyId: props.storageEncryptionKey?.keyArn, storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted, }); diff --git a/packages/@aws-cdk/aws-rds/lib/private/util.ts b/packages/@aws-cdk/aws-rds/lib/private/util.ts index 8cba1e4a1ee1e..f97486b5daddf 100644 --- a/packages/@aws-cdk/aws-rds/lib/private/util.ts +++ b/packages/@aws-cdk/aws-rds/lib/private/util.ts @@ -1,10 +1,14 @@ import * as iam from '@aws-cdk/aws-iam'; import * as s3 from '@aws-cdk/aws-s3'; -import { Construct, CfnDeletionPolicy, CfnResource, RemovalPolicy } from '@aws-cdk/core'; +import { CfnDeletionPolicy, CfnResource, RemovalPolicy } from '@aws-cdk/core'; import { DatabaseSecret } from '../database-secret'; import { IEngine } from '../engine'; import { Credentials } from '../props'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * The default set of characters we exclude from generated passwords for database users. * It's a combination of characters that have a tendency to cause problems in shell scripts, @@ -88,9 +92,7 @@ export function applyRemovalPolicy(cfnDatabase: CfnResource, removalPolicy?: Rem * Enable if explicitly provided or if the RemovalPolicy has been set to RETAIN */ export function defaultDeletionProtection(deletionProtection?: boolean, removalPolicy?: RemovalPolicy): boolean | undefined { - return deletionProtection !== undefined - ? deletionProtection - : (removalPolicy === RemovalPolicy.RETAIN ? true : undefined); + return deletionProtection ?? (removalPolicy === RemovalPolicy.RETAIN ? true : undefined); } /** diff --git a/packages/@aws-cdk/aws-redshift/lib/cluster.ts b/packages/@aws-cdk/aws-redshift/lib/cluster.ts index 9126ef4fda737..0add37fbbdff3 100644 --- a/packages/@aws-cdk/aws-redshift/lib/cluster.ts +++ b/packages/@aws-cdk/aws-redshift/lib/cluster.ts @@ -406,11 +406,11 @@ export class Cluster extends ClusterBase { super(scope, id); this.vpc = props.vpc; - this.vpcSubnets = props.vpcSubnets ? props.vpcSubnets : { + this.vpcSubnets = props.vpcSubnets ?? { subnetType: ec2.SubnetType.PRIVATE, }; - const removalPolicy = props.removalPolicy ? props.removalPolicy : RemovalPolicy.RETAIN; + const removalPolicy = props.removalPolicy ?? RemovalPolicy.RETAIN; const subnetGroup = props.subnetGroup ?? new ClusterSubnetGroup(this, 'Subnets', { description: `Subnets for ${id} Redshift cluster`, @@ -419,11 +419,10 @@ export class Cluster extends ClusterBase { removalPolicy: removalPolicy, }); - const securityGroups = props.securityGroups !== undefined ? - props.securityGroups : [new ec2.SecurityGroup(this, 'SecurityGroup', { - description: 'Redshift security group', - vpc: this.vpc, - })]; + const securityGroups = props.securityGroups ?? [new ec2.SecurityGroup(this, 'SecurityGroup', { + description: 'Redshift security group', + vpc: this.vpc, + })]; const securityGroupIds = securityGroups.map(sg => sg.securityGroupId); @@ -464,22 +463,20 @@ export class Cluster extends ClusterBase { port: props.port, clusterParameterGroupName: props.parameterGroup && props.parameterGroup.clusterParameterGroupName, // Admin - masterUsername: secret ? secret.secretValueFromJson('username').toString() : props.masterUser.masterUsername, - masterUserPassword: secret - ? secret.secretValueFromJson('password').toString() - : (props.masterUser.masterPassword - ? props.masterUser.masterPassword.toString() - : 'default'), + masterUsername: secret?.secretValueFromJson('username').toString() ?? props.masterUser.masterUsername, + masterUserPassword: secret?.secretValueFromJson('password').toString() + ?? props.masterUser.masterPassword?.toString() + ?? 'default', preferredMaintenanceWindow: props.preferredMaintenanceWindow, nodeType: props.nodeType || NodeType.DC2_LARGE, numberOfNodes: nodeCount, loggingProperties, - iamRoles: props.roles ? props.roles.map(role => role.roleArn) : undefined, + iamRoles: props?.roles?.map(role => role.roleArn), dbName: props.defaultDatabaseName || 'default_db', publiclyAccessible: props.publiclyAccessible || false, // Encryption kmsKeyId: props.encryptionKey && props.encryptionKey.keyArn, - encrypted: props.encrypted !== undefined ? props.encrypted : true, + encrypted: props.encrypted ?? true, }); cluster.applyRemovalPolicy(removalPolicy, { diff --git a/packages/@aws-cdk/aws-route53/lib/util.ts b/packages/@aws-cdk/aws-route53/lib/util.ts index a316e2ecd59f9..d703c77348538 100644 --- a/packages/@aws-cdk/aws-route53/lib/util.ts +++ b/packages/@aws-cdk/aws-route53/lib/util.ts @@ -1,6 +1,10 @@ -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { IHostedZone } from './hosted-zone-ref'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Validates a zone name is valid by Route53 specifc naming rules, * and that there is no trailing dot in the name. diff --git a/packages/@aws-cdk/aws-s3-assets/README.md b/packages/@aws-cdk/aws-s3-assets/README.md index 3d508070d6a50..aab4c46d9c44d 100644 --- a/packages/@aws-cdk/aws-s3-assets/README.md +++ b/packages/@aws-cdk/aws-s3-assets/README.md @@ -115,6 +115,7 @@ new assets.Asset(this, 'BundledAsset', { }, // Docker bundling fallback image: BundlingDockerImage.fromRegistry('alpine'), + entrypoint: ['/bin/sh', '-c'], command: ['bundle'], }, }); diff --git a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts index bbf526b79df86..4ca33864952f3 100644 --- a/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts +++ b/packages/@aws-cdk/aws-s3-deployment/lib/bucket-deployment.ts @@ -227,7 +227,7 @@ export class BucketDeployment extends CoreConstruct { Prune: props.prune ?? true, UserMetadata: props.metadata ? mapUserMetadata(props.metadata) : undefined, SystemMetadata: mapSystemMetadata(props), - DistributionId: props.distribution ? props.distribution.distributionId : undefined, + DistributionId: props.distribution?.distributionId, DistributionPaths: props.distributionPaths, }, }); diff --git a/packages/@aws-cdk/aws-s3-notifications/lib/lambda.ts b/packages/@aws-cdk/aws-s3-notifications/lib/lambda.ts index 36ad917ec4ec4..183512a996e26 100644 --- a/packages/@aws-cdk/aws-s3-notifications/lib/lambda.ts +++ b/packages/@aws-cdk/aws-s3-notifications/lib/lambda.ts @@ -1,7 +1,11 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as s3 from '@aws-cdk/aws-s3'; -import { CfnResource, Construct, Names, Stack } from '@aws-cdk/core'; +import { CfnResource, Names, Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; /** * Use a Lambda function as a bucket notification destination diff --git a/packages/@aws-cdk/aws-s3/lib/bucket.ts b/packages/@aws-cdk/aws-s3/lib/bucket.ts index 5c5abfbfec559..525b22f6afd1d 100644 --- a/packages/@aws-cdk/aws-s3/lib/bucket.ts +++ b/packages/@aws-cdk/aws-s3/lib/bucket.ts @@ -413,7 +413,7 @@ abstract class BucketBase extends Resource implements IBucket { detailType: ['AWS API Call via CloudTrail'], detail: { resources: { - ARN: options.paths ? options.paths.map(p => this.arnForObjects(p)) : [this.bucketArn], + ARN: options.paths?.map(p => this.arnForObjects(p)) ?? [this.bucketArn], }, }, }); @@ -1607,13 +1607,13 @@ export class Bucket extends BucketBase { return { rules: this.lifecycleRules.map(parseLifecycleRule) }; function parseLifecycleRule(rule: LifecycleRule): CfnBucket.RuleProperty { - const enabled = rule.enabled !== undefined ? rule.enabled : true; + const enabled = rule.enabled ?? true; const x: CfnBucket.RuleProperty = { // eslint-disable-next-line max-len abortIncompleteMultipartUpload: rule.abortIncompleteMultipartUploadAfter !== undefined ? { daysAfterInitiation: rule.abortIncompleteMultipartUploadAfter.toDays() } : undefined, expirationDate: rule.expirationDate, - expirationInDays: rule.expiration && rule.expiration.toDays(), + expirationInDays: rule.expiration?.toDays(), id: rule.id, noncurrentVersionExpirationInDays: rule.noncurrentVersionExpiration && rule.noncurrentVersionExpiration.toDays(), noncurrentVersionTransitions: mapOrUndefined(rule.noncurrentVersionTransitions, t => ({ diff --git a/packages/@aws-cdk/aws-ses-actions/lib/bounce.ts b/packages/@aws-cdk/aws-ses-actions/lib/bounce.ts index b84c414c5c43e..a1a385651614a 100644 --- a/packages/@aws-cdk/aws-ses-actions/lib/bounce.ts +++ b/packages/@aws-cdk/aws-ses-actions/lib/bounce.ts @@ -100,7 +100,7 @@ export class Bounce implements ses.IReceiptRuleAction { sender: this.props.sender, smtpReplyCode: this.props.template.props.smtpReplyCode, message: this.props.template.props.message, - topicArn: this.props.topic ? this.props.topic.topicArn : undefined, + topicArn: this.props.topic?.topicArn, statusCode: this.props.template.props.statusCode, }, }; diff --git a/packages/@aws-cdk/aws-ses-actions/lib/lambda.ts b/packages/@aws-cdk/aws-ses-actions/lib/lambda.ts index 290211bf1806f..d6be68f92ce24 100644 --- a/packages/@aws-cdk/aws-ses-actions/lib/lambda.ts +++ b/packages/@aws-cdk/aws-ses-actions/lib/lambda.ts @@ -78,7 +78,7 @@ export class Lambda implements ses.IReceiptRuleAction { lambdaAction: { functionArn: this.props.function.functionArn, invocationType: this.props.invocationType, - topicArn: this.props.topic ? this.props.topic.topicArn : undefined, + topicArn: this.props.topic?.topicArn, }, }; } diff --git a/packages/@aws-cdk/aws-ses-actions/lib/s3.ts b/packages/@aws-cdk/aws-ses-actions/lib/s3.ts index 9be2fd8750378..1f288afe2a887 100644 --- a/packages/@aws-cdk/aws-ses-actions/lib/s3.ts +++ b/packages/@aws-cdk/aws-ses-actions/lib/s3.ts @@ -92,9 +92,9 @@ export class S3 implements ses.IReceiptRuleAction { return { s3Action: { bucketName: this.props.bucket.bucketName, - kmsKeyArn: this.props.kmsKey ? this.props.kmsKey.keyArn : undefined, + kmsKeyArn: this.props.kmsKey?.keyArn, objectKeyPrefix: this.props.objectKeyPrefix, - topicArn: this.props.topic ? this.props.topic.topicArn : undefined, + topicArn: this.props.topic?.topicArn, }, }; } diff --git a/packages/@aws-cdk/aws-ses-actions/lib/stop.ts b/packages/@aws-cdk/aws-ses-actions/lib/stop.ts index ab2e9cbb91d1e..f42d206d3d350 100644 --- a/packages/@aws-cdk/aws-ses-actions/lib/stop.ts +++ b/packages/@aws-cdk/aws-ses-actions/lib/stop.ts @@ -23,7 +23,7 @@ export class Stop implements ses.IReceiptRuleAction { return { stopAction: { scope: 'RuleSet', - topicArn: this.props.topic ? this.props.topic.topicArn : undefined, + topicArn: this.props.topic?.topicArn, }, }; } diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts b/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts index c94d4d4edd138..dcb3d011d4251 100644 --- a/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts +++ b/packages/@aws-cdk/aws-ses/lib/receipt-rule-set.ts @@ -62,7 +62,7 @@ abstract class ReceiptRuleSetBase extends Resource implements IReceiptRuleSet { */ public addRule(id: string, options?: ReceiptRuleOptions): ReceiptRule { this.lastAddedRule = new ReceiptRule(this, id, { - after: this.lastAddedRule ? this.lastAddedRule : undefined, + after: this.lastAddedRule ?? undefined, ruleSet: this, ...options, }); diff --git a/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts b/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts index 72c056d913693..5b6a276929c8e 100644 --- a/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts +++ b/packages/@aws-cdk/aws-ses/lib/receipt-rule.ts @@ -124,10 +124,10 @@ export class ReceiptRule extends Resource implements IReceiptRule { }); const resource = new CfnReceiptRule(this, 'Resource', { - after: props.after ? props.after.receiptRuleName : undefined, + after: props.after?.receiptRuleName, rule: { actions: Lazy.any({ produce: () => this.renderActions() }), - enabled: props.enabled === undefined ? true : props.enabled, + enabled: props.enabled ?? true, name: this.physicalName, recipients: props.recipients, scanEnabled: props.scanEnabled, diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts index 3ecab463d2c2c..aa7581653d5ba 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts @@ -1,9 +1,13 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; -import { Construct, Names, Stack } from '@aws-cdk/core'; +import { Names, Stack } from '@aws-cdk/core'; import { SubscriptionProps } from './subscription'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Properties for a Lambda subscription */ diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts index bac6a13859d9c..4a41c5e43feb2 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts @@ -1,9 +1,13 @@ import * as iam from '@aws-cdk/aws-iam'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; -import { Construct, Names, Stack } from '@aws-cdk/core'; +import { Names, Stack } from '@aws-cdk/core'; import { SubscriptionProps } from './subscription'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Properties for an SQS subscription */ diff --git a/packages/@aws-cdk/aws-sns/lib/subscriber.ts b/packages/@aws-cdk/aws-sns/lib/subscriber.ts index edfd3632d415a..d63667dde46e5 100644 --- a/packages/@aws-cdk/aws-sns/lib/subscriber.ts +++ b/packages/@aws-cdk/aws-sns/lib/subscriber.ts @@ -1,7 +1,11 @@ -import { Construct } from '@aws-cdk/core'; + import { SubscriptionOptions } from './subscription'; import { ITopic } from './topic-base'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Subscription configuration */ diff --git a/packages/@aws-cdk/aws-sns/lib/topic-base.ts b/packages/@aws-cdk/aws-sns/lib/topic-base.ts index bddfde6288748..db262ef87e068 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-base.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-base.ts @@ -1,9 +1,13 @@ import * as iam from '@aws-cdk/aws-iam'; -import { Construct, IResource, Resource, Token } from '@aws-cdk/core'; +import { IResource, Resource, Token } from '@aws-cdk/core'; import { TopicPolicy } from './policy'; import { ITopicSubscription } from './subscriber'; import { Subscription } from './subscription'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Represents an SNS topic */ diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts index 4b3ecd5ca0013..2e38d60aac150 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/lib/emr/emr-create-cluster.ts @@ -173,7 +173,7 @@ export class EmrCreateCluster extends sfn.TaskStateBase { constructor(scope: Construct, id: string, private readonly props: EmrCreateClusterProps) { super(scope, id, props); - this.visibleToAllUsers = this.props.visibleToAllUsers !== undefined ? this.props.visibleToAllUsers : true; + this.visibleToAllUsers = this.props.visibleToAllUsers ?? true; this.integrationPattern = props.integrationPattern || sfn.IntegrationPattern.RUN_JOB; validatePatternSupported(this.integrationPattern, EmrCreateCluster.SUPPORTED_INTEGRATION_PATTERNS); diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts index 56b14e69fa5d8..8c7ee585e4b09 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts @@ -380,11 +380,11 @@ export class StateMachine extends StateMachineBase { const graph = new StateGraph(props.definition.startState, `State Machine ${id} definition`); graph.timeout = props.timeout; - this.stateMachineType = props.stateMachineType ? props.stateMachineType : StateMachineType.STANDARD; + this.stateMachineType = props.stateMachineType ?? StateMachineType.STANDARD; const resource = new CfnStateMachine(this, 'Resource', { stateMachineName: this.physicalName, - stateMachineType: props.stateMachineType ? props.stateMachineType : undefined, + stateMachineType: props.stateMachineType ?? undefined, roleArn: this.role.roleArn, definitionString: Stack.of(this).toJsonString(graph.toGraphJson()), loggingConfiguration: props.logs ? this.buildLoggingConfiguration(props.logs) : undefined, diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts index de9b4d16aab01..fb5e52d2831b2 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/pass.ts @@ -145,7 +145,7 @@ export class Pass extends State implements INextable { return { Type: StateType.PASS, Comment: this.comment, - Result: this.result ? this.result.value : undefined, + Result: this.result?.value, ResultPath: renderJsonPath(this.resultPath), ...this.renderInputOutput(), ...this.renderParameters(), diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts index 02cceb6e64ed2..42a4c8e9bf1b5 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts @@ -254,7 +254,7 @@ export abstract class State extends CoreConstruct implements IChainable { this.retries.push({ ...props, - errors: props.errors ? props.errors : [Errors.ALL], + errors: props.errors ?? [Errors.ALL], }); } @@ -268,7 +268,7 @@ export abstract class State extends CoreConstruct implements IChainable { this.catches.push({ next: handler, props: { - errors: props.errors ? props.errors : [Errors.ALL], + errors: props.errors ?? [Errors.ALL], resultPath: props.resultPath, }, }); @@ -352,7 +352,7 @@ export abstract class State extends CoreConstruct implements IChainable { protected renderChoices(): any { return { Choices: renderList(this.choices, renderChoice), - Default: this.defaultChoice ? this.defaultChoice.stateId : undefined, + Default: this.defaultChoice?.stateId, }; } diff --git a/packages/@aws-cdk/cfnspec/CHANGELOG.md b/packages/@aws-cdk/cfnspec/CHANGELOG.md index bf8b43b6865e2..2d117eebb4978 100644 --- a/packages/@aws-cdk/cfnspec/CHANGELOG.md +++ b/packages/@aws-cdk/cfnspec/CHANGELOG.md @@ -1,3 +1,87 @@ +# CloudFormation Resource Specification v26.0.0 + +## New Resource Types + +* AWS::LookoutVision::Project +* AWS::SageMaker::FeatureGroup + +## Attribute Changes + +* AWS::MediaConnect::FlowVpcInterface FlowArn (__deleted__) +* AWS::MediaConnect::FlowVpcInterface Name (__deleted__) +* AWS::S3::AccessPoint NetworkOrigin (__added__) +* AWS::S3::AccessPoint PolicyStatus (__added__) + +## Property Changes + +* AWS::ACMPCA::Certificate ApiPassthrough (__added__) +* AWS::ACMPCA::Certificate ValidityNotBefore (__added__) +* AWS::AmazonMQ::Configuration AuthenticationStrategy (__added__) +* AWS::ApiGatewayV2::Stage AccessPolicyId (__added__) +* AWS::ECS::Cluster Configuration (__added__) +* AWS::Kinesis::Stream Tags.DuplicatesAllowed (__changed__) + * Old: false + * New: true +* AWS::MediaConnect::FlowVpcInterface FlowArn (__added__) +* AWS::MediaConnect::FlowVpcInterface Name (__added__) +* AWS::S3::AccessPoint NetworkOrigin (__deleted__) +* AWS::S3::AccessPoint PolicyStatus (__deleted__) +* AWS::SSM::MaintenanceWindowTask MaxConcurrency.Required (__changed__) + * Old: true + * New: false +* AWS::SSM::MaintenanceWindowTask MaxErrors.Required (__changed__) + * Old: true + * New: false +* AWS::SSM::MaintenanceWindowTask Targets.Required (__changed__) + * Old: true + * New: false +* AWS::SSO::InstanceAccessControlAttributeConfiguration InstanceAccessControlAttributeConfiguration (__deleted__) +* AWS::SageMaker::Device Tags.ItemType (__changed__) + * Old: Json + * New: Tag +* AWS::SageMaker::Device Tags.Type (__changed__) + * Old: Tag + * New: List +* AWS::SageMaker::DeviceFleet Tags.ItemType (__changed__) + * Old: Json + * New: Tag +* AWS::SageMaker::DeviceFleet Tags.Type (__changed__) + * Old: Tag + * New: List +* AWS::SageMaker::Model InferenceExecutionConfig (__added__) + +## Property Type Changes + +* AWS::ACMPCA::Certificate.ApiPassthrough (__added__) +* AWS::ACMPCA::Certificate.CertificatePolicyList (__added__) +* AWS::ACMPCA::Certificate.EdiPartyName (__added__) +* AWS::ACMPCA::Certificate.ExtendedKeyUsage (__added__) +* AWS::ACMPCA::Certificate.ExtendedKeyUsageList (__added__) +* AWS::ACMPCA::Certificate.Extensions (__added__) +* AWS::ACMPCA::Certificate.GeneralName (__added__) +* AWS::ACMPCA::Certificate.GeneralNameList (__added__) +* AWS::ACMPCA::Certificate.KeyUsage (__added__) +* AWS::ACMPCA::Certificate.OtherName (__added__) +* AWS::ACMPCA::Certificate.PolicyInformation (__added__) +* AWS::ACMPCA::Certificate.PolicyQualifierInfo (__added__) +* AWS::ACMPCA::Certificate.PolicyQualifierInfoList (__added__) +* AWS::ACMPCA::Certificate.Qualifier (__added__) +* AWS::ACMPCA::Certificate.Subject (__added__) +* AWS::AppFlow::Flow.IdFieldNamesList (__added__) +* AWS::ECS::Cluster.ClusterConfiguration (__added__) +* AWS::ECS::Cluster.ExecuteCommandConfiguration (__added__) +* AWS::ECS::Cluster.ExecuteCommandLogConfiguration (__added__) +* AWS::SageMaker::Model.InferenceExecutionConfig (__added__) +* AWS::AppFlow::Flow.SalesforceDestinationProperties IdFieldNames (__added__) +* AWS::AppFlow::Flow.SalesforceDestinationProperties WriteOperationType (__added__) +* AWS::DLM::LifecyclePolicy.CreateRule Location (__added__) +* AWS::DLM::LifecyclePolicy.CrossRegionCopyRule Target (__added__) +* AWS::DLM::LifecyclePolicy.CrossRegionCopyRule TargetRegion.Required (__changed__) + * Old: true + * New: false +* AWS::DLM::LifecyclePolicy.PolicyDetails ResourceLocations (__added__) + + # CloudFormation Resource Specification v24.0.0 ## New Resource Types diff --git a/packages/@aws-cdk/cfnspec/cfn.version b/packages/@aws-cdk/cfnspec/cfn.version index 51105aade994f..1e212a919f492 100644 --- a/packages/@aws-cdk/cfnspec/cfn.version +++ b/packages/@aws-cdk/cfnspec/cfn.version @@ -1 +1 @@ -24.0.0 +26.0.0 diff --git a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json index 909704a8ddc33..9391cf7efc80d 100644 --- a/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json +++ b/packages/@aws-cdk/cfnspec/spec-source/000_CloudFormationResourceSpecification.json @@ -1,5 +1,396 @@ { "PropertyTypes": { + "AWS::ACMPCA::Certificate.ApiPassthrough": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-apipassthrough.html", + "Properties": { + "Extensions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-apipassthrough.html#cfn-acmpca-certificate-apipassthrough-extensions", + "Required": false, + "Type": "Extensions", + "UpdateType": "Immutable" + }, + "Subject": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-apipassthrough.html#cfn-acmpca-certificate-apipassthrough-subject", + "Required": false, + "Type": "Subject", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.CertificatePolicyList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-certificatepolicylist.html", + "Properties": { + "CertificatePolicyList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-certificatepolicylist.html#cfn-acmpca-certificate-certificatepolicylist-certificatepolicylist", + "ItemType": "PolicyInformation", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.EdiPartyName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-edipartyname.html", + "Properties": { + "NameAssigner": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-edipartyname.html#cfn-acmpca-certificate-edipartyname-nameassigner", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PartyName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-edipartyname.html#cfn-acmpca-certificate-edipartyname-partyname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.ExtendedKeyUsage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extendedkeyusage.html", + "Properties": { + "ExtendedKeyUsageObjectIdentifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extendedkeyusage.html#cfn-acmpca-certificate-extendedkeyusage-extendedkeyusageobjectidentifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "ExtendedKeyUsageType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extendedkeyusage.html#cfn-acmpca-certificate-extendedkeyusage-extendedkeyusagetype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.ExtendedKeyUsageList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extendedkeyusagelist.html", + "Properties": { + "ExtendedKeyUsageList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extendedkeyusagelist.html#cfn-acmpca-certificate-extendedkeyusagelist-extendedkeyusagelist", + "ItemType": "ExtendedKeyUsage", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.Extensions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extensions.html", + "Properties": { + "CertificatePolicies": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extensions.html#cfn-acmpca-certificate-extensions-certificatepolicies", + "Required": false, + "Type": "CertificatePolicyList", + "UpdateType": "Immutable" + }, + "ExtendedKeyUsage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extensions.html#cfn-acmpca-certificate-extensions-extendedkeyusage", + "Required": false, + "Type": "ExtendedKeyUsageList", + "UpdateType": "Immutable" + }, + "KeyUsage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extensions.html#cfn-acmpca-certificate-extensions-keyusage", + "Required": false, + "Type": "KeyUsage", + "UpdateType": "Immutable" + }, + "SubjectAlternativeNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-extensions.html#cfn-acmpca-certificate-extensions-subjectalternativenames", + "Required": false, + "Type": "GeneralNameList", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.GeneralName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html", + "Properties": { + "DirectoryName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-directoryname", + "Required": false, + "Type": "Subject", + "UpdateType": "Immutable" + }, + "DnsName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-dnsname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "EdiPartyName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-edipartyname", + "Required": false, + "Type": "EdiPartyName", + "UpdateType": "Immutable" + }, + "IpAddress": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-ipaddress", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "OtherName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-othername", + "Required": false, + "Type": "OtherName", + "UpdateType": "Immutable" + }, + "RegisteredId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-registeredid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Rfc822Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-rfc822name", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "UniformResourceIdentifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalname.html#cfn-acmpca-certificate-generalname-uniformresourceidentifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.GeneralNameList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalnamelist.html", + "Properties": { + "GeneralNameList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-generalnamelist.html#cfn-acmpca-certificate-generalnamelist-generalnamelist", + "ItemType": "GeneralName", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.KeyUsage": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html", + "Properties": { + "CRLSign": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-crlsign", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "DataEncipherment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-dataencipherment", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "DecipherOnly": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-decipheronly", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "DigitalSignature": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-digitalsignature", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "EncipherOnly": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-encipheronly", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "KeyAgreement": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-keyagreement", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "KeyCertSign": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-keycertsign", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "KeyEncipherment": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-keyencipherment", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + }, + "NonRepudiation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-keyusage.html#cfn-acmpca-certificate-keyusage-nonrepudiation", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.OtherName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-othername.html", + "Properties": { + "TypeId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-othername.html#cfn-acmpca-certificate-othername-typeid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Value": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-othername.html#cfn-acmpca-certificate-othername-value", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.PolicyInformation": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyinformation.html", + "Properties": { + "CertPolicyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyinformation.html#cfn-acmpca-certificate-policyinformation-certpolicyid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "PolicyQualifiers": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyinformation.html#cfn-acmpca-certificate-policyinformation-policyqualifiers", + "Required": false, + "Type": "PolicyQualifierInfoList", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.PolicyQualifierInfo": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyqualifierinfo.html", + "Properties": { + "PolicyQualifierId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyqualifierinfo.html#cfn-acmpca-certificate-policyqualifierinfo-policyqualifierid", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Qualifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyqualifierinfo.html#cfn-acmpca-certificate-policyqualifierinfo-qualifier", + "Required": true, + "Type": "Qualifier", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.PolicyQualifierInfoList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyqualifierinfolist.html", + "Properties": { + "PolicyQualifierInfoList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-policyqualifierinfolist.html#cfn-acmpca-certificate-policyqualifierinfolist-policyqualifierinfolist", + "ItemType": "PolicyQualifierInfo", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.Qualifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-qualifier.html", + "Properties": { + "CpsUri": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-qualifier.html#cfn-acmpca-certificate-qualifier-cpsuri", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, + "AWS::ACMPCA::Certificate.Subject": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html", + "Properties": { + "CommonName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-commonname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Country": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-country", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "DistinguishedNameQualifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-distinguishednamequalifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "GenerationQualifier": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-generationqualifier", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "GivenName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-givenname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Initials": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-initials", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Locality": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-locality", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Organization": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-organization", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "OrganizationalUnit": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-organizationalunit", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Pseudonym": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-pseudonym", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "SerialNumber": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-serialnumber", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "State": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-state", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Surname": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-surname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Title": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-subject.html#cfn-acmpca-certificate-subject-title", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + } + } + }, "AWS::ACMPCA::Certificate.Validity": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-acmpca-certificate-validity.html", "Properties": { @@ -3033,6 +3424,18 @@ } } }, + "AWS::AppFlow::Flow.IdFieldNamesList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-idfieldnameslist.html", + "Properties": { + "IdFieldNamesList": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-idfieldnameslist.html#cfn-appflow-flow-idfieldnameslist-idfieldnameslist", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + } + } + }, "AWS::AppFlow::Flow.IncrementalPullConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-incrementalpullconfig.html", "Properties": { @@ -3184,11 +3587,23 @@ "Type": "ErrorHandlingConfig", "UpdateType": "Mutable" }, + "IdFieldNames": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-salesforcedestinationproperties.html#cfn-appflow-flow-salesforcedestinationproperties-idfieldnames", + "Required": false, + "Type": "IdFieldNamesList", + "UpdateType": "Mutable" + }, "Object": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-salesforcedestinationproperties.html#cfn-appflow-flow-salesforcedestinationproperties-object", "PrimitiveType": "String", "Required": true, "UpdateType": "Mutable" + }, + "WriteOperationType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appflow-flow-salesforcedestinationproperties.html#cfn-appflow-flow-salesforcedestinationproperties-writeoperationtype", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" } } }, @@ -12981,6 +13396,12 @@ "Required": false, "UpdateType": "Mutable" }, + "Location": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-createrule.html#cfn-dlm-lifecyclepolicy-createrule-location", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "Times": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-createrule.html#cfn-dlm-lifecyclepolicy-createrule-times", "PrimitiveItemType": "String", @@ -13057,10 +13478,16 @@ "Type": "CrossRegionCopyRetainRule", "UpdateType": "Mutable" }, + "Target": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-target", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "TargetRegion": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-crossregioncopyrule.html#cfn-dlm-lifecyclepolicy-crossregioncopyrule-targetregion", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Mutable" } } @@ -13198,6 +13625,13 @@ "Required": false, "UpdateType": "Mutable" }, + "ResourceLocations": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-policydetails.html#cfn-dlm-lifecyclepolicy-policydetails-resourcelocations", + "PrimitiveItemType": "String", + "Required": false, + "Type": "List", + "UpdateType": "Mutable" + }, "ResourceTypes": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dlm-lifecyclepolicy-policydetails.html#cfn-dlm-lifecyclepolicy-policydetails-resourcetypes", "PrimitiveItemType": "String", @@ -18020,6 +18454,17 @@ } } }, + "AWS::ECS::Cluster.ClusterConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-clusterconfiguration.html", + "Properties": { + "ExecuteCommandConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-clusterconfiguration.html#cfn-ecs-cluster-clusterconfiguration-executecommandconfiguration", + "Required": false, + "Type": "ExecuteCommandConfiguration", + "UpdateType": "Mutable" + } + } + }, "AWS::ECS::Cluster.ClusterSettings": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-clustersettings.html", "Properties": { @@ -18037,6 +18482,64 @@ } } }, + "AWS::ECS::Cluster.ExecuteCommandConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html", + "Properties": { + "KmsKeyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html#cfn-ecs-cluster-executecommandconfiguration-kmskeyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "LogConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html#cfn-ecs-cluster-executecommandconfiguration-logconfiguration", + "Required": false, + "Type": "ExecuteCommandLogConfiguration", + "UpdateType": "Mutable" + }, + "Logging": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandconfiguration.html#cfn-ecs-cluster-executecommandconfiguration-logging", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, + "AWS::ECS::Cluster.ExecuteCommandLogConfiguration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html", + "Properties": { + "CloudWatchEncryptionEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html#cfn-ecs-cluster-executecommandlogconfiguration-cloudwatchencryptionenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "CloudWatchLogGroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html#cfn-ecs-cluster-executecommandlogconfiguration-cloudwatchloggroupname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "S3BucketName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html#cfn-ecs-cluster-executecommandlogconfiguration-s3bucketname", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, + "S3EncryptionEnabled": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html#cfn-ecs-cluster-executecommandlogconfiguration-s3encryptionenabled", + "PrimitiveType": "Boolean", + "Required": false, + "UpdateType": "Mutable" + }, + "S3KeyPrefix": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-cluster-executecommandlogconfiguration.html#cfn-ecs-cluster-executecommandlogconfiguration-s3keyprefix", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + } + } + }, "AWS::ECS::Service.AwsVpcConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-awsvpcconfiguration.html", "Properties": { @@ -47726,6 +48229,23 @@ } } }, + "AWS::SageMaker::FeatureGroup.FeatureDefinition": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-featuregroup-featuredefinition.html", + "Properties": { + "FeatureName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-featuregroup-featuredefinition.html#cfn-sagemaker-featuregroup-featuredefinition-featurename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "FeatureType": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-featuregroup-featuredefinition.html#cfn-sagemaker-featuregroup-featuredefinition-featuretype", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::SageMaker::Model.ContainerDefinition": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition.html", "Properties": { @@ -47790,6 +48310,17 @@ } } }, + "AWS::SageMaker::Model.InferenceExecutionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-inferenceexecutionconfig.html", + "Properties": { + "Mode": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-inferenceexecutionconfig.html#cfn-sagemaker-model-inferenceexecutionconfig-mode", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::SageMaker::Model.MultiModelConfig": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition-multimodelconfig.html", "Properties": { @@ -51929,7 +52460,7 @@ } } }, - "ResourceSpecificationVersion": "24.0.0", + "ResourceSpecificationVersion": "26.0.0", "ResourceTypes": { "AWS::ACMPCA::Certificate": { "Attributes": { @@ -51942,6 +52473,12 @@ }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificate.html", "Properties": { + "ApiPassthrough": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificate.html#cfn-acmpca-certificate-apipassthrough", + "Required": false, + "Type": "ApiPassthrough", + "UpdateType": "Immutable" + }, "CertificateAuthorityArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificate.html#cfn-acmpca-certificate-certificateauthorityarn", "PrimitiveType": "String", @@ -51971,6 +52508,12 @@ "Required": true, "Type": "Validity", "UpdateType": "Immutable" + }, + "ValidityNotBefore": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-acmpca-certificate.html#cfn-acmpca-certificate-validitynotbefore", + "Required": false, + "Type": "Validity", + "UpdateType": "Immutable" } } }, @@ -52267,6 +52810,12 @@ }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-amazonmq-configuration.html", "Properties": { + "AuthenticationStrategy": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-amazonmq-configuration.html#cfn-amazonmq-configuration-authenticationstrategy", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, "Data": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-amazonmq-configuration.html#cfn-amazonmq-configuration-data", "PrimitiveType": "String", @@ -54041,6 +54590,12 @@ "Type": "AccessLogSettings", "UpdateType": "Mutable" }, + "AccessPolicyId": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-stage.html#cfn-apigatewayv2-stage-accesspolicyid", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Mutable" + }, "ApiId": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-stage.html#cfn-apigatewayv2-stage-apiid", "PrimitiveType": "String", @@ -64723,6 +65278,12 @@ "Type": "List", "UpdateType": "Mutable" }, + "Configuration": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html#cfn-ecs-cluster-configuration", + "Required": false, + "Type": "ClusterConfiguration", + "UpdateType": "Mutable" + }, "DefaultCapacityProviderStrategy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html#cfn-ecs-cluster-defaultcapacityproviderstrategy", "ItemType": "CapacityProviderStrategyItem", @@ -72208,7 +72769,7 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kinesis-stream.html#cfn-kinesis-stream-tags", - "DuplicatesAllowed": false, + "DuplicatesAllowed": true, "ItemType": "Tag", "Required": false, "Type": "List", @@ -73334,6 +73895,22 @@ } } }, + "AWS::LookoutVision::Project": { + "Attributes": { + "Arn": { + "PrimitiveType": "String" + } + }, + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutvision-project.html", + "Properties": { + "ProjectName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutvision-project.html#cfn-lookoutvision-project-projectname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + } + } + }, "AWS::MSK::Cluster": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html", "Properties": { @@ -74013,12 +74590,6 @@ }, "AWS::MediaConnect::FlowVpcInterface": { "Attributes": { - "FlowArn": { - "PrimitiveType": "String" - }, - "Name": { - "PrimitiveType": "String" - }, "NetworkInterfaceIds": { "PrimitiveItemType": "String", "Type": "List" @@ -74026,6 +74597,18 @@ }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediaconnect-flowvpcinterface.html", "Properties": { + "FlowArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediaconnect-flowvpcinterface.html#cfn-mediaconnect-flowvpcinterface-flowarn", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "Name": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediaconnect-flowvpcinterface.html#cfn-mediaconnect-flowvpcinterface-name", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, "RoleArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-mediaconnect-flowvpcinterface.html#cfn-mediaconnect-flowvpcinterface-rolearn", "PrimitiveType": "String", @@ -79615,6 +80198,14 @@ } }, "AWS::S3::AccessPoint": { + "Attributes": { + "NetworkOrigin": { + "PrimitiveType": "String" + }, + "PolicyStatus": { + "PrimitiveType": "Json" + } + }, "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html", "Properties": { "Bucket": { @@ -79629,24 +80220,12 @@ "Required": false, "UpdateType": "Mutable" }, - "NetworkOrigin": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-networkorigin", - "PrimitiveType": "String", - "Required": false, - "UpdateType": "Mutable" - }, "Policy": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policy", "PrimitiveType": "Json", "Required": false, "UpdateType": "Mutable" }, - "PolicyStatus": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-policystatus", - "PrimitiveType": "Json", - "Required": false, - "UpdateType": "Mutable" - }, "PublicAccessBlockConfiguration": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-accesspoint.html#cfn-s3-accesspoint-publicaccessblockconfiguration", "Required": false, @@ -80454,13 +81033,13 @@ "MaxConcurrency": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtask.html#cfn-ssm-maintenancewindowtask-maxconcurrency", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Mutable" }, "MaxErrors": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtask.html#cfn-ssm-maintenancewindowtask-maxerrors", "PrimitiveType": "String", - "Required": true, + "Required": false, "UpdateType": "Mutable" }, "Name": { @@ -80484,7 +81063,7 @@ "Targets": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ssm-maintenancewindowtask.html#cfn-ssm-maintenancewindowtask-targets", "ItemType": "Target", - "Required": true, + "Required": false, "Type": "List", "UpdateType": "Mutable" }, @@ -80785,12 +81364,6 @@ "Type": "List", "UpdateType": "Mutable" }, - "InstanceAccessControlAttributeConfiguration": { - "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sso-instanceaccesscontrolattributeconfiguration.html#cfn-sso-instanceaccesscontrolattributeconfiguration-instanceaccesscontrolattributeconfiguration", - "PrimitiveType": "Json", - "Required": false, - "UpdateType": "Mutable" - }, "InstanceArn": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sso-instanceaccesscontrolattributeconfiguration.html#cfn-sso-instanceaccesscontrolattributeconfiguration-instancearn", "PrimitiveType": "String", @@ -80973,9 +81546,9 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-device.html#cfn-sagemaker-device-tags", - "ItemType": "Json", + "ItemType": "Tag", "Required": false, - "Type": "Tag", + "Type": "List", "UpdateType": "Mutable" } } @@ -81009,9 +81582,9 @@ }, "Tags": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-devicefleet.html#cfn-sagemaker-devicefleet-tags", - "ItemType": "Json", + "ItemType": "Tag", "Required": false, - "Type": "Tag", + "Type": "List", "UpdateType": "Mutable" } } @@ -81106,6 +81679,69 @@ } } }, + "AWS::SageMaker::FeatureGroup": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html", + "Properties": { + "Description": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-description", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "EventTimeFeatureName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-eventtimefeaturename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "FeatureDefinitions": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-featuredefinitions", + "DuplicatesAllowed": true, + "ItemType": "FeatureDefinition", + "Required": true, + "Type": "List", + "UpdateType": "Immutable" + }, + "FeatureGroupName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-featuregroupname", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "OfflineStoreConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-offlinestoreconfig", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Immutable" + }, + "OnlineStoreConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-onlinestoreconfig", + "PrimitiveType": "Json", + "Required": false, + "UpdateType": "Immutable" + }, + "RecordIdentifierFeatureName": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-recordidentifierfeaturename", + "PrimitiveType": "String", + "Required": true, + "UpdateType": "Immutable" + }, + "RoleArn": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-rolearn", + "PrimitiveType": "String", + "Required": false, + "UpdateType": "Immutable" + }, + "Tags": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-featuregroup.html#cfn-sagemaker-featuregroup-tags", + "DuplicatesAllowed": true, + "ItemType": "Tag", + "Required": false, + "Type": "List", + "UpdateType": "Immutable" + } + } + }, "AWS::SageMaker::Model": { "Attributes": { "ModelName": { @@ -81133,6 +81769,12 @@ "Required": true, "UpdateType": "Immutable" }, + "InferenceExecutionConfig": { + "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-model.html#cfn-sagemaker-model-inferenceexecutionconfig", + "Required": false, + "Type": "InferenceExecutionConfig", + "UpdateType": "Immutable" + }, "ModelName": { "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sagemaker-model.html#cfn-sagemaker-model-modelname", "PrimitiveType": "String", diff --git a/packages/@aws-cdk/cloudformation-include/package.json b/packages/@aws-cdk/cloudformation-include/package.json index 6a3dd4c17346d..1732065bd3a96 100644 --- a/packages/@aws-cdk/cloudformation-include/package.json +++ b/packages/@aws-cdk/cloudformation-include/package.json @@ -159,6 +159,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", "@aws-cdk/aws-managedblockchain": "0.0.0", "@aws-cdk/aws-mediaconnect": "0.0.0", @@ -306,6 +307,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", + "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", "@aws-cdk/aws-managedblockchain": "0.0.0", "@aws-cdk/aws-mediaconnect": "0.0.0", diff --git a/packages/@aws-cdk/core/lib/app.ts b/packages/@aws-cdk/core/lib/app.ts index eb095f801ee4f..28e04607a0228 100644 --- a/packages/@aws-cdk/core/lib/app.ts +++ b/packages/@aws-cdk/core/lib/app.ts @@ -116,7 +116,7 @@ export class App extends Stage { this.node.setContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT, analyticsReporting); } - const autoSynth = props.autoSynth !== undefined ? props.autoSynth : cxapi.OUTDIR_ENV in process.env; + const autoSynth = props.autoSynth ?? cxapi.OUTDIR_ENV in process.env; if (autoSynth) { // synth() guarantuees it will only execute once, so a default of 'true' // doesn't bite manual calling of the function. diff --git a/packages/@aws-cdk/core/lib/arn.ts b/packages/@aws-cdk/core/lib/arn.ts index 2ed3485a6c9b6..1ffcc2da2b33c 100644 --- a/packages/@aws-cdk/core/lib/arn.ts +++ b/packages/@aws-cdk/core/lib/arn.ts @@ -77,10 +77,10 @@ export class Arn { * can be 'undefined'. */ public static format(components: ArnComponents, stack: Stack): string { - const partition = components.partition !== undefined ? components.partition : stack.partition; - const region = components.region !== undefined ? components.region : stack.region; - const account = components.account !== undefined ? components.account : stack.account; - const sep = components.sep !== undefined ? components.sep : '/'; + const partition = components.partition ?? stack.partition; + const region = components.region ?? stack.region; + const account = components.account ?? stack.account; + const sep = components.sep ?? '/'; const values = ['arn', ':', partition, ':', components.service, ':', region, ':', account, ':', components.resource]; diff --git a/packages/@aws-cdk/core/lib/bundling.ts b/packages/@aws-cdk/core/lib/bundling.ts index c9a9c07e77f34..b1247fd913ea0 100644 --- a/packages/@aws-cdk/core/lib/bundling.ts +++ b/packages/@aws-cdk/core/lib/bundling.ts @@ -13,6 +13,17 @@ export interface BundlingOptions { */ readonly image: BundlingDockerImage; + /** + * The entrypoint to run in the Docker container. + * + * @example ['/bin/sh', '-c'] + * + * @see https://docs.docker.com/engine/reference/builder/#entrypoint + * + * @default - run the entrypoint defined in the image + */ + readonly entrypoint?: string[]; + /** * The command to run in the Docker container. * @@ -152,7 +163,15 @@ export class BundlingDockerImage { public run(options: DockerRunOptions = {}) { const volumes = options.volumes || []; const environment = options.environment || {}; - const command = options.command || []; + const entrypoint = options.entrypoint?.[0] || null; + const command = [ + ...options.entrypoint?.[1] + ? [...options.entrypoint.slice(1)] + : [], + ...options.command + ? [...options.command] + : [], + ]; const dockerArgs: string[] = [ 'run', '--rm', @@ -164,6 +183,9 @@ export class BundlingDockerImage { ...options.workingDirectory ? ['-w', options.workingDirectory] : [], + ...entrypoint + ? ['--entrypoint', entrypoint] + : [], this.image, ...command, ]; @@ -238,6 +260,13 @@ export enum DockerVolumeConsistency { * Docker run options */ export interface DockerRunOptions { + /** + * The entrypoint to run in the container. + * + * @default - run the entrypoint defined in the image + */ + readonly entrypoint?: string[]; + /** * The command to run in the container. * diff --git a/packages/@aws-cdk/core/lib/fs/copy.ts b/packages/@aws-cdk/core/lib/fs/copy.ts index b9feb555c8f65..627dc2aa988dc 100644 --- a/packages/@aws-cdk/core/lib/fs/copy.ts +++ b/packages/@aws-cdk/core/lib/fs/copy.ts @@ -5,7 +5,7 @@ import { CopyOptions, SymlinkFollowMode } from './options'; import { shouldFollow } from './utils'; export function copyDirectory(srcDir: string, destDir: string, options: CopyOptions = { }, rootDir?: string) { - const follow = options.follow !== undefined ? options.follow : SymlinkFollowMode.EXTERNAL; + const follow = options.follow ?? SymlinkFollowMode.EXTERNAL; rootDir = rootDir || srcDir; diff --git a/packages/@aws-cdk/core/lib/names.ts b/packages/@aws-cdk/core/lib/names.ts index 03998fcebe902..2d204c298d9fe 100644 --- a/packages/@aws-cdk/core/lib/names.ts +++ b/packages/@aws-cdk/core/lib/names.ts @@ -9,7 +9,7 @@ import { makeUniqueId } from './private/uniqueid'; export class Names { /** * Returns a CloudFormation-compatible unique identifier for a construct based - * on its path. The identifier includes a human readable porition rendered + * on its path. The identifier includes a human readable portion rendered * from the path components and a hash suffix. * * @param construct The construct @@ -23,7 +23,7 @@ export class Names { /** * Returns a CloudFormation-compatible unique identifier for a construct based - * on its path. The identifier includes a human readable porition rendered + * on its path. The identifier includes a human readable portion rendered * from the path components and a hash suffix. * * TODO (v2): replace with API to use `constructs.Node`. diff --git a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts index b4f9f29cf732a..2527bca98c9d6 100644 --- a/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/@aws-cdk/core/lib/stack-synthesizers/default-synthesizer.ts @@ -291,7 +291,8 @@ export class DefaultStackSynthesizer extends StackSynthesizer { assertBound(this.bucketName); validateFileAssetSource(asset); - const objectKey = this.bucketPrefix + asset.sourceHash + (asset.packaging === FileAssetPackaging.ZIP_DIRECTORY ? '.zip' : ''); + const extension = asset.fileName != undefined ? path.extname(asset.fileName) : ''; + const objectKey = this.bucketPrefix + asset.sourceHash + (asset.packaging === FileAssetPackaging.ZIP_DIRECTORY ? '.zip' : extension); // Add to manifest this.files[asset.sourceHash] = { @@ -448,7 +449,12 @@ export class DefaultStackSynthesizer extends StackSynthesizer { // // Instead, we'll have a protocol with the CLI that we put an 's3://.../...' URL here, and the CLI // is going to resolve it to the correct 'https://.../' URL before it gives it to CloudFormation. - return `s3://${this.bucketName}/${this.bucketPrefix}${sourceHash}`; + // + // ALSO: it would be great to reuse the return value of `addFileAsset()` here, except those contain + // CloudFormation REFERENCES to locations, not actual locations (can contain `{ Ref: AWS::Region }` and + // `{ Ref: SomeParameter }` etc). We therefore have to duplicate some logic here :(. + const extension = path.extname(this.stack.templateFile); + return `s3://${this.bucketName}/${this.bucketPrefix}${sourceHash}${extension}`; } /** diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 53e7adfdc206e..2284ddedc203f 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -371,7 +371,7 @@ export class Stack extends CoreConstruct implements ITaggable { this.templateOptions.description = props.description; } - this._stackName = props.stackName !== undefined ? props.stackName : this.generateStackName(); + this._stackName = props.stackName ?? this.generateStackName(); this.tags = new TagManager(TagType.KEY_VALUE, 'aws:cdk:stack', props.tags); if (!VALID_STACK_NAME_REGEX.test(this.stackName)) { diff --git a/packages/@aws-cdk/core/lib/tag-aspect.ts b/packages/@aws-cdk/core/lib/tag-aspect.ts index d56c28b551d87..09d79c7ea9799 100644 --- a/packages/@aws-cdk/core/lib/tag-aspect.ts +++ b/packages/@aws-cdk/core/lib/tag-aspect.ts @@ -126,7 +126,7 @@ export class Tag extends TagBase { resource.tags.setTag( this.key, this.value, - this.props.priority !== undefined ? this.props.priority : this.defaultPriority, + this.props.priority ?? this.defaultPriority, this.props.applyToLaunchedInstances !== false, ); } @@ -175,7 +175,7 @@ export class RemoveTag extends TagBase { protected applyTag(resource: ITaggable): void { if (resource.tags.applyTagAspectHere(this.props.includeResourceTypes, this.props.excludeResourceTypes)) { - resource.tags.removeTag(this.key, this.props.priority !== undefined ? this.props.priority : this.defaultPriority); + resource.tags.removeTag(this.key, this.props.priority ?? this.defaultPriority); } } } diff --git a/packages/@aws-cdk/core/lib/token.ts b/packages/@aws-cdk/core/lib/token.ts index 4bbcdb454f9bd..f92a2560cac7c 100644 --- a/packages/@aws-cdk/core/lib/token.ts +++ b/packages/@aws-cdk/core/lib/token.ts @@ -183,7 +183,7 @@ export class Tokenization { return resolve(obj, { scope: options.scope, resolver: options.resolver, - preparing: (options.preparing !== undefined ? options.preparing : false), + preparing: (options.preparing ?? false), }); } diff --git a/packages/@aws-cdk/core/test/bundling.test.ts b/packages/@aws-cdk/core/test/bundling.test.ts index e2b0d6b43b98b..258860d65585c 100644 --- a/packages/@aws-cdk/core/test/bundling.test.ts +++ b/packages/@aws-cdk/core/test/bundling.test.ts @@ -171,6 +171,44 @@ nodeunitShim({ test.done(); }, + 'custom entrypoint is passed through to docker exec'(test: Test) { + const spawnSyncStub = sinon.stub(child_process, 'spawnSync').returns({ + status: 0, + stderr: Buffer.from('stderr'), + stdout: Buffer.from('stdout'), + pid: 123, + output: ['stdout', 'stderr'], + signal: null, + }); + + const image = BundlingDockerImage.fromRegistry('alpine'); + image.run({ + entrypoint: ['/cool/entrypoint', '--cool-entrypoint-arg'], + command: ['cool', 'command'], + environment: { + VAR1: 'value1', + VAR2: 'value2', + }, + volumes: [{ hostPath: '/host-path', containerPath: '/container-path' }], + workingDirectory: '/working-directory', + user: 'user:group', + }); + + test.ok(spawnSyncStub.calledWith('docker', [ + 'run', '--rm', + '-u', 'user:group', + '-v', '/host-path:/container-path:delegated', + '--env', 'VAR1=value1', + '--env', 'VAR2=value2', + '-w', '/working-directory', + '--entrypoint', '/cool/entrypoint', + 'alpine', + '--cool-entrypoint-arg', + 'cool', 'command', + ], { stdio: ['ignore', process.stderr, 'inherit'] })); + test.done(); + }, + 'cp utility copies from an image'(test: Test) { // GIVEN const containerId = '1234567890abcdef1234567890abcdef'; diff --git a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts index 0131b7a4acc63..73f8f185f06ba 100644 --- a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts +++ b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts @@ -36,9 +36,9 @@ nodeunitShim({ // THEN -- the S3 url is advertised on the stack artifact const stackArtifact = asm.getStackArtifact('Stack'); - const templateHash = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); + const templateObjectKey = last(stackArtifact.stackTemplateAssetObjectUrl?.split('/')); - test.equals(stackArtifact.stackTemplateAssetObjectUrl, `s3://cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}/${templateHash}`); + test.equals(stackArtifact.stackTemplateAssetObjectUrl, `s3://cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}/${templateObjectKey}`); // THEN - the template is in the asset manifest const manifestArtifact = asm.artifacts.filter(isAssetManifest)[0]; @@ -52,7 +52,7 @@ nodeunitShim({ destinations: { 'current_account-current_region': { bucketName: 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}', - objectKey: templateHash, + objectKey: templateObjectKey, assumeRoleArn: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}', }, }, @@ -111,7 +111,7 @@ nodeunitShim({ // THEN - we have a fixed asset location with region placeholders test.equals(evalCFN(location.bucketName), 'cdk-hnb659fds-assets-the_account-the_region'); - test.equals(evalCFN(location.s3Url), 'https://s3.the_region.domain.aws/cdk-hnb659fds-assets-the_account-the_region/abcdef'); + test.equals(evalCFN(location.s3Url), 'https://s3.the_region.domain.aws/cdk-hnb659fds-assets-the_account-the_region/abcdef.js'); // THEN - object key contains source hash somewhere test.ok(location.objectKey.indexOf('abcdef') > -1); @@ -208,7 +208,7 @@ nodeunitShim({ test.deepEqual(manifest.files?.['file-asset-hash']?.destinations?.['current_account-current_region'], { bucketName: 'file-asset-bucket', - objectKey: 'file-asset-hash', + objectKey: 'file-asset-hash.js', assumeRoleArn: 'file:role:arn', assumeRoleExternalId: 'file-external-id', }); @@ -256,7 +256,7 @@ nodeunitShim({ // THEN test.deepEqual(manifest.files?.['file-asset-hash-with-prefix']?.destinations?.['current_account-current_region'], { bucketName: 'file-asset-bucket', - objectKey: '000000000000/file-asset-hash-with-prefix', + objectKey: '000000000000/file-asset-hash-with-prefix.js', assumeRoleArn: 'file:role:arn', assumeRoleExternalId: 'file-external-id', }); diff --git a/packages/@aws-cdk/core/test/tag-aspect.test.ts b/packages/@aws-cdk/core/test/tag-aspect.test.ts index cb2c5363e2153..b0871e2d13c02 100644 --- a/packages/@aws-cdk/core/test/tag-aspect.test.ts +++ b/packages/@aws-cdk/core/test/tag-aspect.test.ts @@ -6,7 +6,7 @@ class TaggableResource extends CfnResource { public readonly tags: TagManager; constructor(scope: Construct, id: string, props: CfnResourceProps) { super(scope, id, props); - const tags = props.properties === undefined ? undefined : props.properties.tags; + const tags = props.properties?.tags; this.tags = new TagManager(TagType.STANDARD, 'AWS::Fake::Resource', tags); } public testProperties() { @@ -18,7 +18,7 @@ class AsgTaggableResource extends CfnResource { public readonly tags: TagManager; constructor(scope: Construct, id: string, props: CfnResourceProps) { super(scope, id, props); - const tags = props.properties === undefined ? undefined : props.properties.tags; + const tags = props.properties?.tags; this.tags = new TagManager(TagType.AUTOSCALING_GROUP, 'AWS::Fake::Resource', tags); } public testProperties() { @@ -30,7 +30,7 @@ class MapTaggableResource extends CfnResource { public readonly tags: TagManager; constructor(scope: Construct, id: string, props: CfnResourceProps) { super(scope, id, props); - const tags = props.properties === undefined ? undefined : props.properties.tags; + const tags = props.properties?.tags; this.tags = new TagManager(TagType.MAP, 'AWS::Fake::Resource', tags); } public testProperties() { diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/provider.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/provider.ts index 3ae10fdf2e560..9da696f268b92 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/provider.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/provider.ts @@ -3,12 +3,16 @@ import * as cfn from '@aws-cdk/aws-cloudformation'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as lambda from '@aws-cdk/aws-lambda'; import * as logs from '@aws-cdk/aws-logs'; -import { Construct as CoreConstruct, Duration } from '@aws-cdk/core'; +import { Duration } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as consts from './runtime/consts'; import { calculateRetryPolicy } from './util'; import { WaiterStateMachine } from './waiter-state-machine'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + const RUNTIME_HANDLER_PATH = path.join(__dirname, 'runtime'); const FRAMEWORK_HANDLER_TIMEOUT = Duration.minutes(15); // keep it simple for now diff --git a/packages/@aws-cdk/custom-resources/lib/provider-framework/waiter-state-machine.ts b/packages/@aws-cdk/custom-resources/lib/provider-framework/waiter-state-machine.ts index 513e18d7f9c8b..6799fb3178123 100644 --- a/packages/@aws-cdk/custom-resources/lib/provider-framework/waiter-state-machine.ts +++ b/packages/@aws-cdk/custom-resources/lib/provider-framework/waiter-state-machine.ts @@ -1,6 +1,10 @@ import { Grant, IGrantable, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; -import { CfnResource, Construct, Duration, Stack } from '@aws-cdk/core'; +import { CfnResource, Duration, Stack } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; export interface WaiterStateMachineProps { /** diff --git a/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts b/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts index 0a67af8a5609d..415f49f703e26 100644 --- a/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts +++ b/packages/@aws-cdk/pipelines/lib/synths/simple-synth-action.ts @@ -6,11 +6,15 @@ import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Stack } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { cloudAssemblyBuildSpecDir } from '../private/construct-internals'; import { toPosixPath } from '../private/fs'; import { copyEnvironmentVariables, filterEmpty } from './_util'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Configuration options for a SimpleSynth */ diff --git a/packages/@aws-cdk/pipelines/lib/validation/shell-script-action.ts b/packages/@aws-cdk/pipelines/lib/validation/shell-script-action.ts index f70aa7897c1be..59f64555a76b5 100644 --- a/packages/@aws-cdk/pipelines/lib/validation/shell-script-action.ts +++ b/packages/@aws-cdk/pipelines/lib/validation/shell-script-action.ts @@ -4,9 +4,13 @@ import * as codepipeline_actions from '@aws-cdk/aws-codepipeline-actions'; import * as ec2 from '@aws-cdk/aws-ec2'; import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct } from '@aws-cdk/core'; + import { StackOutput } from '../stage'; +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + /** * Properties for ShellScriptAction */ diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 7c938b86bd7e0..10849cc784a4a 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -213,6 +213,7 @@ "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", "@aws-cdk/aws-managedblockchain": "0.0.0", "@aws-cdk/aws-mediaconnect": "0.0.0", diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml index ee30856453ff9..b541401f930e7 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml @@ -358,7 +358,7 @@ Resources: Action: - ssm:GetParameter Resource: - - Fn::Sub: "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}" + - Fn::Sub: "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}" Version: '2012-10-17' PolicyName: default RoleName: diff --git a/packages/aws-cdk/lib/cdk-toolkit.ts b/packages/aws-cdk/lib/cdk-toolkit.ts index c3e8c649eaa7b..84542eb8f4c7f 100644 --- a/packages/aws-cdk/lib/cdk-toolkit.ts +++ b/packages/aws-cdk/lib/cdk-toolkit.ts @@ -110,7 +110,7 @@ export class CdkToolkit { public async deploy(options: DeployOptions) { const stacks = await this.selectStacksForDeploy(options.stackNames, options.exclusively); - const requireApproval = options.requireApproval !== undefined ? options.requireApproval : RequireApproval.Broadening; + const requireApproval = options.requireApproval ?? RequireApproval.Broadening; const parameterMap: { [name: string]: { [name: string]: string | undefined } } = { '*': {} }; for (const key in options.parameters) { diff --git a/packages/decdk/package.json b/packages/decdk/package.json index 7f8ace298e383..a5c184e65f1d5 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -139,6 +139,7 @@ "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", "@aws-cdk/aws-managedblockchain": "0.0.0", "@aws-cdk/aws-mediaconnect": "0.0.0", diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index dddf5eb0b77fd..a02fee1b78939 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -218,6 +218,7 @@ "@aws-cdk/aws-licensemanager": "0.0.0", "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-logs-destinations": "0.0.0", + "@aws-cdk/aws-lookoutvision": "0.0.0", "@aws-cdk/aws-macie": "0.0.0", "@aws-cdk/aws-managedblockchain": "0.0.0", "@aws-cdk/aws-mediaconnect": "0.0.0", diff --git a/tools/cdk-build-tools/config/eslintrc.js b/tools/cdk-build-tools/config/eslintrc.js index 446af2c2e2ff4..8fda93acca482 100644 --- a/tools/cdk-build-tools/config/eslintrc.js +++ b/tools/cdk-build-tools/config/eslintrc.js @@ -41,6 +41,7 @@ module.exports = { }, ignorePatterns: ['*.js', '*.d.ts', 'node_modules/', '*.generated.ts'], rules: { + 'cdk/construct-import-order': [ 'error' ], 'cdk/no-core-construct': [ 'error' ], 'cdk/no-qualified-construct': [ 'error' ], // Require use of the `import { foo } from 'bar';` form instead of `import foo = require('bar');` diff --git a/tools/eslint-plugin-cdk/lib/index.ts b/tools/eslint-plugin-cdk/lib/index.ts index 94eef4dd8f57f..a9eb84b77ef0f 100644 --- a/tools/eslint-plugin-cdk/lib/index.ts +++ b/tools/eslint-plugin-cdk/lib/index.ts @@ -1,4 +1,5 @@ export const rules = { 'no-core-construct': require('./rules/no-core-construct'), 'no-qualified-construct': require('./rules/no-qualified-construct'), + 'construct-import-order': require('./rules/construct-import-order'), }; diff --git a/tools/eslint-plugin-cdk/lib/rules/construct-import-order.ts b/tools/eslint-plugin-cdk/lib/rules/construct-import-order.ts new file mode 100644 index 0000000000000..fc6066e486747 --- /dev/null +++ b/tools/eslint-plugin-cdk/lib/rules/construct-import-order.ts @@ -0,0 +1,104 @@ +// +// This rule ensures that the `@aws-cdk/core.Construct` class is always +// imported at the end, and in a separate section. In the `v2-main` branch, +// this class is removed and so is the import. Keeping it in a separate line +// and section ensures that any other adjustments to the import do not cause +// conflicts on forward merges. +// + +import { ImportDeclaration } from 'estree'; +import { Rule } from 'eslint'; + +interface ImportOrderViolation { + node: ImportDeclaration; + localName: string; + range: [number, number]; +} + +let importOrderViolation: ImportOrderViolation | undefined; +let coreConstructImportLine: ImportDeclaration | undefined; +let lastImport: Rule.Node | undefined; + +export function create(context: Rule.RuleContext): Rule.NodeListener { + return { + Program: _ => { + // reset for every file + importOrderViolation = undefined; + coreConstructImportLine = undefined; + lastImport = undefined; + }, + + // collect all "import" statements. we will later use them to determine + // exactly how to import `core.Construct`. + ImportDeclaration: node => { + lastImport = node; + + if (coreConstructImportLine && coreConstructImportLine.range) { + // If CoreConstruct import was previously seen, this import line should not succeed it. + + importOrderViolation = { + node: coreConstructImportLine, + range: coreConstructImportLine.range, + localName: coreConstructImportLine.specifiers[0].local.name, + }; + } + + for (const [i, s] of node.specifiers.entries()) { + const isConstruct = (s.local.name === 'CoreConstruct' || s.local.name === 'Construct') && node.source.value === '@aws-cdk/core'; + if (isConstruct && s.range) { + if (node.specifiers.length > 1) { + // if there is more than one specifier on the line that also imports CoreConstruct, i.e., + // `import { Resource, Construct as CoreConstruct, Token } from '@aws-cdk/core'` + + // If this is the last specifier, delete just that. If not, delete until the beginning of the next specifier. + const range: [number, number] = (i === node.specifiers.length - 1) ? s.range : [s.range[0], node.specifiers[i + 1].range![0]]; + importOrderViolation = { node, range, localName: s.local.name }; + } else { + // This means that CoreConstruct is the only import within this line, + // so record the node so the whole line can be removed if there are imports that follow + + coreConstructImportLine = node; + } + } + } + }, + + Identifier: node => { + if ( + node.parent.type !== 'ImportSpecifier' && + (node.name === 'CoreConstruct' || node.name === 'Construct') && + importOrderViolation + ) { + reportImportOrderViolations(context); + } + }, + } +} + +function reportImportOrderViolations(context: Rule.RuleContext) { + if (importOrderViolation && lastImport) { + const violation = importOrderViolation; + const _lastImport = lastImport; + context.report({ + message: 'To avoid merge conflicts with the v2 branch, import of "@aws-cdk/core.Construct" must be in its own line, ' + + 'and as the very last import.', + node: violation.node, + fix: fixer => { + const fixes: Rule.Fix[] = []; + fixes.push(fixer.removeRange(violation.range)); + const sym = violation.localName === 'Construct' ? 'Construct' : 'Construct as CoreConstruct' + const addImport = `import { ${sym} } from '@aws-cdk/core';`; + fixes.push(fixer.insertTextAfter(_lastImport, [ + "", + "", + "// keep this import separate from other imports to reduce chance for merge conflicts with v2-main", + "// eslint-disable-next-line no-duplicate-imports, import/order", + addImport, + ].join('\n'))); + return fixes; + } + }); + // reset, so that this is reported only once + importOrderViolation = undefined; + } +} \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures.test.ts b/tools/eslint-plugin-cdk/test/rules/fixtures.test.ts index b65670f43b429..bf1c03f359047 100644 --- a/tools/eslint-plugin-cdk/test/rules/fixtures.test.ts +++ b/tools/eslint-plugin-cdk/test/rules/fixtures.test.ts @@ -55,9 +55,15 @@ fs.readdirSync(fixturesRoot).filter(f => fs.lstatSync(path.join(fixturesRoot, f) async function lintAndFix(file: string, outputDir: string) { const newPath = path.join(outputDir, path.basename(file)) let result = await linter.lintFiles(file); - await ESLint.outputFixes(result.map(r => { - r.filePath = newPath; - return r; - })); + const hasFixes = result.find(r => typeof(r.output) === 'string') !== undefined; + if (hasFixes) { + await ESLint.outputFixes(result.map(r => { + r.filePath = newPath; + return r; + })); + } else { + // If there are no fixes, copy the input file as output + await fs.copyFile(file, newPath); + } return newPath; } diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.expected.ts new file mode 100644 index 0000000000000..a6c9ecede29f0 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.expected.ts @@ -0,0 +1,9 @@ + +import { Something } from 'Somewhere'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +class MyConstruct extends Construct { +} \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.ts new file mode 100644 index 0000000000000..8689366792602 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-class-usage.ts @@ -0,0 +1,5 @@ +import { Construct } from '@aws-cdk/core'; +import { Something } from 'Somewhere'; + +class MyConstruct extends Construct { +} \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.expected.ts new file mode 100644 index 0000000000000..b0458a380f220 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.expected.ts @@ -0,0 +1,8 @@ +import { Resource } from '@aws-cdk/core'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +let x: Resource; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.ts new file mode 100644 index 0000000000000..c417c728ab86c --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-multiple-specifiers.ts @@ -0,0 +1,4 @@ +import { Construct, Resource } from '@aws-cdk/core'; + +let x: Resource; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.expected.ts new file mode 100644 index 0000000000000..a9e29b0d3a912 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.expected.ts @@ -0,0 +1,9 @@ + +import { Something } from 'Somewhere'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +let x: Something; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.ts new file mode 100644 index 0000000000000..d7716d82c3d12 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/construct-nonfinal.ts @@ -0,0 +1,5 @@ +import { Construct } from '@aws-cdk/core'; +import { Something } from 'Somewhere'; + +let x: Something; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.expected.ts new file mode 100644 index 0000000000000..eccef051ab56c --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.expected.ts @@ -0,0 +1,9 @@ + +import { Something } from 'Somewhere'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + +class MyConstruct extends CoreConstruct { +} \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.ts new file mode 100644 index 0000000000000..5cf72993ecbe9 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-class-usage.ts @@ -0,0 +1,5 @@ +import { Construct as CoreConstruct } from '@aws-cdk/core'; +import { Something } from 'Somewhere'; + +class MyConstruct extends CoreConstruct { +} \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.expected.ts new file mode 100644 index 0000000000000..8a8b9c6efe4b4 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.expected.ts @@ -0,0 +1,9 @@ +import { Resource, Token } from '@aws-cdk/core'; +import { Construct } from 'constructs' + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + +let x: CoreConstruct; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.ts new file mode 100644 index 0000000000000..403f5166a31bc --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-multiple-specifiers.ts @@ -0,0 +1,5 @@ +import { Construct as CoreConstruct, Resource, Token } from '@aws-cdk/core'; +import { Construct } from 'constructs' + +let x: CoreConstruct; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.expected.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.expected.ts new file mode 100644 index 0000000000000..a0430db275df2 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.expected.ts @@ -0,0 +1,9 @@ + +import { Construct } from 'constructs'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; + +let x: CoreConstruct; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.ts b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.ts new file mode 100644 index 0000000000000..98baab61bb84c --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/coreconstruct-nonfinal.ts @@ -0,0 +1,5 @@ +import { Construct as CoreConstruct } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +let x: CoreConstruct; +let y: Construct; \ No newline at end of file diff --git a/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/eslintrc.js b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/eslintrc.js new file mode 100644 index 0000000000000..3082e03d4ed79 --- /dev/null +++ b/tools/eslint-plugin-cdk/test/rules/fixtures/construct-import-order/eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['rulesdir'], + rules: { + 'rulesdir/construct-import-order': [ 'error' ], + } +} \ No newline at end of file diff --git a/version.v1.json b/version.v1.json index f804856ae4ccf..345b477275b1d 100644 --- a/version.v1.json +++ b/version.v1.json @@ -1,3 +1,3 @@ { - "version": "1.88.0" + "version": "1.89.0" }