From ea6085c52e805939411e2c325f351878b13b1663 Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 23 Sep 2024 12:07:04 -0400 Subject: [PATCH] feat: allow all `sts` options for roles assumed by the cli (#31089) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow passing [all STS options](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters) to assume role configuration for various CDK roles. > The following PR description focuses on Session Tags because it was originally the only option we wanted to add support to. After some thought, we decided to allow all available STS options via a transparent pass-through. ### Prerequisites - https://github.com/cdklabs/cloud-assembly-schema/pull/33 - https://github.com/cdklabs/cdk-assets/pull/40 ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/26157 Fixes https://github.com/aws/aws-cdk/issues/22535 ### Reason for this change Enabling [ABAC](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html) via STS session tags. From the [AWS docs](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html): > _“Session Tags are key-value pair attributes that you pass when you assume an IAM role or federate a user in AWS STS. You do this by making an AWS CLI or AWS API request through AWS STS or through your identity provider (IdP). When you use AWS STS to request temporary security credentials, you generate a session. Sessions expire and have [credentials](https://docs.aws.amazon.com/STS/latest/APIReference/API_Credentials.html), such as an access key pair and a session token. When you use the session credentials to make a subsequent request, the [request context](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html#AccessPolicyLanguage_RequestContext) includes the [aws:PrincipalTag](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-principaltag) context key. You can use the aws:PrincipalTag key in the Condition element of your policies to allow or deny access based on those tags”_ ### Description of changes The CDK creates [4 IAM roles](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles) during bootstrap. It then assumes these roles at various phases of the deployment. - [DeploymentActionRole](https://github.com/aws/aws-cdk/blob/v2.154.1/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml#L429): Assumed when invoking CloudFormation operations such as _Deploy_ and _DescribeStackEvents_. - [FilePublishingRole](https://github.com/aws/aws-cdk/blob/v2.154.1/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml#L275): Assumed when file assets are uploaded to the bootstrap bucket. - [ImagePublishingRole](https://github.com/aws/aws-cdk/blob/v2.154.1/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml#L298): Assumed when docker images are published to the bootstrap ECR registry. - [LookupRole](https://github.com/aws/aws-cdk/blob/v2.154.1/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml#L321): Assumed while performing context lookups. Each of these roles should be assumable with their own specific session tags, as they server different purposes. > Note: The [CloudFormationExecutionRole](https://github.com/aws/aws-cdk/blob/v2.154.1/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml#L536) is also created during bootstrap, but the CLI never assumes it, therefore it doesn't need session tags support. Session tags for each role will be configurable via synthesizer properties (similarly to how `externalId` is [exposed](https://github.com/aws/aws-cdk/pull/15604)) both for the `DefaultStackSynthesizer`, and for a custom synthesizer extending the `StackSynthesizer` class. The new properties will propagate down and will eventually be written to the cloud assembly. #### `+ manifest.json` ```json { "version": "36.0.0", "artifacts": { "MyStack.assets": { "type": "cdk:asset-manifest", "properties": { "file": "SeshTagsManifestStack.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, "MyStack": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "assumeRoleAdditionalOptions": { "Tags": < deployRoleSessionTags > } "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", "requiresBootstrapStackVersion": 8, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" "assumeRoleAdditionalOptions": { "Tags": < lookupRoleSessionTags > } } }, ``` #### `+ assets.json` ```json { "version": "36.0.0", "files": { "9ebfd704f02f99b2711998e6435822b0dbed6e80dcac7e75f339fe894861ac20": { "source": { "path": "mystack.template.json", "packaging": "file" }, "destinations": { "current_account-current_region": { "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" "assumeRoleAdditionalOptions": { "Tags": < fileAssetPublishingRoleSessionTags > } } } } }, "dockerImages": { "dockerHash": { "source": { "directory": "." }, "destinations": { "current_account-current_region": { "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-image-publishing-role-${AWS::AccountId}-${AWS::Region}" "assumeRoleAdditionalOptions": { "Tags": < imageAssetPublishingRoleSessionTags > } } } } } } ``` ### Description of how you validated changes - CLI integration tests. - CLI and framework unit tests. ### Checklist - [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cli-integ/lib/with-cdk-app.ts | 10 + .../session-tags.all-roles-deny-all.yaml | 703 +++++++++ .../session-tags.deploy-role-deny-sqs.yaml | 700 ++++++++ .../cli-integ/resources/cdk-apps/app/app.js | 69 + .../bootstrapping.integtest.ts | 37 + packages/@aws-cdk/cx-api/FEATURE_FLAGS.md | 1401 ----------------- packages/aws-cdk-lib/core/README.md | 80 + .../asset-manifest-builder.ts | 15 + .../stack-synthesizers/default-synthesizer.ts | 94 +- .../stack-synthesizers/stack-synthesizer.ts | 19 +- packages/aws-cdk-lib/core/lib/stack.ts | 21 +- .../new-style-synthesis.test.ts | 85 +- .../aws-cdk-lib/core/test/synthesis.test.ts | 79 + .../lib/artifacts/cloudformation-artifact.ts | 14 +- .../lib/blueprint/stage-deployment.ts | 3 + .../test/codepipeline/codepipeline.test.ts | 31 +- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 41 +- .../lib/api/bootstrap/bootstrap-template.yaml | 30 +- packages/aws-cdk/lib/api/deployments.ts | 12 +- packages/aws-cdk/lib/context-providers/ami.ts | 7 +- .../context-providers/availability-zones.ts | 7 +- .../endpoint-service-availability-zones.ts | 10 +- .../lib/context-providers/hosted-zones.ts | 7 +- .../aws-cdk/lib/context-providers/keys.ts | 9 +- .../lib/context-providers/load-balancers.ts | 9 +- .../lib/context-providers/security-groups.ts | 8 +- .../lib/context-providers/ssm-parameters.ts | 13 +- .../aws-cdk/lib/context-providers/vpcs.ts | 9 +- packages/aws-cdk/lib/util/asset-publishing.ts | 11 +- packages/aws-cdk/test/api/fake-sts.ts | 27 + .../aws-cdk/test/api/sdk-provider.test.ts | 33 + 31 files changed, 2112 insertions(+), 1482 deletions(-) create mode 100644 packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml create mode 100644 packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml delete mode 100644 packages/@aws-cdk/cx-api/FEATURE_FLAGS.md diff --git a/packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts b/packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts index 619ca8b96175c..934531f45ffcb 100644 --- a/packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts +++ b/packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts @@ -233,6 +233,13 @@ export async function cloneDirectory(source: string, target: string, output?: No } interface CommonCdkBootstrapCommandOptions { + /** + * Path to a custom bootstrap template. + * + * @default - the default CDK bootstrap template. + */ + readonly bootstrapTemplate?: string; + readonly toolkitStackName: string; /** @@ -422,6 +429,9 @@ export class TestFixture extends ShellHelper { if (options.usePreviousParameters === false) { args.push('--no-previous-parameters'); } + if (options.bootstrapTemplate) { + args.push('--template', options.bootstrapTemplate); + } return this.cdk(args, { ...options.cliOptions, diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml new file mode 100644 index 0000000000000..e2b80c535d615 --- /dev/null +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml @@ -0,0 +1,703 @@ +Description: This stack includes resources needed to deploy AWS CDK apps into this + environment +Parameters: + TrustedAccounts: + Description: List of AWS accounts that are trusted to publish assets and deploy + stacks to this environment + Default: '' + Type: CommaDelimitedList + TrustedAccountsForLookup: + Description: List of AWS accounts that are trusted to look up values in this + environment + Default: '' + Type: CommaDelimitedList + CloudFormationExecutionPolicies: + Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation + deployment role + Default: '' + Type: CommaDelimitedList + FileAssetsBucketName: + Description: The name of the S3 bucket used for file assets + Default: '' + Type: String + FileAssetsBucketKmsKeyId: + Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed + S3 key, or the ID/ARN of an existing key. + Default: '' + Type: String + ContainerAssetsRepositoryName: + Description: A user-provided custom name to use for the container assets ECR repository + Default: '' + Type: String + Qualifier: + Description: An identifier to distinguish multiple bootstrap stacks in the same environment + Default: hnb659fds + Type: String + # "cdk-(qualifier)-image-publishing-role-(account)-(region)" needs to be <= 64 chars + # account = 12, region <= 14, 10 chars for qualifier and 28 for rest of role name + AllowedPattern: "[A-Za-z0-9_-]{1,10}" + ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters + PublicAccessBlockConfiguration: + Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration + Default: 'true' + Type: 'String' + AllowedValues: ['true', 'false'] + InputPermissionsBoundary: + Description: Whether or not to use either the CDK supplied or custom permissions boundary + Default: '' + Type: 'String' + UseExamplePermissionsBoundary: + Default: 'false' + AllowedValues: [ 'true', 'false' ] + Type: String + BootstrapVariant: + Type: String + Default: 'AWS CDK: Default Resources' + Description: Describe the provenance of the resources in this bootstrap + stack. Change this when you customize the template. To prevent accidents, + the CDK CLI will not overwrite bootstrap stacks with a different variant. +Conditions: + HasTrustedAccounts: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: TrustedAccounts + HasTrustedAccountsForLookup: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: TrustedAccountsForLookup + HasCloudFormationExecutionPolicies: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: CloudFormationExecutionPolicies + HasCustomFileAssetsBucketName: + Fn::Not: + - Fn::Equals: + - '' + - Ref: FileAssetsBucketName + CreateNewKey: + Fn::Equals: + - '' + - Ref: FileAssetsBucketKmsKeyId + UseAwsManagedKey: + Fn::Equals: + - 'AWS_MANAGED_KEY' + - Ref: FileAssetsBucketKmsKeyId + ShouldCreatePermissionsBoundary: + Fn::Equals: + - 'true' + - Ref: UseExamplePermissionsBoundary + PermissionsBoundarySet: + Fn::Not: + - Fn::Equals: + - '' + - Ref: InputPermissionsBoundary + HasCustomContainerAssetsRepositoryName: + Fn::Not: + - Fn::Equals: + - '' + - Ref: ContainerAssetsRepositoryName + UsePublicAccessBlockConfiguration: + Fn::Equals: + - 'true' + - Ref: PublicAccessBlockConfiguration +Resources: + FileAssetsBucketEncryptionKey: + Type: AWS::KMS::Key + Properties: + KeyPolicy: + Statement: + - Action: + - kms:Create* + - kms:Describe* + - kms:Enable* + - kms:List* + - kms:Put* + - kms:Update* + - kms:Revoke* + - kms:Disable* + - kms:Get* + - kms:Delete* + - kms:ScheduleKeyDeletion + - kms:CancelKeyDeletion + - kms:GenerateDataKey + - kms:TagResource + - kms:UntagResource + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + Resource: "*" + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Principal: + # Not actually everyone -- see below for Conditions + AWS: "*" + Resource: "*" + Condition: + StringEquals: + # See https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-caller-account + kms:CallerAccount: + Ref: AWS::AccountId + kms:ViaService: + - Fn::Sub: s3.${AWS::Region}.amazonaws.com + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Principal: + AWS: + Fn::Sub: "${FilePublishingRole.Arn}" + Resource: "*" + Condition: CreateNewKey + FileAssetsBucketEncryptionKeyAlias: + Condition: CreateNewKey + Type: AWS::KMS::Alias + Properties: + AliasName: + Fn::Sub: "alias/cdk-${Qualifier}-assets-key" + TargetKeyId: + Ref: FileAssetsBucketEncryptionKey + StagingBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: + Fn::If: + - HasCustomFileAssetsBucketName + - Fn::Sub: "${FileAssetsBucketName}" + - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region} + AccessControl: Private + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: aws:kms + KMSMasterKeyID: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::If: + - UseAwsManagedKey + - Ref: AWS::NoValue + - Fn::Sub: "${FileAssetsBucketKmsKeyId}" + PublicAccessBlockConfiguration: + Fn::If: + - UsePublicAccessBlockConfiguration + - BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + - Ref: AWS::NoValue + VersioningConfiguration: + Status: Enabled + LifecycleConfiguration: + Rules: + # Exising objects will never be overwritten but Security Hub wants this rule to exist + - Id: CleanupOldVersions + Status: Enabled + NoncurrentVersionExpiration: + NoncurrentDays: 365 + UpdateReplacePolicy: Retain + DeletionPolicy: Retain + StagingBucketPolicy: + Type: 'AWS::S3::BucketPolicy' + Properties: + Bucket: { Ref: 'StagingBucket' } + PolicyDocument: + Id: 'AccessControl' + Version: '2012-10-17' + Statement: + - Sid: 'AllowSSLRequestsOnly' + Action: 's3:*' + Effect: 'Deny' + Resource: + - { 'Fn::Sub': '${StagingBucket.Arn}' } + - { 'Fn::Sub': '${StagingBucket.Arn}/*' } + Condition: + Bool: { 'aws:SecureTransport': 'false' } + Principal: '*' + ContainerAssetsRepository: + Type: AWS::ECR::Repository + Properties: + ImageTagMutability: IMMUTABLE + # Untagged images should never exist but Security Hub wants this rule to exist + LifecyclePolicy: + LifecyclePolicyText: | + { + "rules": [ + { + "rulePriority": 1, + "description": "Untagged images should not exist, but expire any older than one year", + "selection": { + "tagStatus": "untagged", + "countType": "sinceImagePushed", + "countUnit": "days", + "countNumber": 365 + }, + "action": { "type": "expire" } + } + ] + } + RepositoryName: + Fn::If: + - HasCustomContainerAssetsRepositoryName + - Fn::Sub: "${ContainerAssetsRepositoryName}" + - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region} + RepositoryPolicyText: + Version: "2012-10-17" + Statement: + # Necessary for Lambda container images + # https://docs.aws.amazon.com/lambda/latest/dg/configuration-images.html#configuration-images-permissions + - Sid: LambdaECRImageRetrievalPolicy + Effect: Allow + Principal: { Service: "lambda.amazonaws.com" } + Action: + - ecr:BatchGetImage + - ecr:GetDownloadUrlForLayer + Condition: + StringLike: + "aws:sourceArn": { "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" } + FilePublishingRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: file-publishing + ImagePublishingRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: image-publishing + LookupRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccountsForLookup + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccountsForLookup + - Ref: AWS::NoValue + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region} + Policies: + - PolicyDocument: + Statement: + - Sid: AllowEc2OnlyIfEngineeringDepartement + Effect: Allow + Action: + - ec2:* + Resource: "*" + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" + Version: '2012-10-17' + PolicyName: LookupRolePolicy + Tags: + - Key: aws-cdk:bootstrap-role + Value: lookup + FilePublishingRoleDefaultPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyDocument: + Statement: + - Action: + - s3:GetObject* + - s3:GetBucket* + - s3:GetEncryptionConfiguration + - s3:List* + - s3:DeleteObject* + - s3:PutObject* + - s3:Abort* + Resource: + - Fn::Sub: "${StagingBucket.Arn}" + - Fn::Sub: "${StagingBucket.Arn}/*" + # This condition requires that the File Publishing Role is assumed with the session tags + # 'Department: Engineering'; if these tags are not passed in, the role will + # not be able to perform these S3 actions. + Condition: + StringEquals: + aws:ResourceAccount: + - Fn::Sub: ${AWS::AccountId} + aws:PrincipalTag/Department: "Engineering" + Effect: Allow + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Resource: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId} + Version: '2012-10-17' + Roles: + - Ref: FilePublishingRole + PolicyName: + Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + ImagePublishingRoleDefaultPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyDocument: + Statement: + - Action: + - ecr:PutImage + - ecr:InitiateLayerUpload + - ecr:UploadLayerPart + - ecr:CompleteLayerUpload + - ecr:BatchCheckLayerAvailability + - ecr:DescribeRepositories + - ecr:DescribeImages + - ecr:BatchGetImage + - ecr:GetDownloadUrlForLayer + Resource: + Fn::Sub: "${ContainerAssetsRepository.Arn}" + Effect: Allow + # This condition requires that the Image Publishing Role is assumed with the session tags + # 'Department: Engineering'; if these tags are not passed in, the role will + # not be able to perform these ECR actions. + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" + - Action: + - ecr:GetAuthorizationToken + Resource: "*" + Effect: Allow + Version: '2012-10-17' + Roles: + - Ref: ImagePublishingRole + PolicyName: + Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + DeploymentActionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + Policies: + - PolicyDocument: + Statement: + - Sid: CloudFormationPermissions + Effect: Allow + Action: + - cloudformation:CreateChangeSet + - cloudformation:DeleteChangeSet + - cloudformation:DescribeChangeSet + - cloudformation:DescribeStacks + - cloudformation:ExecuteChangeSet + - cloudformation:CreateStack + - cloudformation:UpdateStack + Resource: "*" + # This condition requires that the Deploy Role is assumed with the session tags + # 'Department: Engineering'; if these tags are not passed in, the Deploy Role will + # not be able to perform these CloudFormation actions. + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" + - Sid: PipelineCrossAccountArtifactsBucket + # Read/write buckets in different accounts. Permissions to buckets in + # same account are granted by bucket policies. + # + # Write permissions necessary to write outputs to the cross-region artifact replication bucket + # https://aws.amazon.com/premiumsupport/knowledge-center/codepipeline-deploy-cloudformation/. + Effect: Allow + Action: + - s3:GetObject* + - s3:GetBucket* + - s3:List* + - s3:Abort* + - s3:DeleteObject* + - s3:PutObject* + Resource: "*" + Condition: + StringNotEquals: + s3:ResourceAccount: + Ref: 'AWS::AccountId' + - Sid: PipelineCrossAccountArtifactsKey + # Use keys only for the purposes of reading encrypted files from S3. + Effect: Allow + Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Resource: "*" + Condition: + StringEquals: + kms:ViaService: + Fn::Sub: s3.${AWS::Region}.amazonaws.com + - Action: iam:PassRole + Resource: + Fn::Sub: "${CloudFormationExecutionRole.Arn}" + Effect: Allow + - Sid: CliPermissions + Action: + # Permissions needed by the CLI when doing `cdk deploy`. + # Our CI/CD does not need DeleteStack, + # but we also want to use this role from the CLI, + # and there you can call `cdk destroy` + - cloudformation:DescribeStackEvents + - cloudformation:GetTemplate + - cloudformation:DeleteStack + - cloudformation:UpdateTerminationProtection + - sts:GetCallerIdentity + # `cdk import` + - cloudformation:GetTemplateSummary + Resource: "*" + Effect: Allow + - Sid: CliStagingBucket + Effect: Allow + Action: + - s3:GetObject* + - s3:GetBucket* + - s3:List* + Resource: + - Fn::Sub: ${StagingBucket.Arn} + - Fn::Sub: ${StagingBucket.Arn}/* + - Sid: ReadVersion + Effect: Allow + Action: + - ssm:GetParameter + - ssm:GetParameters # CreateChangeSet uses this to evaluate any SSM parameters (like `CdkBootstrapVersion`) + Resource: + - Fn::Sub: "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}" + Version: '2012-10-17' + PolicyName: default + RoleName: + Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: deploy + CloudFormationExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: cloudformation.amazonaws.com + Version: '2012-10-17' + ManagedPolicyArns: + Fn::If: + - HasCloudFormationExecutionPolicies + - Ref: CloudFormationExecutionPolicies + - Fn::If: + - HasTrustedAccounts + # The CLI will prevent this case from occurring + - Ref: AWS::NoValue + # The CLI will advertise that we picked this implicitly + - - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + RoleName: + Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region} + PermissionsBoundary: + Fn::If: + - PermissionsBoundarySet + - Fn::Sub: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}' + - Ref: AWS::NoValue + CdkBoostrapPermissionsBoundaryPolicy: + # Edit the template prior to boostrap in order to have this example policy created + Condition: ShouldCreatePermissionsBoundary + Type: AWS::IAM::ManagedPolicy + Properties: + PolicyDocument: + Statement: + # If permission boundaries do not have an explicit `allow`, then the effect is `deny` + - Sid: ExplicitAllowAll + Action: + - "*" + Effect: Allow + Resource: "*" + # Default permissions to prevent privilege escalation + - Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied + Action: + - iam:CreateUser + - iam:CreateRole + - iam:PutRolePermissionsBoundary + - iam:PutUserPermissionsBoundary + Condition: + StringNotEquals: + iam:PermissionsBoundary: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + Effect: Deny + Resource: "*" + # Forbid the policy itself being edited + - Sid: DenyPermBoundaryIAMPolicyAlteration + Action: + - iam:CreatePolicyVersion + - iam:DeletePolicy + - iam:DeletePolicyVersion + - iam:SetDefaultPolicyVersion + Effect: Deny + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + # Forbid removing the permissions boundary from any user or role that has it associated + - Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole + Action: + - iam:DeleteUserPermissionsBoundary + - iam:DeleteRolePermissionsBoundary + Effect: Deny + Resource: "*" + # Add your specific organizational security policy here + # Uncomment the example to deny access to AWS Config + #- Sid: OrganizationalSecurityPolicy + # Action: + # - "config:*" + # Effect: Deny + # Resource: "*" + Version: "2012-10-17" + Description: "Bootstrap Permission Boundary" + ManagedPolicyName: + Fn::Sub: cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + Path: / + # The SSM parameter is used in pipeline-deployed templates to verify the version + # of the bootstrap resources. + CdkBootstrapVersion: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: + Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' + Value: '22' +Outputs: + BucketName: + Description: The name of the S3 bucket owned by the CDK toolkit stack + Value: + Fn::Sub: "${StagingBucket}" + BucketDomainName: + Description: The domain name of the S3 bucket owned by the CDK toolkit stack + Value: + Fn::Sub: "${StagingBucket.RegionalDomainName}" + # @deprecated - This Export can be removed at some future point in time. + # We can't do it today because if there are stacks that use it, the bootstrap + # stack cannot be updated. Not used anymore by apps >= 1.60.0 + FileAssetKeyArn: + Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated) + Value: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::Sub: "${FileAssetsBucketKmsKeyId}" + Export: + Name: + Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn + ImageRepositoryName: + Description: The name of the ECR repository which hosts docker image assets + Value: + Fn::Sub: "${ContainerAssetsRepository}" + # The Output is used by the CLI to verify the version of the bootstrap resources. + BootstrapVersion: + Description: The version of the bootstrap resources that are currently mastered + in this stack + Value: + Fn::GetAtt: [CdkBootstrapVersion, Value] diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml new file mode 100644 index 0000000000000..94e789a0493ce --- /dev/null +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml @@ -0,0 +1,700 @@ +Description: This stack includes resources needed to deploy AWS CDK apps into this + environment +Parameters: + TrustedAccounts: + Description: List of AWS accounts that are trusted to publish assets and deploy + stacks to this environment + Default: '' + Type: CommaDelimitedList + TrustedAccountsForLookup: + Description: List of AWS accounts that are trusted to look up values in this + environment + Default: '' + Type: CommaDelimitedList + CloudFormationExecutionPolicies: + Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation + deployment role + Default: '' + Type: CommaDelimitedList + FileAssetsBucketName: + Description: The name of the S3 bucket used for file assets + Default: '' + Type: String + FileAssetsBucketKmsKeyId: + Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed + S3 key, or the ID/ARN of an existing key. + Default: '' + Type: String + ContainerAssetsRepositoryName: + Description: A user-provided custom name to use for the container assets ECR repository + Default: '' + Type: String + Qualifier: + Description: An identifier to distinguish multiple bootstrap stacks in the same environment + Default: hnb659fds + Type: String + # "cdk-(qualifier)-image-publishing-role-(account)-(region)" needs to be <= 64 chars + # account = 12, region <= 14, 10 chars for qualifier and 28 for rest of role name + AllowedPattern: "[A-Za-z0-9_-]{1,10}" + ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters + PublicAccessBlockConfiguration: + Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration + Default: 'true' + Type: 'String' + AllowedValues: ['true', 'false'] + InputPermissionsBoundary: + Description: Whether or not to use either the CDK supplied or custom permissions boundary + Default: '' + Type: 'String' + UseExamplePermissionsBoundary: + Default: 'false' + AllowedValues: [ 'true', 'false' ] + Type: String + BootstrapVariant: + Type: String + Default: 'AWS CDK: Default Resources' + Description: Describe the provenance of the resources in this bootstrap + stack. Change this when you customize the template. To prevent accidents, + the CDK CLI will not overwrite bootstrap stacks with a different variant. +Conditions: + HasTrustedAccounts: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: TrustedAccounts + HasTrustedAccountsForLookup: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: TrustedAccountsForLookup + HasCloudFormationExecutionPolicies: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: CloudFormationExecutionPolicies + HasCustomFileAssetsBucketName: + Fn::Not: + - Fn::Equals: + - '' + - Ref: FileAssetsBucketName + CreateNewKey: + Fn::Equals: + - '' + - Ref: FileAssetsBucketKmsKeyId + UseAwsManagedKey: + Fn::Equals: + - 'AWS_MANAGED_KEY' + - Ref: FileAssetsBucketKmsKeyId + ShouldCreatePermissionsBoundary: + Fn::Equals: + - 'true' + - Ref: UseExamplePermissionsBoundary + PermissionsBoundarySet: + Fn::Not: + - Fn::Equals: + - '' + - Ref: InputPermissionsBoundary + HasCustomContainerAssetsRepositoryName: + Fn::Not: + - Fn::Equals: + - '' + - Ref: ContainerAssetsRepositoryName + UsePublicAccessBlockConfiguration: + Fn::Equals: + - 'true' + - Ref: PublicAccessBlockConfiguration +Resources: + FileAssetsBucketEncryptionKey: + Type: AWS::KMS::Key + Properties: + KeyPolicy: + Statement: + - Action: + - kms:Create* + - kms:Describe* + - kms:Enable* + - kms:List* + - kms:Put* + - kms:Update* + - kms:Revoke* + - kms:Disable* + - kms:Get* + - kms:Delete* + - kms:ScheduleKeyDeletion + - kms:CancelKeyDeletion + - kms:GenerateDataKey + - kms:TagResource + - kms:UntagResource + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + Resource: "*" + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Principal: + # Not actually everyone -- see below for Conditions + AWS: "*" + Resource: "*" + Condition: + StringEquals: + # See https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-caller-account + kms:CallerAccount: + Ref: AWS::AccountId + kms:ViaService: + - Fn::Sub: s3.${AWS::Region}.amazonaws.com + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Principal: + AWS: + Fn::Sub: "${FilePublishingRole.Arn}" + Resource: "*" + Condition: CreateNewKey + FileAssetsBucketEncryptionKeyAlias: + Condition: CreateNewKey + Type: AWS::KMS::Alias + Properties: + AliasName: + Fn::Sub: "alias/cdk-${Qualifier}-assets-key" + TargetKeyId: + Ref: FileAssetsBucketEncryptionKey + StagingBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: + Fn::If: + - HasCustomFileAssetsBucketName + - Fn::Sub: "${FileAssetsBucketName}" + - Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region} + AccessControl: Private + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: aws:kms + KMSMasterKeyID: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::If: + - UseAwsManagedKey + - Ref: AWS::NoValue + - Fn::Sub: "${FileAssetsBucketKmsKeyId}" + PublicAccessBlockConfiguration: + Fn::If: + - UsePublicAccessBlockConfiguration + - BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + - Ref: AWS::NoValue + VersioningConfiguration: + Status: Enabled + LifecycleConfiguration: + Rules: + # Exising objects will never be overwritten but Security Hub wants this rule to exist + - Id: CleanupOldVersions + Status: Enabled + NoncurrentVersionExpiration: + NoncurrentDays: 365 + UpdateReplacePolicy: Retain + DeletionPolicy: Retain + StagingBucketPolicy: + Type: 'AWS::S3::BucketPolicy' + Properties: + Bucket: { Ref: 'StagingBucket' } + PolicyDocument: + Id: 'AccessControl' + Version: '2012-10-17' + Statement: + - Sid: 'AllowSSLRequestsOnly' + Action: 's3:*' + Effect: 'Deny' + Resource: + - { 'Fn::Sub': '${StagingBucket.Arn}' } + - { 'Fn::Sub': '${StagingBucket.Arn}/*' } + Condition: + Bool: { 'aws:SecureTransport': 'false' } + Principal: '*' + ContainerAssetsRepository: + Type: AWS::ECR::Repository + Properties: + ImageTagMutability: IMMUTABLE + # Untagged images should never exist but Security Hub wants this rule to exist + LifecyclePolicy: + LifecyclePolicyText: | + { + "rules": [ + { + "rulePriority": 1, + "description": "Untagged images should not exist, but expire any older than one year", + "selection": { + "tagStatus": "untagged", + "countType": "sinceImagePushed", + "countUnit": "days", + "countNumber": 365 + }, + "action": { "type": "expire" } + } + ] + } + RepositoryName: + Fn::If: + - HasCustomContainerAssetsRepositoryName + - Fn::Sub: "${ContainerAssetsRepositoryName}" + - Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region} + RepositoryPolicyText: + Version: "2012-10-17" + Statement: + # Necessary for Lambda container images + # https://docs.aws.amazon.com/lambda/latest/dg/configuration-images.html#configuration-images-permissions + - Sid: LambdaECRImageRetrievalPolicy + Effect: Allow + Principal: { Service: "lambda.amazonaws.com" } + Action: + - ecr:BatchGetImage + - ecr:GetDownloadUrlForLayer + Condition: + StringLike: + "aws:sourceArn": { "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" } + FilePublishingRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: file-publishing + ImagePublishingRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: image-publishing + LookupRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccountsForLookup + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccountsForLookup + - Ref: AWS::NoValue + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + RoleName: + Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region} + ManagedPolicyArns: + - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess" + Policies: + - PolicyDocument: + Statement: + - Sid: DontReadSecrets + Effect: Deny + Action: + - kms:Decrypt + Resource: "*" + Version: '2012-10-17' + PolicyName: LookupRolePolicy + Tags: + - Key: aws-cdk:bootstrap-role + Value: lookup + FilePublishingRoleDefaultPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyDocument: + Statement: + - Action: + - s3:GetObject* + - s3:GetBucket* + - s3:GetEncryptionConfiguration + - s3:List* + - s3:DeleteObject* + - s3:PutObject* + - s3:Abort* + Resource: + - Fn::Sub: "${StagingBucket.Arn}" + - Fn::Sub: "${StagingBucket.Arn}/*" + Condition: + StringEquals: + aws:ResourceAccount: + - Fn::Sub: ${AWS::AccountId} + Effect: Allow + - Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Effect: Allow + Resource: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId} + Version: '2012-10-17' + Roles: + - Ref: FilePublishingRole + PolicyName: + Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + ImagePublishingRoleDefaultPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyDocument: + Statement: + - Action: + - ecr:PutImage + - ecr:InitiateLayerUpload + - ecr:UploadLayerPart + - ecr:CompleteLayerUpload + - ecr:BatchCheckLayerAvailability + - ecr:DescribeRepositories + - ecr:DescribeImages + - ecr:BatchGetImage + - ecr:GetDownloadUrlForLayer + Resource: + Fn::Sub: "${ContainerAssetsRepository.Arn}" + Effect: Allow + - Action: + - ecr:GetAuthorizationToken + Resource: "*" + Effect: Allow + Version: '2012-10-17' + Roles: + - Ref: ImagePublishingRole + PolicyName: + Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region} + DeploymentActionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + # The TagSession action is required to be able to assume this role with session tags. + # Without this trust policy, attemping to assume this role with session tags will fail. + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId + - Fn::If: + - HasTrustedAccounts + - Action: sts:AssumeRole + Effect: Allow + Principal: + AWS: + Ref: TrustedAccounts + - Ref: AWS::NoValue + Policies: + - PolicyDocument: + Statement: + - Sid: CloudFormationPermissions + Effect: Allow + Action: + - cloudformation:CreateChangeSet + - cloudformation:DeleteChangeSet + - cloudformation:DescribeChangeSet + - cloudformation:DescribeStacks + - cloudformation:ExecuteChangeSet + - cloudformation:CreateStack + - cloudformation:UpdateStack + Resource: "*" + - Sid: PipelineCrossAccountArtifactsBucket + # Read/write buckets in different accounts. Permissions to buckets in + # same account are granted by bucket policies. + # + # Write permissions necessary to write outputs to the cross-region artifact replication bucket + # https://aws.amazon.com/premiumsupport/knowledge-center/codepipeline-deploy-cloudformation/. + Effect: Allow + Action: + - s3:GetObject* + - s3:GetBucket* + - s3:List* + - s3:Abort* + - s3:DeleteObject* + - s3:PutObject* + Resource: "*" + Condition: + StringNotEquals: + s3:ResourceAccount: + Ref: 'AWS::AccountId' + - Sid: PipelineCrossAccountArtifactsKey + # Use keys only for the purposes of reading encrypted files from S3. + Effect: Allow + Action: + - kms:Decrypt + - kms:DescribeKey + - kms:Encrypt + - kms:ReEncrypt* + - kms:GenerateDataKey* + Resource: "*" + Condition: + StringEquals: + kms:ViaService: + Fn::Sub: s3.${AWS::Region}.amazonaws.com + - Action: iam:PassRole + Resource: + Fn::Sub: "${CloudFormationExecutionRole.Arn}" + Effect: Allow + # Permissions to allow the Deploy Role to perform SQS Actions. + # Users of this bootstrap template intend to uses the Deploy Role + # instead of the CFN ExecutionRole, so the deploy role needs permissions + # to perform CFN actions; in this simple case, we only permit SQS Actions. + - Sid: SQSPermissions + Action: sqs:* + Resource: "*" + Effect: Allow + # This condition requires that the Deploy Role is assumed with the session tags + # 'Department: Engineering'; if these tags are not passed in, the DeployRole will + # not be able to perform SQS actions. + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" + - Sid: CliPermissions + Action: + # Permissions needed by the CLI when doing `cdk deploy`. + # Our CI/CD does not need DeleteStack, + # but we also want to use this role from the CLI, + # and there you can call `cdk destroy` + - cloudformation:DescribeStackEvents + - cloudformation:GetTemplate + - cloudformation:DeleteStack + - cloudformation:UpdateTerminationProtection + - sts:GetCallerIdentity + # `cdk import` + - cloudformation:GetTemplateSummary + Resource: "*" + Effect: Allow + - Sid: CliStagingBucket + Effect: Allow + Action: + - s3:GetObject* + - s3:GetBucket* + - s3:List* + Resource: + - Fn::Sub: ${StagingBucket.Arn} + - Fn::Sub: ${StagingBucket.Arn}/* + - Sid: ReadVersion + Effect: Allow + Action: + - ssm:GetParameter + - ssm:GetParameters # CreateChangeSet uses this to evaluate any SSM parameters (like `CdkBootstrapVersion`) + Resource: + - Fn::Sub: "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}" + Version: '2012-10-17' + PolicyName: default + RoleName: + Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region} + Tags: + - Key: aws-cdk:bootstrap-role + Value: deploy + CloudFormationExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: cloudformation.amazonaws.com + Version: '2012-10-17' + ManagedPolicyArns: + Fn::If: + - HasCloudFormationExecutionPolicies + - Ref: CloudFormationExecutionPolicies + - Fn::If: + - HasTrustedAccounts + # The CLI will prevent this case from occurring + - Ref: AWS::NoValue + # The CLI will advertise that we picked this implicitly + - - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess" + RoleName: + Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region} + PermissionsBoundary: + Fn::If: + - PermissionsBoundarySet + - Fn::Sub: 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary}' + - Ref: AWS::NoValue + CdkBoostrapPermissionsBoundaryPolicy: + # Edit the template prior to boostrap in order to have this example policy created + Condition: ShouldCreatePermissionsBoundary + Type: AWS::IAM::ManagedPolicy + Properties: + PolicyDocument: + Statement: + # If permission boundaries do not have an explicit `allow`, then the effect is `deny` + - Sid: ExplicitAllowAll + Action: + - "*" + Effect: Allow + Resource: "*" + # Default permissions to prevent privilege escalation + - Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied + Action: + - iam:CreateUser + - iam:CreateRole + - iam:PutRolePermissionsBoundary + - iam:PutUserPermissionsBoundary + Condition: + StringNotEquals: + iam:PermissionsBoundary: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + Effect: Deny + Resource: "*" + # Forbid the policy itself being edited + - Sid: DenyPermBoundaryIAMPolicyAlteration + Action: + - iam:CreatePolicyVersion + - iam:DeletePolicy + - iam:DeletePolicyVersion + - iam:SetDefaultPolicyVersion + Effect: Deny + Resource: + Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + # Forbid removing the permissions boundary from any user or role that has it associated + - Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole + Action: + - iam:DeleteUserPermissionsBoundary + - iam:DeleteRolePermissionsBoundary + Effect: Deny + Resource: "*" + # Add your specific organizational security policy here + # Uncomment the example to deny access to AWS Config + #- Sid: OrganizationalSecurityPolicy + # Action: + # - "config:*" + # Effect: Deny + # Resource: "*" + Version: "2012-10-17" + Description: "Bootstrap Permission Boundary" + ManagedPolicyName: + Fn::Sub: cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region} + Path: / + # The SSM parameter is used in pipeline-deployed templates to verify the version + # of the bootstrap resources. + CdkBootstrapVersion: + Type: AWS::SSM::Parameter + Properties: + Type: String + Name: + Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' + Value: '22' +Outputs: + BucketName: + Description: The name of the S3 bucket owned by the CDK toolkit stack + Value: + Fn::Sub: "${StagingBucket}" + BucketDomainName: + Description: The domain name of the S3 bucket owned by the CDK toolkit stack + Value: + Fn::Sub: "${StagingBucket.RegionalDomainName}" + # @deprecated - This Export can be removed at some future point in time. + # We can't do it today because if there are stacks that use it, the bootstrap + # stack cannot be updated. Not used anymore by apps >= 1.60.0 + FileAssetKeyArn: + Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated) + Value: + Fn::If: + - CreateNewKey + - Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}" + - Fn::Sub: "${FileAssetsBucketKmsKeyId}" + Export: + Name: + Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn + ImageRepositoryName: + Description: The name of the ECR repository which hosts docker image assets + Value: + Fn::Sub: "${ContainerAssetsRepository}" + # The Output is used by the CLI to verify the version of the bootstrap resources. + BootstrapVersion: + Description: The version of the bootstrap resources that are currently mastered + in this stack + Value: + Fn::GetAtt: [CdkBootstrapVersion, Value] \ No newline at end of file diff --git a/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js b/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js index 9074ba75961f3..c0d6307db8f57 100755 --- a/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js +++ b/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js @@ -424,6 +424,67 @@ class LambdaStack extends cdk.Stack { } } +class SessionTagsStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new DefaultStackSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + } + }) + }); + + // VPC lookup to test LookupRole + ec2.Vpc.fromLookup(this, 'DefaultVPC', { isDefault: true }); + + // Lambda Function to test AssetPublishingRole + const fn = new lambda.Function(this, 'my-function', { + code: lambda.Code.asset(path.join(__dirname, 'lambda')), + runtime: lambda.Runtime.NODEJS_LATEST, + handler: 'index.handler' + }); + + // DockerImageAsset to test ImageAssetPublishingRole + new docker.DockerImageAsset(this, 'image', { + directory: path.join(__dirname, 'docker') + }); + } +} + +class NoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { + + emitArtifact(session, options) { + super.emitArtifact(session, { + ...options, + cloudFormationExecutionRoleArn: undefined, + }) + } +} + +class SessionTagsWithNoExecutionRoleCustomSynthesizerStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new NoExecutionRoleCustomSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + }) + }); + + new sqs.Queue(this, 'sessionTagsQueue'); + } +} class LambdaHotswapStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); @@ -708,6 +769,14 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); + + if (process.env.ENABLE_VPC_TESTING == 'IMPORT') { + // this stack performs a VPC lookup so we gate synth + const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }; + new SessionTagsStack(app, `${stackPrefix}-session-tags`, { env }); + } + + new SessionTagsWithNoExecutionRoleCustomSynthesizerStack(app, `${stackPrefix}-session-tags-with-custom-synthesizer`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new AppSyncHotswapStack(app, `${stackPrefix}-appsync-hotswap`); diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts index 5e8d1304b4973..1056faeb3a223 100644 --- a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts +++ b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts @@ -87,6 +87,43 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); +integTest('can deploy with session tags on the deploy, lookup, file asset, and image asset publishing roles', withoutBootstrap(async (fixture) => { + const bootstrapStackName = fixture.bootstrapStackName; + + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.all-roles-deny-all.yaml'), + }); + + await fixture.cdkDeploy('session-tags', { + options: [ + '--toolkit-stack-name', bootstrapStackName, + '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`, + '--context', '@aws-cdk/core:newStyleStackSynthesis=1', + ], + modEnv: { + ENABLE_VPC_TESTING: 'IMPORT', + }, + }); +})); + +integTest('can deploy without execution role and with session tags on deploy role', withoutBootstrap(async (fixture) => { + const bootstrapStackName = fixture.bootstrapStackName; + + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.deploy-role-deny-sqs.yaml'), + }); + + await fixture.cdkDeploy('session-tags-with-custom-synthesizer', { + options: [ + '--toolkit-stack-name', bootstrapStackName, + '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`, + '--context', '@aws-cdk/core:newStyleStackSynthesis=1', + ], + }); +})); + integTest('deploy new style synthesis to new style bootstrap', withoutBootstrap(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; diff --git a/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md b/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md deleted file mode 100644 index beadd60aa4ed2..0000000000000 --- a/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md +++ /dev/null @@ -1,1401 +0,0 @@ -# CDK Feature Flags - -[CDK Feature Flags](https://docs.aws.amazon.com/cdk/v2/guide/featureflags.html) are a mechanism that allows the CDK to evolve and change the behavior of certain classes and methods, without causing disruption to existing deployed infrastructure. - -Feature flags are [context values](https://docs.aws.amazon.com/cdk/v2/guide/context.html) and can be configured using any of the context management methods, at any level of the construct tree. Commonly, they are specified in the `cdk.json` file. -`cdk init` will create new CDK projects with a `cdk.json` file containing all recommended feature flags enabled. - -## Current list of feature flags - -Flags come in three types: - -- **Default change**: The default behavior of an API has been changed in order to improve its ergonomics. The old behavior can still be achieved, but requires source changes. -- **Fix/deprecation**: The old behavior was incorrect or not recommended for new users. The only way to keep it is to not set this flag. -- **Config**: Configurable behavior that we recommend you turn on. - - - -| Flag | Summary | Since | Type | -| ----- | ----- | ----- | ----- | -| [@aws-cdk/core:newStyleStackSynthesis](#aws-cdkcorenewstylestacksynthesis) | Switch to new stack synthesis method which enables CI/CD | 2.0.0 | (fix) | -| [@aws-cdk/core:stackRelativeExports](#aws-cdkcorestackrelativeexports) | Name exports based on the construct paths relative to the stack, rather than the global construct path | 2.0.0 | (fix) | -| [@aws-cdk/aws-rds:lowercaseDbIdentifier](#aws-cdkaws-rdslowercasedbidentifier) | Force lowercasing of RDS Cluster names in CDK | 2.0.0 | (fix) | -| [@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId](#aws-cdkaws-apigatewayusageplankeyorderinsensitiveid) | Allow adding/removing multiple UsagePlanKeys independently | 2.0.0 | (fix) | -| [@aws-cdk/aws-lambda:recognizeVersionProps](#aws-cdkaws-lambdarecognizeversionprops) | Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`. | 2.0.0 | (fix) | -| [@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2\_2021](#aws-cdkaws-cloudfrontdefaultsecuritypolicytlsv12_2021) | Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default. | 2.0.0 | (fix) | -| [@aws-cdk/core:target-partitions](#aws-cdkcoretarget-partitions) | What regions to include in lookup tables of environment agnostic stacks | 2.4.0 | (config) | -| [@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver](#aws-cdk-containersecs-service-extensionsenabledefaultlogdriver) | ECS extensions will automatically add an `awslogs` driver if no logging is specified | 2.8.0 | (default) | -| [@aws-cdk/aws-ec2:uniqueImdsv2TemplateName](#aws-cdkaws-ec2uniqueimdsv2templatename) | Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names. | 2.8.0 | (fix) | -| [@aws-cdk/aws-iam:minimizePolicies](#aws-cdkaws-iamminimizepolicies) | Minimize IAM policies by combining Statements | 2.18.0 | (config) | -| [@aws-cdk/core:checkSecretUsage](#aws-cdkcorechecksecretusage) | Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations | 2.21.0 | (config) | -| [@aws-cdk/aws-lambda:recognizeLayerVersion](#aws-cdkaws-lambdarecognizelayerversion) | Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`. | 2.27.0 | (fix) | -| [@aws-cdk/core:validateSnapshotRemovalPolicy](#aws-cdkcorevalidatesnapshotremovalpolicy) | Error on snapshot removal policies on resources that do not support it. | 2.28.0 | (default) | -| [@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName](#aws-cdkaws-codepipelinecrossaccountkeyaliasstacksaferesourcename) | Generate key aliases that include the stack name | 2.29.0 | (fix) | -| [@aws-cdk/aws-s3:createDefaultLoggingPolicy](#aws-cdkaws-s3createdefaultloggingpolicy) | Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist. | 2.31.0 | (fix) | -| [@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption](#aws-cdkaws-sns-subscriptionsrestrictsqsdescryption) | Restrict KMS key policy for encrypted Queues a bit more | 2.32.0 | (fix) | -| [@aws-cdk/aws-ecs:arnFormatIncludesClusterName](#aws-cdkaws-ecsarnformatincludesclustername) | ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID. | 2.35.0 | (fix) | -| [@aws-cdk/aws-apigateway:disableCloudWatchRole](#aws-cdkaws-apigatewaydisablecloudwatchrole) | Make default CloudWatch Role behavior safe for multiple API Gateways in one environment | 2.38.0 | (fix) | -| [@aws-cdk/core:enablePartitionLiterals](#aws-cdkcoreenablepartitionliterals) | Make ARNs concrete if AWS partition is known | 2.38.0 | (fix) | -| [@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker](#aws-cdkaws-ecsdisableexplicitdeploymentcontrollerforcircuitbreaker) | Avoid setting the "ECS" deployment controller when adding a circuit breaker | 2.51.0 | (fix) | -| [@aws-cdk/aws-events:eventsTargetQueueSameAccount](#aws-cdkaws-eventseventstargetqueuesameaccount) | Event Rules may only push to encrypted SQS queues in the same account | 2.51.0 | (fix) | -| [@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName](#aws-cdkaws-iamimportedrolestacksafedefaultpolicyname) | Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in. | 2.60.0 | (fix) | -| [@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy](#aws-cdkaws-s3serveraccesslogsusebucketpolicy) | Use S3 Bucket Policy instead of ACLs for Server Access Logging | 2.60.0 | (fix) | -| [@aws-cdk/customresources:installLatestAwsSdkDefault](#aws-cdkcustomresourcesinstalllatestawssdkdefault) | Whether to install the latest SDK by default in AwsCustomResource | 2.60.0 | (default) | -| [@aws-cdk/aws-route53-patters:useCertificate](#aws-cdkaws-route53-pattersusecertificate) | Use the official `Certificate` resource instead of `DnsValidatedCertificate` | 2.61.0 | (default) | -| [@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup](#aws-cdkaws-codedeployremovealarmsfromdeploymentgroup) | Remove CloudWatch alarms from deployment group | 2.65.0 | (fix) | -| [@aws-cdk/aws-rds:databaseProxyUniqueResourceName](#aws-cdkaws-rdsdatabaseproxyuniqueresourcename) | Use unique resource name for Database Proxy | 2.65.0 | (fix) | -| [@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId](#aws-cdkaws-apigatewayauthorizerchangedeploymentlogicalid) | Include authorizer configuration in the calculation of the API deployment logical ID. | 2.66.0 | (fix) | -| [@aws-cdk/aws-ec2:launchTemplateDefaultUserData](#aws-cdkaws-ec2launchtemplatedefaultuserdata) | Define user data for a launch template by default when a machine image is provided. | 2.67.0 | (fix) | -| [@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments](#aws-cdkaws-secretsmanageruseattachedsecretresourcepolicyforsecrettargetattachments) | SecretTargetAttachments uses the ResourcePolicy of the attached Secret. | 2.67.0 | (fix) | -| [@aws-cdk/aws-redshift:columnId](#aws-cdkaws-redshiftcolumnid) | Whether to use an ID to track Redshift column changes | 2.68.0 | (fix) | -| [@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2](#aws-cdkaws-stepfunctions-tasksenableemrservicepolicyv2) | Enable AmazonEMRServicePolicy_v2 managed policies | 2.72.0 | (fix) | -| [@aws-cdk/aws-apigateway:requestValidatorUniqueId](#aws-cdkaws-apigatewayrequestvalidatoruniqueid) | Generate a unique id for each RequestValidator added to a method | 2.78.0 | (fix) | -| [@aws-cdk/aws-ec2:restrictDefaultSecurityGroup](#aws-cdkaws-ec2restrictdefaultsecuritygroup) | Restrict access to the VPC default security group | 2.78.0 | (default) | -| [@aws-cdk/aws-kms:aliasNameRef](#aws-cdkaws-kmsaliasnameref) | KMS Alias name and keyArn will have implicit reference to KMS Key | 2.83.0 | (fix) | -| [@aws-cdk/core:includePrefixInUniqueNameGeneration](#aws-cdkcoreincludeprefixinuniquenamegeneration) | Include the stack prefix in the stack name generation process | 2.84.0 | (fix) | -| [@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig](#aws-cdkaws-autoscalinggeneratelaunchtemplateinsteadoflaunchconfig) | Generate a launch template when creating an AutoScalingGroup | 2.88.0 | (fix) | -| [@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby](#aws-cdkaws-opensearchserviceenableopensearchmultiazwithstandby) | Enables support for Multi-AZ with Standby deployment for opensearch domains | 2.88.0 | (default) | -| [@aws-cdk/aws-efs:denyAnonymousAccess](#aws-cdkaws-efsdenyanonymousaccess) | EFS denies anonymous clients accesses | 2.93.0 | (default) | -| [@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId](#aws-cdkaws-efsmounttargetorderinsensitivelogicalid) | When enabled, mount targets will have a stable logicalId that is linked to the associated subnet. | 2.93.0 | (fix) | -| [@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion](#aws-cdkaws-lambda-nodejsuselatestruntimeversion) | Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default | 2.93.0 | (default) | -| [@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier](#aws-cdkaws-appsyncusearnforsourceapiassociationidentifier) | When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id. | 2.97.0 | (fix) | -| [@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters](#aws-cdkaws-rdsauroraclusterchangescopeofinstanceparametergroupwitheachparameters) | When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change. | 2.97.0 | (fix) | -| [@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials](#aws-cdkaws-rdspreventrenderingdeprecatedcredentials) | When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials. | 2.98.0 | (fix) | -| [@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource](#aws-cdkaws-codepipeline-actionsusenewdefaultbranchforcodecommitsource) | When enabled, the CodeCommit source action is using the default branch name 'main'. | 2.103.1 | (fix) | -| [@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction](#aws-cdkaws-cloudwatch-actionschangelambdapermissionlogicalidforlambdaaction) | When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID. | 2.124.0 | (fix) | -| [@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse](#aws-cdkaws-codepipelinecrossaccountkeysdefaultvaluetofalse) | Enables Pipeline to set the default value for crossAccountKeys to false. | 2.127.0 | (default) | -| [@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2](#aws-cdkaws-codepipelinedefaultpipelinetypetov2) | Enables Pipeline to set the default pipeline type to V2. | 2.133.0 | (default) | -| [@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope](#aws-cdkaws-kmsreducecrossaccountregionpolicyscope) | When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only. | 2.134.0 | (fix) | -| [@aws-cdk/aws-eks:nodegroupNameAttribute](#aws-cdkaws-eksnodegroupnameattribute) | When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix. | 2.139.0 | (fix) | -| [@aws-cdk/aws-ec2:ebsDefaultGp3Volume](#aws-cdkaws-ec2ebsdefaultgp3volume) | When enabled, the default volume type of the EBS volume will be GP3 | 2.140.0 | (default) | -| [@aws-cdk/pipelines:reduceAssetRoleTrustScope](#aws-cdkpipelinesreduceassetroletrustscope) | Remove the root account principal from PipelineAssetsFileRole trust policy | 2.141.0 | (default) | -| [@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm](#aws-cdkaws-ecsremovedefaultdeploymentalarm) | When enabled, remove default deployment alarm settings | 2.143.0 | (default) | -| [@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault](#aws-cdkcustom-resourceslogapiresponsedatapropertytruedefault) | When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default | 2.145.0 | (fix) | -| [@aws-cdk/aws-s3:keepNotificationInImportedBucket](#aws-cdkaws-s3keepnotificationinimportedbucket) | When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack. | 2.155.0 | (fix) | -| [@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask](#aws-cdkaws-stepfunctions-tasksusenews3uriparametersforbedrockinvokemodeltask) | When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model. | 2.156.0 | (fix) | -| [@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions](#aws-cdkaws-ecsreduceec2fargatecloudwatchpermissions) | When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration | 2.159.0 | (fix) | - - - -## Currently recommended cdk.json - -The following json shows the current recommended set of flags, as `cdk init` would generate it for new projects. - - -```json -{ - "context": { - "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ], - "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, - "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, - "@aws-cdk/aws-iam:minimizePolicies": true, - "@aws-cdk/core:validateSnapshotRemovalPolicy": true, - "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, - "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, - "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, - "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, - "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, - "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, - "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, - "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, - "@aws-cdk/aws-route53-patters:useCertificate": true, - "@aws-cdk/customresources:installLatestAwsSdkDefault": false, - "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, - "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, - "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, - "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, - "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, - "@aws-cdk/aws-redshift:columnId": true, - "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, - "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, - "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, - "@aws-cdk/aws-kms:aliasNameRef": true, - "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, - "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, - "@aws-cdk/aws-efs:denyAnonymousAccess": true, - "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, - "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, - "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, - "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, - "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, - "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, - "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, - "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, - "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, - "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, - "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, - "@aws-cdk/aws-eks:nodegroupNameAttribute": true, - "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, - "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, - "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, - "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, - "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true - } -} -``` - - -## Flags removed in v2 - -These **default change** flags have been removed in v2. These used to be configurable in v1, but in v2 their -behavior has become the default. Remove these from your `cdk.json` file. If the old behavior is important -for your infrastructure, see the flag's description on how to achieve it. - - - -| Flag | Summary | Type | Since | -| ----- | ----- | ----- | ----- | -| [@aws-cdk/core:enableStackNameDuplicates](#aws-cdkcoreenablestacknameduplicates) | Allow multiple stacks with the same name | (default) | 1.16.0 | -| [aws-cdk:enableDiffNoFail](#aws-cdkenablediffnofail) | Make `cdk diff` not fail when there are differences | (default) | 1.19.0 | -| [@aws-cdk/aws-ecr-assets:dockerIgnoreSupport](#aws-cdkaws-ecr-assetsdockerignoresupport) | DockerImageAsset properly supports `.dockerignore` files by default | (default) | 1.73.0 | -| [@aws-cdk/aws-secretsmanager:parseOwnedSecretName](#aws-cdkaws-secretsmanagerparseownedsecretname) | Fix the referencing of SecretsManager names from ARNs | (default) | 1.77.0 | -| [@aws-cdk/aws-kms:defaultKeyPolicies](#aws-cdkaws-kmsdefaultkeypolicies) | Tighten default KMS key policies | (default) | 1.78.0 | -| [@aws-cdk/aws-s3:grantWriteWithoutAcl](#aws-cdkaws-s3grantwritewithoutacl) | Remove `PutObjectAcl` from Bucket.grantWrite | (default) | 1.85.0 | -| [@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount](#aws-cdkaws-ecs-patternsremovedefaultdesiredcount) | Do not specify a default DesiredCount for ECS services | (default) | 1.92.0 | -| [@aws-cdk/aws-efs:defaultEncryptionAtRest](#aws-cdkaws-efsdefaultencryptionatrest) | Enable this feature flag to have elastic file systems encrypted at rest by default. | (default) | 1.98.0 | - - - -## Flags with a different default in v2 - -These **fix/deprecation** flags are still configurable in v2, but their default has changed compared to v1. If you -are migrating a v1 CDK project to v2, explicitly set any of these flags which does not currently appear in your -`cdk.json` to `false`, to avoid unexpected infrastructure changes. - - - -| Flag | Summary | Type | Since | v1 default | v2 default | -| ----- | ----- | ----- | ----- | ----- | ----- | -| [@aws-cdk/core:newStyleStackSynthesis](#aws-cdkcorenewstylestacksynthesis) | Switch to new stack synthesis method which enables CI/CD | (fix) | 1.39.0 | `false` | `true` | -| [@aws-cdk/core:stackRelativeExports](#aws-cdkcorestackrelativeexports) | Name exports based on the construct paths relative to the stack, rather than the global construct path | (fix) | 1.58.0 | `false` | `true` | -| [@aws-cdk/aws-rds:lowercaseDbIdentifier](#aws-cdkaws-rdslowercasedbidentifier) | Force lowercasing of RDS Cluster names in CDK | (fix) | 1.97.0 | `false` | `true` | -| [@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId](#aws-cdkaws-apigatewayusageplankeyorderinsensitiveid) | Allow adding/removing multiple UsagePlanKeys independently | (fix) | 1.98.0 | `false` | `true` | -| [@aws-cdk/aws-lambda:recognizeVersionProps](#aws-cdkaws-lambdarecognizeversionprops) | Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`. | (fix) | 1.106.0 | `false` | `true` | -| [@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2\_2021](#aws-cdkaws-cloudfrontdefaultsecuritypolicytlsv12_2021) | Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default. | (fix) | 1.117.0 | `false` | `true` | -| [@aws-cdk/pipelines:reduceAssetRoleTrustScope](#aws-cdkpipelinesreduceassetroletrustscope) | Remove the root account principal from PipelineAssetsFileRole trust policy | (default) | | `false` | `true` | -| [@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask](#aws-cdkaws-stepfunctions-tasksusenews3uriparametersforbedrockinvokemodeltask) | When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model. | (fix) | | `false` | `true` | - - - -Here is an example of a `cdk.json` file that restores v1 behavior for these flags: - - -```json -{ - "context": { - "@aws-cdk/core:newStyleStackSynthesis": false, - "@aws-cdk/core:stackRelativeExports": false, - "@aws-cdk/aws-rds:lowercaseDbIdentifier": false, - "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": false, - "@aws-cdk/aws-lambda:recognizeVersionProps": false, - "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": false, - "@aws-cdk/pipelines:reduceAssetRoleTrustScope": false, - "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": false - } -} -``` - -## Feature flag details - -Here are more details about each of the flags: - - -### @aws-cdk/core:enableStackNameDuplicates - -*Allow multiple stacks with the same name* (default) - -If this is set, multiple stacks can use the same stack name (e.g. deployed to -different environments). This means that the name of the synthesized template -file will be based on the construct path and not on the defined `stackName` -of the stack. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.16.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Pass stack identifiers to the CLI instead of stack names. - - -### aws-cdk:enableDiffNoFail - -*Make `cdk diff` not fail when there are differences* (default) - -Determines what status code `cdk diff` should return when the specified stack -differs from the deployed stack or the local CloudFormation template: - -* `aws-cdk:enableDiffNoFail=true` => status code == 0 -* `aws-cdk:enableDiffNoFail=false` => status code == 1 - -You can override this behavior with the --fail flag: - -* `--fail` => status code == 1 -* `--no-fail` => status code == 0 - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.19.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Specify `--fail` to the CLI. - - -### @aws-cdk/aws-ecr-assets:dockerIgnoreSupport - -*DockerImageAsset properly supports `.dockerignore` files by default* (default) - -If this flag is not set, the default behavior for `DockerImageAsset` is to use -glob semantics for `.dockerignore` files. If this flag is set, the default behavior -is standard Docker ignore semantics. - -This is a feature flag as the old behavior was technically incorrect but -users may have come to depend on it. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.73.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Update your `.dockerignore` file to match standard Docker ignore rules, if necessary. - - -### @aws-cdk/aws-secretsmanager:parseOwnedSecretName - -*Fix the referencing of SecretsManager names from ARNs* (default) - -Secret.secretName for an "owned" secret will attempt to parse the secretName from the ARN, -rather than the default full resource name, which includes the SecretsManager suffix. - -If this flag is not set, Secret.secretName will include the SecretsManager suffix, which cannot be directly -used by SecretsManager.DescribeSecret, and must be parsed by the user first (e.g., Fn:Join, Fn:Select, Fn:Split). - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.77.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Use `parseArn(secret.secretName).resourceName` to emulate the incorrect old parsing. - - -### @aws-cdk/aws-kms:defaultKeyPolicies - -*Tighten default KMS key policies* (default) - -KMS Keys start with a default key policy that grants the account access to administer the key, -mirroring the behavior of the KMS SDK/CLI/Console experience. Users may override the default key -policy by specifying their own. - -If this flag is not set, the default key policy depends on the setting of the `trustAccountIdentities` -flag. If false (the default, for backwards-compatibility reasons), the default key policy somewhat -resembles the default admin key policy, but with the addition of 'GenerateDataKey' permissions. If -true, the policy matches what happens when this feature flag is set. - -Additionally, if this flag is not set and the user supplies a custom key policy, this will be appended -to the key's default policy (rather than replacing it). - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.78.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Pass `trustAccountIdentities: false` to `Key` construct to restore the old behavior. - - -### @aws-cdk/aws-s3:grantWriteWithoutAcl - -*Remove `PutObjectAcl` from Bucket.grantWrite* (default) - -Change the old 's3:PutObject*' permission to 's3:PutObject' on Bucket, -as the former includes 's3:PutObjectAcl', -which could be used to grant read/write object access to IAM principals in other accounts. -Use a feature flag to make sure existing customers who might be relying -on the overly-broad permissions are not broken. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.85.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Call `bucket.grantPutAcl()` in addition to `bucket.grantWrite()` to grant ACL permissions. - - -### @aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount - -*Do not specify a default DesiredCount for ECS services* (default) - -ApplicationLoadBalancedServiceBase, ApplicationMultipleTargetGroupServiceBase, -NetworkLoadBalancedServiceBase, NetworkMultipleTargetGroupServiceBase, and -QueueProcessingServiceBase currently determine a default value for the desired count of -a CfnService if a desiredCount is not provided. The result of this is that on every -deployment, the service count is reset to the fixed value, even if it was autoscaled. - -If this flag is not set, the default behaviour for CfnService.desiredCount is to set a -desiredCount of 1, if one is not provided. If true, a default will not be defined for -CfnService.desiredCount and as such desiredCount will be undefined, if one is not provided. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.92.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** You can pass `desiredCount: 1` explicitly, but you should never need this. - - -### @aws-cdk/aws-efs:defaultEncryptionAtRest - -*Enable this feature flag to have elastic file systems encrypted at rest by default.* (default) - -Encryption can also be configured explicitly using the `encrypted` property. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.98.0 | `false` | `true` | -| (default in v2) | `true` | | - -**Compatibility with old behavior:** Pass the `encrypted: false` property to the `FileSystem` construct to disable encryption. - - -### @aws-cdk/core:newStyleStackSynthesis - -*Switch to new stack synthesis method which enables CI/CD* (fix) - -If this flag is specified, all `Stack`s will use the `DefaultStackSynthesizer` by -default. If it is not set, they will use the `LegacyStackSynthesizer`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.39.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/core:stackRelativeExports - -*Name exports based on the construct paths relative to the stack, rather than the global construct path* (fix) - -Combined with the stack name this relative construct path is good enough to -ensure uniqueness, and makes the export names robust against refactoring -the location of the stack in the construct tree (specifically, moving the Stack -into a Stage). - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.58.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/aws-rds:lowercaseDbIdentifier - -*Force lowercasing of RDS Cluster names in CDK* (fix) - -Cluster names must be lowercase, and the service will lowercase the name when the cluster -is created. However, CDK did not use to know about this, and would use the user-provided name -referencing the cluster, which would fail if it happened to be mixed-case. - -With this flag, lowercase the name in CDK so we can reference it properly. - -Must be behind a permanent flag because changing a name from mixed case to lowercase between deployments -would lead CloudFormation to think the name was changed and would trigger a cluster replacement -(losing data!). - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.97.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId - -*Allow adding/removing multiple UsagePlanKeys independently* (fix) - -The UsagePlanKey resource connects an ApiKey with a UsagePlan. API Gateway does not allow more than one UsagePlanKey -for any given UsagePlan and ApiKey combination. For this reason, CloudFormation cannot replace this resource without -either the UsagePlan or ApiKey changing. - -The feature addition to support multiple UsagePlanKey resources - 142bd0e2 - recognized this and attempted to keep -existing UsagePlanKey logical ids unchanged. -However, this intentionally caused the logical id of the UsagePlanKey to be sensitive to order. That is, when -the 'first' UsagePlanKey resource is removed, the logical id of the 'second' assumes what was originally the 'first', -which again is disallowed. - -In effect, there is no way to get out of this mess in a backwards compatible way, while supporting existing stacks. -This flag changes the logical id layout of UsagePlanKey to not be sensitive to order. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.98.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/aws-lambda:recognizeVersionProps - -*Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.* (fix) - -The previous calculation incorrectly considered properties of the `AWS::Lambda::Function` resource that did -not constitute creating a new Version. - -See 'currentVersion' section in the aws-lambda module's README for more details. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.106.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021 - -*Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.* (fix) - -The security policy can also be configured explicitly using the `minimumProtocolVersion` property. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.117.0 | `false` | `true` | -| 2.0.0 | `true` | `true` | - - -### @aws-cdk/core:target-partitions - -*What regions to include in lookup tables of environment agnostic stacks* (config) - -Has no effect on stacks that have a defined region, but will limit the amount -of unnecessary regions included in stacks without a known region. - -The type of this value should be a list of strings. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.137.0 | `false` | `["aws","aws-cn"]` | -| 2.4.0 | `false` | `["aws","aws-cn"]` | - - -### @aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver - -*ECS extensions will automatically add an `awslogs` driver if no logging is specified* (default) - -Enable this feature flag to configure default logging behavior for the ECS Service Extensions. This will enable the -`awslogs` log driver for the application container of the service to send the container logs to CloudWatch Logs. - -This is a feature flag as the new behavior provides a better default experience for the users. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.140.0 | `false` | `true` | -| 2.8.0 | `false` | `true` | - -**Compatibility with old behavior:** Specify a log driver explicitly. - - -### @aws-cdk/aws-ec2:uniqueImdsv2TemplateName - -*Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names.* (fix) - -Previously, the generated Launch Template names were only unique within a stack because they were based only on the -`Instance` construct ID. If another stack that has an `Instance` with the same construct ID is deployed in the same -account and region, the deployments would always fail as the generated Launch Template names were the same. - -The new implementation addresses this issue by generating the Launch Template name with the `Names.uniqueId` method. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.140.0 | `false` | `true` | -| 2.8.0 | `false` | `true` | - - -### @aws-cdk/aws-iam:minimizePolicies - -*Minimize IAM policies by combining Statements* (config) - -Minimize IAM policies by combining Principals, Actions and Resources of two -Statements in the policies, as long as it doesn't change the meaning of the -policy. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.150.0 | `false` | `true` | -| 2.18.0 | `false` | `true` | - - -### @aws-cdk/core:checkSecretUsage - -*Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations* (config) - -With this flag enabled, `SecretValue` instances can only be passed to -constructs that accept `SecretValue`s; otherwise, `unsafeUnwrap()` must be -called to use it as a regular string. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.153.0 | `false` | `true` | -| 2.21.0 | `false` | `true` | - - -### @aws-cdk/aws-lambda:recognizeLayerVersion - -*Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.* (fix) - -This flag correct incorporates Lambda Layer properties into the Lambda Function Version. - -See 'currentVersion' section in the aws-lambda module's README for more details. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| 1.159.0 | `false` | `true` | -| 2.27.0 | `false` | `true` | - - -### @aws-cdk/core:validateSnapshotRemovalPolicy - -*Error on snapshot removal policies on resources that do not support it.* (default) - -Makes sure we do not allow snapshot removal policy on resources that do not support it. -If supplied on an unsupported resource, CloudFormation ignores the policy altogether. -This flag will reduce confusion and unexpected loss of data when erroneously supplying -the snapshot removal policy. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.28.0 | `false` | `true` | - -**Compatibility with old behavior:** The old behavior was incorrect. Update your source to not specify SNAPSHOT policies on resources that do not support it. - - -### @aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName - -*Generate key aliases that include the stack name* (fix) - -Enable this feature flag to have CodePipeline generate a unique cross account key alias name using the stack name. - -Previously, when creating multiple pipelines with similar naming conventions and when crossAccountKeys is true, -the KMS key alias name created for these pipelines may be the same due to how the uniqueId is generated. - -This new implementation creates a stack safe resource name for the alias using the stack name instead of the stack ID. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.29.0 | `false` | `true` | - - -### @aws-cdk/aws-s3:createDefaultLoggingPolicy - -*Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist.* (fix) - -For example, in order to send VPC flow logs to an S3 bucket, there is a specific Bucket Policy -that needs to be attached to the bucket. If you create the bucket without a policy and then add the -bucket as the flow log destination, the service will automatically create the bucket policy with the -necessary permissions. If you were to then try and add your own bucket policy CloudFormation will throw -and error indicating that a bucket policy already exists. - -In cases where we know what the required policy is we can go ahead and create the policy so we can -remain in control of it. - -@see https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-and-resource-policy.html#AWS-logs-infrastructure-S3 - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.31.0 | `false` | `true` | - - -### @aws-cdk/aws-sns-subscriptions:restrictSqsDescryption - -*Restrict KMS key policy for encrypted Queues a bit more* (fix) - -Enable this feature flag to restrict the decryption of a SQS queue, which is subscribed to a SNS topic, to -only the topic which it is subscribed to and not the whole SNS service of an account. - -Previously the decryption was only restricted to the SNS service principal. To make the SQS subscription more -secure, it is a good practice to restrict the decryption further and only allow the connected SNS topic to decryption -the subscribed queue. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.32.0 | `false` | `true` | - - -### @aws-cdk/aws-ecs:arnFormatIncludesClusterName - -*ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID.* (fix) - -If this flag is not set, the old ARN format (without cluster name) for ECS is used. -If this flag is set, the new ARN format (with cluster name) for ECS is used. - -This is a feature flag as the old format is still valid for existing ECS clusters. - -See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-account-settings.html#ecs-resource-ids - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.35.0 | `false` | `true` | - - -### @aws-cdk/aws-apigateway:disableCloudWatchRole - -*Make default CloudWatch Role behavior safe for multiple API Gateways in one environment* (fix) - -Enable this feature flag to change the default behavior for aws-apigateway.RestApi and aws-apigateway.SpecRestApi -to _not_ create a CloudWatch role and Account. There is only a single ApiGateway account per AWS -environment which means that each time you create a RestApi in your account the ApiGateway account -is overwritten. If at some point the newest RestApi is deleted, the ApiGateway Account and CloudWatch -role will also be deleted, breaking any existing ApiGateways that were depending on them. - -When this flag is enabled you should either create the ApiGateway account and CloudWatch role -separately _or_ only enable the cloudWatchRole on a single RestApi. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.38.0 | `false` | `true` | - - -### @aws-cdk/core:enablePartitionLiterals - -*Make ARNs concrete if AWS partition is known* (fix) - -Enable this feature flag to get partition names as string literals in Stacks with known regions defined in -their environment, such as "aws" or "aws-cn". Previously the CloudFormation intrinsic function -"Ref: AWS::Partition" was used. For example: - -```yaml -Principal: - AWS: - Fn::Join: - - "" - - - "arn:" - - Ref: AWS::Partition - - :iam::123456789876:root -``` - -becomes: - -```yaml -Principal: - AWS: "arn:aws:iam::123456789876:root" -``` - -The intrinsic function will still be used in Stacks where no region is defined or the region's partition -is unknown. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.38.0 | `false` | `true` | - - -### @aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker - -*Avoid setting the "ECS" deployment controller when adding a circuit breaker* (fix) - -Enable this feature flag to avoid setting the "ECS" deployment controller when adding a circuit breaker to an -ECS Service, as this will trigger a full replacement which fails to deploy when using set service names. -This does not change any behaviour as the default deployment controller when it is not defined is ECS. - -This is a feature flag as the new behavior provides a better default experience for the users. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.51.0 | `false` | `true` | - - -### @aws-cdk/aws-events:eventsTargetQueueSameAccount - -*Event Rules may only push to encrypted SQS queues in the same account* (fix) - -This flag applies to SQS Queues that are used as the target of event Rules. When enabled, only principals -from the same account as the Rule can send messages. If a queue is unencrypted, this restriction will -always apply, regardless of the value of this flag. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.51.0 | `false` | `true` | - - -### @aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName - -*Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in.* (fix) - -Without this, importing the same role in multiple places could lead to the permissions given for one version of the imported role -to overwrite permissions given to the role at a different place where it was imported. This was due to all imported instances -of a role using the same default policy name. - -This new implementation creates default policy names based on the constructs node path in their stack. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.60.0 | `false` | `true` | - - -### @aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy - -*Use S3 Bucket Policy instead of ACLs for Server Access Logging* (fix) - -Enable this feature flag to use S3 Bucket Policy for granting permission fo Server Access Logging -rather than using the canned `LogDeliveryWrite` ACL. ACLs do not work when Object Ownership is -enabled on the bucket. - -This flag uses a Bucket Policy statement to allow Server Access Log delivery, following best -practices for S3. - -@see https://docs.aws.amazon.com/AmazonS3/latest/userguide/enable-server-access-logging.html - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.60.0 | `false` | `true` | - - -### @aws-cdk/customresources:installLatestAwsSdkDefault - -*Whether to install the latest SDK by default in AwsCustomResource* (default) - -This was originally introduced and enabled by default to not be limited by the SDK version -that's installed on AWS Lambda. However, it creates issues for Lambdas bound to VPCs that -do not have internet access, or in environments where 'npmjs.com' is not available. - -The recommended setting is to disable the default installation behavior, and pass the -flag on a resource-by-resource basis to enable it if necessary. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.60.0 | `false` | `false` | - -**Compatibility with old behavior:** Set installLatestAwsSdk: true on all resources that need it. - - -### @aws-cdk/aws-route53-patters:useCertificate - -*Use the official `Certificate` resource instead of `DnsValidatedCertificate`* (default) - -Enable this feature flag to use the official CloudFormation supported `Certificate` resource instead -of the deprecated `DnsValidatedCertificate` construct. If this flag is enabled and you are creating -the stack in a region other than us-east-1 then you must also set `crossRegionReferences=true` on the -stack. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.61.0 | `false` | `true` | - -**Compatibility with old behavior:** Define a `DnsValidatedCertificate` explicitly and pass in the `certificate` property - - -### @aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup - -*Remove CloudWatch alarms from deployment group* (fix) - -Enable this flag to be able to remove all CloudWatch alarms from a deployment group by removing -the alarms from the construct. If this flag is not set, removing all alarms from the construct -will still leave the alarms configured for the deployment group. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.65.0 | `false` | `true` | - - -### @aws-cdk/aws-rds:databaseProxyUniqueResourceName - -*Use unique resource name for Database Proxy* (fix) - -If this flag is not set, the default behavior for `DatabaseProxy` is -to use `id` of the constructor for `dbProxyName` when it's not specified in the argument. -In this case, users can't deploy `DatabaseProxy`s that have the same `id` in the same region. - -If this flag is set, the default behavior is to use unique resource names for each `DatabaseProxy`. - -This is a feature flag as the old behavior was technically incorrect, but users may have come to depend on it. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.65.0 | `false` | `true` | - - -### @aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId - -*Include authorizer configuration in the calculation of the API deployment logical ID.* (fix) - -The logical ID of the AWS::ApiGateway::Deployment resource is calculated by hashing -the API configuration, including methods, and resources, etc. Enable this feature flag -to also include the configuration of any authorizer attached to the API in the -calculation, so any changes made to an authorizer will create a new deployment. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.66.0 | `false` | `true` | - - -### @aws-cdk/aws-ec2:launchTemplateDefaultUserData - -*Define user data for a launch template by default when a machine image is provided.* (fix) - -The ec2.LaunchTemplate construct did not define user data when a machine image is -provided despite the document. If this is set, a user data is automatically defined -according to the OS of the machine image. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.67.0 | `false` | `true` | - - -### @aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments - -*SecretTargetAttachments uses the ResourcePolicy of the attached Secret.* (fix) - -Enable this feature flag to make SecretTargetAttachments use the ResourcePolicy of the attached Secret. -SecretTargetAttachments are created to connect a Secret to a target resource. -In CDK code, they behave like regular Secret and can be used as a stand-in in most situations. -Previously, adding to the ResourcePolicy of a SecretTargetAttachment did attempt to create a separate ResourcePolicy for the same Secret. -However Secrets can only have a single ResourcePolicy, causing the CloudFormation deployment to fail. - -When enabling this feature flag for an existing Stack, ResourcePolicies created via a SecretTargetAttachment will need replacement. -This won't be possible without intervention due to limitation outlined above. -First remove all permissions granted to the Secret and deploy without the ResourcePolicies. -Then you can re-add the permissions and deploy again. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.67.0 | `false` | `true` | - - -### @aws-cdk/aws-redshift:columnId - -*Whether to use an ID to track Redshift column changes* (fix) - -Redshift columns are identified by their `name`. If a column is renamed, the old column -will be dropped and a new column will be created. This can cause data loss. - -This flag enables the use of an `id` attribute for Redshift columns. If this flag is enabled, the -internal CDK architecture will track changes of Redshift columns through their `id`, rather -than their `name`. This will prevent data loss when columns are renamed. - -**NOTE** - Enabling this flag comes at a **risk**. When enabled, update the `id`s of all columns, -**however** do not change the `names`s of the columns. If the `name`s of the columns are changed during -initial deployment, the columns will be dropped and recreated, causing data loss. After the initial deployment -of the `id`s, the `name`s of the columns can be changed without data loss. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.68.0 | `false` | `true` | - - -### @aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2 - -*Enable AmazonEMRServicePolicy_v2 managed policies* (fix) - -If this flag is not set, the default behavior for `EmrCreateCluster` is -to use `AmazonElasticMapReduceRole` managed policies. - -If this flag is set, the default behavior is to use the new `AmazonEMRServicePolicy_v2` -managed policies. - -This is a feature flag as the old behavior will be deprecated, but some resources may require manual -intervention since they might not have the appropriate tags propagated automatically. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.72.0 | `false` | `true` | - - -### @aws-cdk/aws-apigateway:requestValidatorUniqueId - -*Generate a unique id for each RequestValidator added to a method* (fix) - -This flag allows multiple RequestValidators to be added to a RestApi when -providing the `RequestValidatorOptions` in the `addMethod()` method. - -If the flag is not set then only a single RequestValidator can be added in this way. -Any additional RequestValidators have to be created directly with `new RequestValidator`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.78.0 | `false` | `true` | - - -### @aws-cdk/aws-ec2:restrictDefaultSecurityGroup - -*Restrict access to the VPC default security group* (default) - -Enable this feature flag to remove the default ingress/egress rules from the -VPC default security group. - -When a VPC is created, a default security group is created as well and this cannot -be deleted. The default security group is created with ingress/egress rules that allow -_all_ traffic. [AWS Security best practices recommend](https://docs.aws.amazon.com/securityhub/latest/userguide/ec2-controls.html#ec2-2) -removing these ingress/egress rules in order to restrict access to the default security group. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.78.0 | `false` | `true` | - -**Compatibility with old behavior:** - To allow all ingress/egress traffic to the VPC default security group you - can set the `restrictDefaultSecurityGroup: false`. - - - -### @aws-cdk/aws-kms:aliasNameRef - -*KMS Alias name and keyArn will have implicit reference to KMS Key* (fix) - -This flag allows an implicit dependency to be created between KMS Alias and KMS Key -when referencing key.aliasName or key.keyArn. - -If the flag is not set then a raw string is passed as the Alias name and no -implicit dependencies will be set. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.83.0 | `false` | `true` | - - -### @aws-cdk/core:includePrefixInUniqueNameGeneration - -*Include the stack prefix in the stack name generation process* (fix) - -This flag prevents the prefix of a stack from making the stack's name longer than the 128 character limit. - -If the flag is set, the prefix is included in the stack name generation process. -If the flag is not set, then the prefix of the stack is prepended to the generated stack name. - -**NOTE** - Enabling this flag comes at a **risk**. If you have already deployed stacks, changing the status of this -feature flag can lead to a change in stacks' name. Changing a stack name mean recreating the whole stack, which -is not viable in some productive setups. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.84.0 | `false` | `true` | - - -### @aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig - -*Generate a launch template when creating an AutoScalingGroup* (fix) - -Enable this flag to allow AutoScalingGroups to generate a launch template when being created. -Launch configurations have been deprecated and cannot be created in AWS Accounts created after -December 31, 2023. Existing 'AutoScalingGroup' properties used for creating a launch configuration -will now create an equivalent 'launchTemplate'. Alternatively, users can provide an explicit -'launchTemplate' or 'mixedInstancesPolicy'. When this flag is enabled a 'launchTemplate' will -attempt to set user data according to the OS of the machine image if explicit user data is not -provided. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.88.0 | `false` | `true` | - -**Compatibility with old behavior:** - If backwards compatibility needs to be maintained due to an existing autoscaling group - using a launch config, set this flag to false. - - - -### @aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby - -*Enables support for Multi-AZ with Standby deployment for opensearch domains* (default) - -If this is set, an opensearch domain will automatically be created with -multi-az with standby enabled. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.88.0 | `false` | `true` | - -**Compatibility with old behavior:** Pass `capacity.multiAzWithStandbyEnabled: false` to `Domain` construct to restore the old behavior. - - -### @aws-cdk/aws-efs:denyAnonymousAccess - -*EFS denies anonymous clients accesses* (default) - -This flag adds the file system policy that denies anonymous clients -access to `efs.FileSystem`. - -If this flag is not set, `efs.FileSystem` will allow all anonymous clients -that can access over the network. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.93.0 | `false` | `true` | - -**Compatibility with old behavior:** You can pass `allowAnonymousAccess: true` so allow anonymous clients access. - - -### @aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId - -*When enabled, mount targets will have a stable logicalId that is linked to the associated subnet.* (fix) - -When this feature flag is enabled, each mount target will have a stable -logicalId that is linked to the associated subnet. If the flag is set to -false then the logicalIds of the mount targets can change if the number of -subnets changes. - -Set this flag to false for existing mount targets. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.93.0 | `false` | `true` | - - -### @aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion - -*Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default* (default) - -If this is set, and a `runtime` prop is not passed to, Lambda NodeJs -functions will us the latest version of the runtime provided by the Lambda -service. Do not use this if you your lambda function is reliant on dependencies -shipped as part of the runtime environment. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.93.0 | `false` | `true` | - -**Compatibility with old behavior:** Pass `runtime: lambda.Runtime.NODEJS_16_X` to `Function` construct to restore the previous behavior. - - -### @aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier - -*When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id.* (fix) - -When this feature flag is enabled, we use the IGraphqlApi ARN rather than ID when creating or updating CfnSourceApiAssociation in -the GraphqlApi construct. Using the ARN allows the association to support an association with a source api or merged api in another account. -Note that for existing source api associations created with this flag disabled, enabling the flag will lead to a resource replacement. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.97.0 | `false` | `true` | - - -### @aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters - -*When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change.* (fix) - -When this feature flag is enabled, a scope of `InstanceParameterGroup` for -`AuroraClusterInstance` with each parameters will change to AuroraClusterInstance -from AuroraCluster. - -If the flag is set to false then it can only make one `AuroraClusterInstance` -with each `InstanceParameterGroup` in the AuroraCluster. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.97.0 | `false` | `true` | - - -### @aws-cdk/aws-rds:preventRenderingDeprecatedCredentials - -*When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials.* (fix) - -The `credentials` property on the `DatabaseClusterFromSnapshotProps` -interface was deprecated with the new `snapshotCredentials` property being -recommended. Before deprecating `credentials`, a secret would be generated -while rendering credentials if the `credentials` property was undefined or -if a secret wasn't provided via the `credentials` property. This behavior -is replicated with the new `snapshotCredentials` property, but the original -`credentials` secret can still be created resulting in an extra database -secret. - -Set this flag to prevent rendering deprecated `credentials` and creating an -extra database secret when only using `snapshotCredentials` to create an RDS -database cluster from a snapshot. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.98.0 | `false` | `true` | - - -### @aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource - -*When enabled, the CodeCommit source action is using the default branch name 'main'.* (fix) - -When setting up a CodeCommit source action for the source stage of a pipeline, please note that the -default branch is 'master'. -However, with the activation of this feature flag, the default branch is updated to 'main'. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.103.1 | `false` | `true` | - - -### @aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction - -*When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID.* (fix) - -When this feature flag is enabled, a logical ID of `LambdaPermission` for a -`LambdaAction` will include an alarm ID. Therefore multiple alarms for the same Lambda -can be created with `LambdaAction`. - -If the flag is set to false then it can only make one alarm for the Lambda with -`LambdaAction`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.124.0 | `false` | `true` | - - -### @aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse - -*Enables Pipeline to set the default value for crossAccountKeys to false.* (default) - -When this feature flag is enabled, and the `crossAccountKeys` property is not provided in a `Pipeline` -construct, the construct automatically defaults the value of this property to false. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.127.0 | `false` | `true` | - -**Compatibility with old behavior:** Pass `crossAccountKeys: true` to `Pipeline` construct to restore the previous behavior. - - -### @aws-cdk/aws-codepipeline:defaultPipelineTypeToV2 - -*Enables Pipeline to set the default pipeline type to V2.* (default) - -When this feature flag is enabled, and the `pipelineType` property is not provided in a `Pipeline` -construct, the construct automatically defaults the value of this property to `PipelineType.V2`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.133.0 | `false` | `true` | - -**Compatibility with old behavior:** Pass `pipelineType: PipelineType.V1` to `Pipeline` construct to restore the previous behavior. - - -### @aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope - -*When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only.* (fix) - -When this feature flag is enabled and calling KMS key grant method, the created IAM policy will reduce the resource scope from -'*' to this specific granting KMS key. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.134.0 | `false` | `true` | - - -### @aws-cdk/aws-eks:nodegroupNameAttribute - -*When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix.* (fix) - -When this feature flag is enabled, the nodegroupName attribute will be exactly the name of the nodegroup without -any prefix. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.139.0 | `false` | `true` | - - -### @aws-cdk/aws-ec2:ebsDefaultGp3Volume - -*When enabled, the default volume type of the EBS volume will be GP3* (default) - -When this featuer flag is enabled, the default volume type of the EBS volume will be `EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.140.0 | `false` | `true` | - -**Compatibility with old behavior:** Pass `volumeType: EbsDeviceVolumeType.GENERAL_PURPOSE_SSD` to `Volume` construct to restore the previous behavior. - - -### @aws-cdk/pipelines:reduceAssetRoleTrustScope - -*Remove the root account principal from PipelineAssetsFileRole trust policy* (default) - -When this feature flag is enabled, the root account principal will not be added to the trust policy of asset role. -When this feature flag is disabled, it will keep the root account principal in the trust policy. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.141.0 | `true` | `true` | - -**Compatibility with old behavior:** Disable the feature flag to add the root account principal back - - -### @aws-cdk/aws-ecs:removeDefaultDeploymentAlarm - -*When enabled, remove default deployment alarm settings* (default) - -When this featuer flag is enabled, remove the default deployment alarm settings when creating a AWS ECS service. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.143.0 | `false` | `true` | - -**Compatibility with old behavior:** Set AWS::ECS::Service 'DeploymentAlarms' manually to restore the previous behavior. - - -### @aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault - -*When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default* (fix) - -This results in 'logApiResponseData' being passed as true to the custom resource provider. This will cause the custom resource handler to receive an 'Update' event. If you don't -have an SDK call configured for the 'Update' event and you're dependent on specific SDK call response data, you will see this error from CFN: - -CustomResource attribute error: Vendor response doesn't contain attribute in object. See https://github.com/aws/aws-cdk/issues/29949) for more details. - -Unlike most feature flags, we don't recommend setting this feature flag to true. However, if you're using the 'AwsCustomResource' construct with 'logApiResponseData' as true in -the event object, then setting this feature flag will keep this behavior. Otherwise, setting this feature flag to false will trigger an 'Update' event by removing the 'logApiResponseData' -property from the event object. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.145.0 | `false` | `false` | - - -### @aws-cdk/aws-s3:keepNotificationInImportedBucket - -*When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack.* (fix) - -Currently, adding notifications to a bucket where it was created by ourselves will override notification added where it is imported. - -When this feature flag is enabled, adding notifications to a bucket in the current stack will only update notification defined in this stack. -Other notifications that are not managed by this stack will be kept. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.155.0 | `false` | `false` | - - -### @aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask - -*When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.* (fix) - -Currently, 'inputPath' and 'outputPath' from the TaskStateBase Props is being used under BedrockInvokeModelProps to define S3URI under 'input' and 'output' fields -of State Machine Task definition. - -When this feature flag is enabled, specify newly introduced props 's3InputUri' and -'s3OutputUri' to populate S3 uri under input and output fields in state machine task definition for Bedrock invoke model. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.156.0 | `true` | `true` | - -**Compatibility with old behavior:** Disable the feature flag to use input and output path fields for s3 URI - - -### @aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions - -*When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration* (fix) - -Currently, we automatically add a number of cloudwatch permissions to the task role when no cloudwatch log group is -specified as logConfiguration and it will grant 'Resources': ['*'] to the task role. - -When this feature flag is enabled, we will only grant the necessary permissions when users specify cloudwatch log group. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.159.0 | `false` | `true` | - -**Compatibility with old behavior:** Disable the feature flag to continue grant permissions to log group when no log group is specified - - - diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index aac7abe87c167..e61ca8b0ce6ab 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -155,6 +155,86 @@ new MyStack(app, 'MyStack', { For more information on bootstrapping accounts and customizing synthesis, see [Bootstrapping in the CDK Developer Guide](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html). +### STS Role Options + +You can configure STS options that instruct the CDK CLI on which configuration should it use when assuming +the various roles that are involved in a deployment operation. + +> See https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles + +These options are available via the `DefaultStackSynthesizer` properties: + +```ts +class MyStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new DefaultStackSynthesizer({ + deployRoleExternalId: '', + deployRoleAdditionalOptions: { + // https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters + }, + fileAssetPublishingExternalId: '', + fileAssetPublishingRoleAdditionalOptions: { + // https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters + }, + imageAssetPublishingExternalId: '', + imageAssetPublishingRoleAdditionalOptions: { + // https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters + }, + lookupRoleExternalId: '', + lookupRoleAdditionalOptions: { + // https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters + }, + }) + }); + } +} +``` + +> Note that the `*additionalOptions` property does not allow passing `ExternalId` or `RoleArn`, as these options +> have dedicated properties that configure them. + +#### Session Tags + +STS session tags are used to implement [Attribute-Based Access Control](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html) (ABAC). + +> See [IAM tutorial: Define permissions to access AWS resources based on tags](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_attribute-based-access-control.html) + +You can pass session tags for each [role created during bootstrap](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles) via the `*additionalOptions` property: + +```ts +class MyStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new DefaultStackSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + }, + }) + }); + } +} +``` + +This will cause the CDK CLI to include session tags when assuming each of these roles during deployment. +Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + +> See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + +- If you are using a custom bootstrap template, make sure the template includes these permissions. +- If you are using the default bootstrap template from a CDK version lower than XXXX, you will need to rebootstrap your enviroment (once). + ## Nested Stacks [Nested stacks](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html) are stacks created as part of other stacks. You create a nested stack within another stack by using the `NestedStack` construct. diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 3cd3924872995..fc743591cfb9c 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts @@ -44,6 +44,7 @@ export class AssetManifestBuilder { region: resolvedOr(stack.region, undefined), assumeRoleArn: target.role?.assumeRoleArn, assumeRoleExternalId: target.role?.assumeRoleExternalId, + assumeRoleAdditionalOptions: target.role?.assumeRoleAdditionalOptions, }); } @@ -82,6 +83,7 @@ export class AssetManifestBuilder { region: resolvedOr(stack.region, undefined), assumeRoleArn: target.role?.assumeRoleArn, assumeRoleExternalId: target.role?.assumeRoleExternalId, + assumeRoleAdditionalOptions: target.role?.assumeRoleAdditionalOptions, }); } @@ -231,6 +233,19 @@ export interface RoleOptions { * @default - No external ID */ readonly assumeRoleExternalId?: string; + + /** + * Additional options to pass to STS when assuming the role for cloudformation deployments. + * + * - `RoleArn` should not be used. Use the dedicated `assumeRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `assumeRoleExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly assumeRoleAdditionalOptions?: { [key: string]: any }; + } function validateFileAssetSource(asset: FileAssetSource) { diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts index 4430c9bdd2090..bdbbdc21bfa47 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/default-synthesizer.ts @@ -23,6 +23,14 @@ const MIN_BOOTSTRAP_STACK_VERSION = 6; */ const MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 8; +/** + * Session tags require `sts:TagSession` permissions on roles during bootstrap. + * This is the first version of the bootstrap templates that adds these permissions. + * + * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + */ +const MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION = 22; + /** * Configuration properties for DefaultStackSynthesizer */ @@ -211,6 +219,54 @@ export interface DefaultStackSynthesizerProps { * @default DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER */ readonly bootstrapStackVersionSsmParameter?: string; + + /** + * Additional options to pass to STS when assuming the deploy role. + * + * - `RoleArn` should not be used. Use the dedicated `deployRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `deployRoleExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly deployRoleAdditionalOptions?: { [key: string]: any }; + + /** + * Additional options to pass to STS when assuming the lookup role. + * + * - `RoleArn` should not be used. Use the dedicated `lookupRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `lookupRoleExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly lookupRoleAdditionalOptions?: { [key: string]: any }; + + /** + * Additional options to pass to STS when assuming the file asset publishing. + * + * - `RoleArn` should not be used. Use the dedicated `fileAssetPublishingRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `fileAssetPublishingExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly fileAssetPublishingRoleAdditionalOptions?: { [key: string]: any }; + + /** + * Additional options to pass to STS when assuming the image asset publishing. + * + * - `RoleArn` should not be used. Use the dedicated `imageAssetPublishingRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `imageAssetPublishingExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly imageAssetPublishingRoleAdditionalOptions?: { [key: string]: any }; } /** @@ -382,6 +438,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.fileAssetPublishingRoleArn ? { assumeRoleArn: this.fileAssetPublishingRoleArn, assumeRoleExternalId: this.props.fileAssetPublishingExternalId, + assumeRoleAdditionalOptions: this.props.fileAssetPublishingRoleAdditionalOptions, } : undefined, }); return this.cloudFormationLocationFromFileAsset(location); @@ -396,6 +453,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.imageAssetPublishingRoleArn ? { assumeRoleArn: this.imageAssetPublishingRoleArn, assumeRoleExternalId: this.props.imageAssetPublishingExternalId, + assumeRoleAdditionalOptions: this.props.imageAssetPublishingRoleAdditionalOptions, } : undefined, }); return this.cloudFormationLocationFromDockerImageAsset(location); @@ -405,7 +463,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab * Synthesize the stack template to the given session, passing the configured lookup role ARN */ protected synthesizeStackTemplate(stack: Stack, session: ISynthesisSession) { - stack._synthesizeTemplate(session, this.lookupRoleArn); + stack._synthesizeTemplate(session, this.lookupRoleArn, this.props.lookupRoleExternalId, this.props.lookupRoleAdditionalOptions); } /** @@ -429,34 +487,58 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab // If it's done AFTER _synthesizeTemplate(), then the template won't contain the // right constructs. if (this.props.generateBootstrapVersionRule ?? true) { - this.addBootstrapVersionRule(MIN_BOOTSTRAP_STACK_VERSION, this.bootstrapStackVersionSsmParameter!); + this.addBootstrapVersionRule(this._requiredBootstrapVersionForDeployment, this.bootstrapStackVersionSsmParameter!); } - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn, + this.props.lookupRoleExternalId, this.props.lookupRoleAdditionalOptions); const templateAsset = this.addFileAsset(templateAssetSource); const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + requiresBootstrapStackVersion: this._requiredBoostrapVersionForAssets, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, }); this.emitArtifact(session, { assumeRoleExternalId: this.props.deployRoleExternalId, assumeRoleArn: this._deployRoleArn, + assumeRoleAdditionalOptions: this.props.deployRoleAdditionalOptions, cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn, stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + requiresBootstrapStackVersion: this._requiredBootstrapVersionForDeployment, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, additionalDependencies: [assetManifestId], lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { arn: this.lookupRoleArn, assumeRoleExternalId: this.props.lookupRoleExternalId, - requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, + assumeRoleAdditionalOptions: this.props.lookupRoleAdditionalOptions, + requiresBootstrapStackVersion: this._requiredBootstrapVersionForLookup, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, } : undefined, }); } + private get _requiredBoostrapVersionForAssets() { + if (this.props.fileAssetPublishingRoleAdditionalOptions?.Tags || this.props.imageAssetPublishingRoleAdditionalOptions?.Tags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; + } + + private get _requiredBootstrapVersionForDeployment() { + if (this.props.deployRoleAdditionalOptions?.Tags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; + } + + private get _requiredBootstrapVersionForLookup() { + if (this.props.lookupRoleAdditionalOptions?.Tags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION; + } + /** * Returns the ARN of the deploy Role. */ diff --git a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts index a261c877c5008..4d4d01d0af039 100644 --- a/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts +++ b/packages/aws-cdk-lib/core/lib/stack-synthesizers/stack-synthesizer.ts @@ -107,8 +107,11 @@ export abstract class StackSynthesizer implements IStackSynthesizer { * the credentials will be the same identity that is doing the `UpdateStack` * call, which may not have the right permissions to write to S3. */ - protected synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string): FileAssetSource { - this.boundStack._synthesizeTemplate(session, lookupRoleArn); + protected synthesizeTemplate(session: ISynthesisSession, + lookupRoleArn?: string, + lookupRoleExternalId?: string, + lookupRoleAdditionalOptions?: { [key: string]: any }): FileAssetSource { + this.boundStack._synthesizeTemplate(session, lookupRoleArn, lookupRoleExternalId, lookupRoleAdditionalOptions); return stackTemplateFileAsset(this.boundStack, session); } @@ -240,6 +243,18 @@ export interface SynthesizeStackArtifactOptions { */ readonly assumeRoleExternalId?: string; + /** + * Additional options to pass to STS when assuming the role for cloudformation deployments. + * + * - `RoleArn` should not be used. Use the dedicated `assumeRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `assumeRoleExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly assumeRoleAdditionalOptions?: { [key: string]: any }; + /** * The role that is passed to CloudFormation to execute the change set * diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index a12ca414491fc..a31610fdfa00c 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -1083,7 +1083,10 @@ export class Stack extends Construct implements ITaggable { * Synthesizes the cloudformation template into a cloud assembly. * @internal */ - public _synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string): void { + public _synthesizeTemplate(session: ISynthesisSession, + lookupRoleArn?: string, + lookupRoleExternalId?: string, + lookupRoleAdditionalOptions?: { [key: string]: any }): void { // In principle, stack synthesis is delegated to the // StackSynthesis object. // @@ -1126,11 +1129,17 @@ export class Stack extends Construct implements ITaggable { fs.writeFileSync(outPath, templateData); for (const ctx of this._missingContext) { - if (lookupRoleArn != null) { - builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn } }); - } else { - builder.addMissing(ctx); - } + + // 'account' and 'region' are added to the schema at tree instantiation time. + // these options however are only known at synthesis, so are added here. + // see https://github.com/aws/aws-cdk/blob/v2.158.0/packages/aws-cdk-lib/core/lib/context-provider.ts#L71 + const queryLookupOptions: Omit = { + lookupRoleArn, + lookupRoleExternalId, + assumeRoleAdditionalOptions: lookupRoleAdditionalOptions, + }; + + builder.addMissing({ ...ctx, props: { ...ctx.props, ...queryLookupOptions } }); } } diff --git a/packages/aws-cdk-lib/core/test/stack-synthesis/new-style-synthesis.test.ts b/packages/aws-cdk-lib/core/test/stack-synthesis/new-style-synthesis.test.ts index f39c577d82be9..9b6d7542a3561 100644 --- a/packages/aws-cdk-lib/core/test/stack-synthesis/new-style-synthesis.test.ts +++ b/packages/aws-cdk-lib/core/test/stack-synthesis/new-style-synthesis.test.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as cxschema from '../../../cloud-assembly-schema'; import { ArtifactType } from '../../../cloud-assembly-schema'; import * as cxapi from '../../../cx-api'; -import { App, Aws, CfnResource, ContextProvider, DefaultStackSynthesizer, FileAssetPackaging, Stack, NestedStack } from '../../lib'; +import { App, Aws, CfnResource, ContextProvider, DefaultStackSynthesizer, FileAssetPackaging, Stack, NestedStack, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, SynthesizeStackArtifactOptions } from '../../lib'; import { ISynthesisSession } from '../../lib/stack-synthesizers/types'; import { evaluateCFN } from '../evaluate-cfn'; @@ -105,6 +105,66 @@ describe('new style synthesis', () => { }); + test('can set role additional options tags on default stack synthesizer', () => { + // GIVEN + stack = new Stack(app, 'SessionTagsStack', { + synthesizer: new DefaultStackSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-DeployRoleTag' }], + }, + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-FileAssetTag' }], + }, + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }], + }, + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }], + }, + }), + env: { + account: '111111111111', region: 'us-east-1', + }, + }); + + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'fileHash', + }); + + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'dockerHash', + }); + + ContextProvider.getValue(stack, { + provider: cxschema.ContextProvider.VPC_PROVIDER, + props: {}, + dummyValue: undefined, + }).value; + + // THEN + const asm = app.synth(); + const manifest = asm.getStackByName('SessionTagsStack').manifest; + // Validates that the deploy and lookup role session tags were set in the Manifest: + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-DeployRoleTag' }]); + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).lookupRole?.assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }]); + + const assetManifest = getAssetManifest(asm); + const assetManifestJSON = readAssetManifest(assetManifest); + + // Validates that the image and file asset session tags were set in the asset manifest: + expect(assetManifestJSON.dockerImages?.dockerHash.destinations['111111111111-us-east-1'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }]); + expect(assetManifestJSON.files?.fileHash.destinations['111111111111-us-east-1'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-FileAssetTag' }]); + + // assert that lookup role options are added to the missing lookup context + expect(asm.manifest.missing![0].props.assumeRoleAdditionalOptions).toEqual({ + Tags: [{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }], + }); + + }); + test('customize version parameter', () => { // GIVEN const myapp = new App(); @@ -188,6 +248,29 @@ describe('new style synthesis', () => { }); + test('generates missing context with the lookup role external id as one of the missing context properties', () => { + // GIVEN + stack = new Stack(app, 'Stack2', { + synthesizer: new DefaultStackSynthesizer({ + generateBootstrapVersionRule: false, + lookupRoleExternalId: 'External', + }), + env: { + account: '111111111111', region: 'us-east-1', + }, + }); + ContextProvider.getValue(stack, { + provider: cxschema.ContextProvider.VPC_PROVIDER, + props: {}, + dummyValue: undefined, + }).value; + + // THEN + const assembly = app.synth(); + expect(assembly.manifest.missing![0].props.lookupRoleExternalId).toEqual('External'); + + }); + test('nested Stack uses the lookup role ARN of the parent stack', () => { // GIVEN const myapp = new App(); diff --git a/packages/aws-cdk-lib/core/test/synthesis.test.ts b/packages/aws-cdk-lib/core/test/synthesis.test.ts index 4e61b05f0a9cc..8b67b371e0be8 100644 --- a/packages/aws-cdk-lib/core/test/synthesis.test.ts +++ b/packages/aws-cdk-lib/core/test/synthesis.test.ts @@ -283,6 +283,85 @@ describe('synthesis', () => { jest.restoreAllMocks(); }); + + test('when deploy role session tags are configured, required stack bootstrap version is 22', () => { + + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], + }, + }), + }); + + const assembly = app.synth(); + const stackArtifact = assembly.getStackByName('stack'); + + expect(stackArtifact.requiresBootstrapStackVersion).toEqual(22); + + const versionRule = stack.node.findChild('CheckBootstrapVersion') as cdk.CfnRule; + + // ugly - but no more than using the snapshot of the resource... + const bootstrapVersions = (versionRule._toCloudFormation() as any).Rules[versionRule.logicalId].Assertions[0].Assert['Fn::Not'][0]['Fn::Contains'][0]; + expect(bootstrapVersions).toContain('21'); + + }); + + test('when lookup role session tags are configured, required lookup bootstrap version is 22', () => { + + const app = new cdk.App(); + new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], + }, + }), + }); + + const assembly = app.synth(); + const stackArtifact = assembly.getStackByName('stack'); + + expect(stackArtifact.lookupRole?.requiresBootstrapStackVersion).toEqual(22); + + }); + + test('when file asset role session tags are configured, required assets bootstrap version is 22', () => { + + const app = new cdk.App(); + new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], + }, + }), + }); + + const assembly = app.synth(); + const assetsArtifact = assembly.tryGetArtifact('stack.assets') as cxapi.AssetManifestArtifact; + + expect(assetsArtifact.requiresBootstrapStackVersion).toEqual(22); + + }); + + test('when image asset role session tags are configured, required assets bootstrap version is 22', () => { + + const app = new cdk.App(); + new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], + }, + }), + }); + + const assembly = app.synth(); + const assetsArtifact = assembly.tryGetArtifact('stack.assets') as cxapi.AssetManifestArtifact; + + expect(assetsArtifact.requiresBootstrapStackVersion).toEqual(22); + + }); + }); function list(outdir: string) { diff --git a/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts b/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts index d73e2a5b33dd7..8fcea525b4907 100644 --- a/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts +++ b/packages/aws-cdk-lib/cx-api/lib/artifacts/cloudformation-artifact.ts @@ -4,7 +4,6 @@ import * as cxschema from '../../../cloud-assembly-schema'; import { CloudArtifact } from '../cloud-artifact'; import type { CloudAssembly } from '../cloud-assembly'; import { Environment, EnvironmentUtils } from '../environment'; - const CLOUDFORMATION_STACK_ARTIFACT_SYM = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); export class CloudFormationStackArtifact extends CloudArtifact { @@ -97,6 +96,18 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ readonly assumeRoleExternalId?: string; + /** + * Additional options to pass to STS when assuming the role for cloudformation deployments. + * + * - `RoleArn` should not be used. Use the dedicated `assumeRoleArn` property instead. + * - `ExternalId` should not be used. Use the dedicated `assumeRoleExternalId` instead. + * - `TransitiveTagKeys` defaults to use all keys (if any) specified in `Tags`. E.g, all tags are transitive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. + */ + readonly assumeRoleAdditionalOptions?: { [key: string]: any }; + /** * The role that is passed to CloudFormation to execute the change set * @@ -166,6 +177,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.notificationArns = properties.notificationArns ?? []; this.assumeRoleArn = properties.assumeRoleArn; this.assumeRoleExternalId = properties.assumeRoleExternalId; + this.assumeRoleAdditionalOptions = properties.assumeRoleAdditionalOptions; this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn; this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl; this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; diff --git a/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts b/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts index 813e7aacf9f12..0ad9d60082243 100644 --- a/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts +++ b/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts @@ -61,6 +61,9 @@ export class StageDeployment { const stepFromArtifact = new Map(); for (const artifact of assembly.stacks) { + if (artifact.assumeRoleAdditionalOptions?.Tags && artifact.assumeRoleArn) { + throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with session tags, but assuming roles with session tags is not supported by CodePipeline.`); + } const step = StackDeployment.fromArtifact(artifact); stepFromArtifact.set(artifact, step); } diff --git a/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts b/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts index ed265b78327f0..28821733c27f2 100644 --- a/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts @@ -523,8 +523,30 @@ test('artifactBucket can be overridden', () => { }); }); +test('throws when deploy role session tags are used', () => { + + const synthesizer = new cdk.DefaultStackSynthesizer({ + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Enginerring' }], + }, + }); + + expect(() => { + new ReuseCodePipelineStack(app, 'PipelineStackA', { + env: PIPELINE_ENV, + reuseStageProps: { + reuseStackProps: { + synthesizer, + }, + }, + }); + }).toThrow('Deployment of stack SampleStage-123456789012-us-east-1-SampleStack requires assuming the role arn:${AWS::Partition}:iam::123456789012:role/cdk-hnb659fds-deploy-role-123456789012-us-east-1 with session tags, but assuming roles with session tags is not supported by CodePipeline.'); + +}); + interface ReuseCodePipelineStackProps extends cdk.StackProps { reuseCrossRegionSupportStacks?: boolean; + reuseStageProps?: ReuseStageProps; } class ReuseCodePipelineStack extends cdk.Stack { public constructor(scope: Construct, id: string, props: ReuseCodePipelineStackProps ) { @@ -559,6 +581,7 @@ class ReuseCodePipelineStack extends cdk.Stack { `SampleStage-${testStageEnv.account}-${testStageEnv.region}`, { env: testStageEnv, + ...(props.reuseStageProps ?? {}), }, ); pipeline.addStage(stage); @@ -566,10 +589,14 @@ class ReuseCodePipelineStack extends cdk.Stack { } } +interface ReuseStageProps extends cdk.StageProps { + readonly reuseStackProps?: cdk.StackProps; +} + class ReuseStage extends cdk.Stage { - public constructor(scope: Construct, id: string, props: cdk.StageProps) { + public constructor(scope: Construct, id: string, props: ReuseStageProps) { super(scope, id, props); - new ReuseStack(this, 'SampleStack', {}); + new ReuseStack(this, 'SampleStack', props.reuseStackProps ?? {}); } } diff --git a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts index 3b3b8cb1722b7..6d8d341d7a8c5 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -1,5 +1,6 @@ import * as os from 'os'; import * as path from 'path'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; import type { ConfigurationOptions } from 'aws-sdk/lib/config-base'; @@ -13,6 +14,12 @@ import { ISDK, SDK, isUnrecoverableAwsError } from './sdk'; import { rootDir } from '../../util/directories'; import { traceMethods } from '../../util/tracing'; +// Partial because `RoleSessionName` is required in STS, but we have a default value for it. +export type AssumeRoleAdditionalOptions = Partial< +// cloud-assembly-schema validates that `ExternalId` and `RoleArn` are not configured +Omit +>; + // Some configuration that can only be achieved by setting // environment variables. process.env.AWS_STS_REGIONAL_ENDPOINTS = 'regional'; @@ -195,7 +202,8 @@ export class SdkProvider { } // We will proceed to AssumeRole using whatever we've been given. - const sdk = await this.withAssumedRole(baseCreds, options.assumeRoleArn, options.assumeRoleExternalId, env.region); + const sdk = await this.withAssumedRole(baseCreds, options.assumeRoleArn, + options.assumeRoleExternalId, options.assumeRoleAdditionalOptions, env.region); // Exercise the AssumeRoleCredentialsProvider we've gotten at least once so // we can determine whether the AssumeRole call succeeds or not. @@ -358,16 +366,20 @@ export class SdkProvider { masterCredentials: Exclude, roleArn: string, externalId: string | undefined, + additionalOptions: AssumeRoleAdditionalOptions | undefined, region: string | undefined) { debug(`Assuming role '${roleArn}'.`); region = region ?? this.defaultRegion; - const creds = new AWS.ChainableTemporaryCredentials({ params: { RoleArn: roleArn, - ...externalId ? { ExternalId: externalId } : {}, + ExternalId: externalId, RoleSessionName: `aws-cdk-${safeUsername()}`, + TransitiveTagKeys: additionalOptions?.Tags + ? additionalOptions.Tags.map((t) => t.Key) + : undefined, + ...(additionalOptions ?? {}), }, stsConfig: { region, @@ -513,6 +525,11 @@ export interface CredentialsOptions { * External ID required to assume the given role. */ readonly assumeRoleExternalId?: string; + + /** + * Session tags required to assume the given role. + */ + readonly assumeRoleAdditionalOptions?: AssumeRoleAdditionalOptions; } /** @@ -575,3 +592,21 @@ function fmtObtainedCredentials( return msg.join(''); } } + +/** + * Instantiate an SDK for context providers. This function ensures that all + * lookup assume role options are used when context providers perform lookups. + */ +export async function initContextProviderSdk(aws: SdkProvider, options: cxschema.ContextLookupRoleOptions): Promise { + + const account = options.account; + const region = options.region; + + const creds: CredentialsOptions = { + assumeRoleArn: options.lookupRoleArn, + assumeRoleExternalId: options.lookupRoleExternalId, + assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, + }; + + return (await aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, creds)).sdk; +} \ No newline at end of file diff --git a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml index dace6e7977a4a..8ed4bb8595446 100644 --- a/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml +++ b/packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml @@ -277,6 +277,13 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + # allows this role to be assumed with session tags. + # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -300,6 +307,13 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + # allows this role to be assumed with session tags. + # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -323,6 +337,13 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + # allows this role to be assumed with session tags. + # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -431,6 +452,13 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + # allows this role to be assumed with session tags. + # see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -623,7 +651,7 @@ Resources: Type: String Name: Fn::Sub: '/cdk-bootstrap/${Qualifier}/version' - Value: '21' + Value: '22' Outputs: BucketName: Description: The name of the S3 bucket owned by the CDK toolkit stack diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts index 706e22db595a5..482c9fe20b408 100644 --- a/packages/aws-cdk/lib/api/deployments.ts +++ b/packages/aws-cdk/lib/api/deployments.ts @@ -492,6 +492,7 @@ export class Deployments { const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, mode, { assumeRoleArn: arns.assumeRoleArn, assumeRoleExternalId: stack.assumeRoleExternalId, + assumeRoleAdditionalOptions: stack.assumeRoleAdditionalOptions, }); return { @@ -538,6 +539,7 @@ export class Deployments { const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, Mode.ForReading, { assumeRoleArn: arns.lookupRoleArn, assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId, + assumeRoleAdditionalOptions: stack.lookupRole?.assumeRoleAdditionalOptions, }); const envResources = this.environmentResources.for(resolvedEnvironment, stackSdk.sdk); @@ -671,13 +673,19 @@ export class Deployments { mode: Mode, options?: CredentialsOptions, ) { - const cacheKey = [ + const cacheKeyElements = [ environment.account, environment.region, `${mode}`, options?.assumeRoleArn ?? '', options?.assumeRoleExternalId ?? '', - ].join(':'); + ]; + + if (options?.assumeRoleAdditionalOptions) { + cacheKeyElements.push(JSON.stringify(options.assumeRoleAdditionalOptions)); + } + + const cacheKey = cacheKeyElements.join(':'); const existing = this.sdkCache.get(cacheKey); if (existing) { return existing; diff --git a/packages/aws-cdk/lib/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts index 062cb445e29ac..9c511cb6569a6 100644 --- a/packages/aws-cdk/lib/context-providers/ami.ts +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -1,7 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug, print } from '../logging'; @@ -21,8 +19,7 @@ export class AmiContextProviderPlugin implements ContextProviderPlugin { print(`Searching for AMI in ${account}:${region}`); debug(`AMI search parameters: ${JSON.stringify(args)}`); - const options = { assumeRoleArn: args.lookupRoleArn }; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const response = await ec2.describeImages({ Owners: args.owners, Filters: Object.entries(args.filters).map(([key, values]) => ({ diff --git a/packages/aws-cdk/lib/context-providers/availability-zones.ts b/packages/aws-cdk/lib/context-providers/availability-zones.ts index 9572875052ae5..f1910c37709f2 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -1,7 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -16,8 +14,7 @@ export class AZContextProviderPlugin implements ContextProviderPlugin { const region = args.region; const account = args.account; debug(`Reading AZs for ${account}:${region}`); - const options = { assumeRoleArn: args.lookupRoleArn }; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const response = await ec2.describeAvailabilityZones().promise(); if (!response.AvailabilityZones) { return []; } const azs = response.AvailabilityZones.filter(zone => zone.State === 'available').map(zone => zone.ZoneName); diff --git a/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts b/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts index d8054c4ab9400..79f96d3cb94f4 100644 --- a/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/endpoint-service-availability-zones.ts @@ -1,6 +1,5 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -11,13 +10,12 @@ export class EndpointServiceAZContextProviderPlugin implements ContextProviderPl constructor(private readonly aws: SdkProvider) { } - public async getValue(args: { [key: string]: any }) { + public async getValue(args: cxschema.EndpointServiceAvailabilityZonesContextQuery) { const region = args.region; const account = args.account; const serviceName = args.serviceName; debug(`Reading AZs for ${account}:${region}:${serviceName}`); - const options = { assumeRoleArn: args.lookupRoleArn }; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const response = await ec2.describeVpcEndpointServices({ ServiceNames: [serviceName] }).promise(); // expect a service in the response diff --git a/packages/aws-cdk/lib/context-providers/hosted-zones.ts b/packages/aws-cdk/lib/context-providers/hosted-zones.ts index 34d8c8657623a..fe5a4be52c4a0 100644 --- a/packages/aws-cdk/lib/context-providers/hosted-zones.ts +++ b/packages/aws-cdk/lib/context-providers/hosted-zones.ts @@ -1,7 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -18,8 +16,7 @@ export class HostedZoneContextProviderPlugin implements ContextProviderPlugin { } const domainName = args.domainName; debug(`Reading hosted zone ${account}:${region}:${domainName}`); - const options = { assumeRoleArn: args.lookupRoleArn }; - const r53 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.route53(); + const r53 = (await initContextProviderSdk(this.aws, args)).route53(); const response = await r53.listHostedZonesByName({ DNSName: domainName }).promise(); if (!response.HostedZones) { throw new Error(`Hosted Zone not found in account ${account}, region ${region}: ${domainName}`); diff --git a/packages/aws-cdk/lib/context-providers/keys.ts b/packages/aws-cdk/lib/context-providers/keys.ts index c0ec05a57d665..52c5dd08f140d 100644 --- a/packages/aws-cdk/lib/context-providers/keys.ts +++ b/packages/aws-cdk/lib/context-providers/keys.ts @@ -2,8 +2,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; import { PromiseResult } from 'aws-sdk/lib/request'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -13,11 +12,7 @@ export class KeyContextProviderPlugin implements ContextProviderPlugin { } public async getValue(args: cxschema.KeyContextQuery) { - const account: string = args.account!; - const region: string = args.region!; - - const options = { assumeRoleArn: args.lookupRoleArn }; - const kms = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.kms(); + const kms = (await initContextProviderSdk(this.aws, args)).kms(); const aliasListEntry = await this.findKey(kms, args); diff --git a/packages/aws-cdk/lib/context-providers/load-balancers.ts b/packages/aws-cdk/lib/context-providers/load-balancers.ts index 959755d8e7c8b..0d5095f277e51 100644 --- a/packages/aws-cdk/lib/context-providers/load-balancers.ts +++ b/packages/aws-cdk/lib/context-providers/load-balancers.ts @@ -1,8 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; /** @@ -13,8 +12,7 @@ export class LoadBalancerContextProviderPlugin implements ContextProviderPlugin } async getValue(query: cxschema.LoadBalancerContextQuery): Promise { - const options = { assumeRoleArn: query.lookupRoleArn }; - const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading, options)).sdk.elbv2(); + const elbv2 = (await initContextProviderSdk(this.aws, query)).elbv2(); if (!query.loadBalancerArn && !query.loadBalancerTags) { throw new Error('The load balancer lookup query must specify either `loadBalancerArn` or `loadBalancerTags`'); @@ -59,8 +57,7 @@ export class LoadBalancerListenerContextProviderPlugin implements ContextProvide } async getValue(query: LoadBalancerListenerQuery): Promise { - const options = { assumeRoleArn: query.lookupRoleArn }; - const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), Mode.ForReading, options)).sdk.elbv2(); + const elbv2 = (await initContextProviderSdk(this.aws, query)).elbv2(); if (!query.listenerArn && !query.loadBalancerArn && !query.loadBalancerTags) { throw new Error('The load balancer listener query must specify at least one of: `listenerArn`, `loadBalancerArn` or `loadBalancerTags`'); diff --git a/packages/aws-cdk/lib/context-providers/security-groups.ts b/packages/aws-cdk/lib/context-providers/security-groups.ts index 19372df9af842..a801841ca9ecf 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -1,8 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin { @@ -10,8 +9,6 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin } async getValue(args: cxschema.SecurityGroupContextQuery): Promise { - const account: string = args.account!; - const region: string = args.region!; if (args.securityGroupId && args.securityGroupName) { throw new Error('\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group'); @@ -21,8 +18,7 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin throw new Error('\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group'); } - const options = { assumeRoleArn: args.lookupRoleArn }; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const filters: AWS.EC2.FilterList = []; if (args.vpcId) { diff --git a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts index 1d06448798023..f85fc4c96f117 100644 --- a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts +++ b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts @@ -1,8 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -23,7 +21,7 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin { const parameterName = args.parameterName; debug(`Reading SSM parameter ${account}:${region}:${parameterName}`); - const response = await this.getSsmParameterValue(account, region, parameterName, args.lookupRoleArn); + const response = await this.getSsmParameterValue(args); const parameterNotFound: boolean = !response.Parameter || response.Parameter.Value === undefined; const suppressError = 'ignoreErrorOnMissingContext' in args && args.ignoreErrorOnMissingContext as boolean; if (parameterNotFound && suppressError && 'dummyValue' in args) { @@ -47,12 +45,11 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin { * * @throws Error if a service error (other than ``ParameterNotFound``) occurs. */ - private async getSsmParameterValue(account: string, region: string, parameterName: string, lookupRoleArn?: string) + private async getSsmParameterValue(args: cxschema.SSMParameterContextQuery) : Promise { - const options = { assumeRoleArn: lookupRoleArn }; - const ssm = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ssm(); + const ssm = (await initContextProviderSdk(this.aws, args)).ssm(); try { - return await ssm.getParameter({ Name: parameterName }).promise(); + return await ssm.getParameter({ Name: args.parameterName }).promise(); } catch (e: any) { if (e.code === 'ParameterNotFound') { return {}; diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index 74349e1b25748..d578df659b4ec 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -1,8 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { Mode } from '../api/aws-auth/credentials'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -12,11 +11,7 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { } public async getValue(args: cxschema.VpcContextQuery) { - const account: string = args.account!; - const region: string = args.region!; - - const options = { assumeRoleArn: args.lookupRoleArn }; - const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).sdk.ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const vpcId = await this.findVpc(ec2, args); diff --git a/packages/aws-cdk/lib/util/asset-publishing.ts b/packages/aws-cdk/lib/util/asset-publishing.ts index 47d83eb5c691c..1a7c5a0c9fc78 100644 --- a/packages/aws-cdk/lib/util/asset-publishing.ts +++ b/packages/aws-cdk/lib/util/asset-publishing.ts @@ -166,12 +166,18 @@ export class PublishingAws implements cdk_assets.IAws { region: options.region ?? this.targetEnv.region, // Default: same region as the stack }; - const cacheKey = JSON.stringify({ + const cacheKeyMap: any = { env, // region, name, account assumeRuleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, quiet: options.quiet, - }); + }; + + if (options.assumeRoleAdditionalOptions) { + cacheKeyMap.assumeRoleAdditionalOptions = options.assumeRoleAdditionalOptions; + } + + const cacheKey = JSON.stringify(cacheKeyMap); const maybeSdk = this.sdkCache.get(cacheKey); if (maybeSdk) { @@ -181,6 +187,7 @@ export class PublishingAws implements cdk_assets.IAws { const sdk = (await this.aws.forEnvironment(env, Mode.ForWriting, { assumeRoleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, + assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, }, options.quiet)).sdk; this.sdkCache.set(cacheKey, sdk); diff --git a/packages/aws-cdk/test/api/fake-sts.ts b/packages/aws-cdk/test/api/fake-sts.ts index 026caecb2810f..26447de20cf02 100644 --- a/packages/aws-cdk/test/api/fake-sts.ts +++ b/packages/aws-cdk/test/api/fake-sts.ts @@ -21,6 +21,8 @@ interface AssumedRole { readonly serialNumber: string; readonly tokenCode: string; readonly roleSessionName: string; + readonly tags?: AWS.STS.Tag[]; + readonly transitiveTagKeys?: string[]; } /** @@ -158,6 +160,28 @@ export class FakeSts { }; } + /** + * Maps have a funky encoding to them when sent to STS. + * + * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + */ + private decodeMapFromRequestBody(parameter: string, body: Record): AWS.STS.Tag[] { + return Object.entries(body) + .filter(([key, _]) => key.startsWith(`${parameter}.member.`) && key.endsWith('.Key')) + .map(([key, tagKey]) => ({ Key: tagKey, Value: body[`${parameter}.member.${key.split('.')[2]}.Value`] })); + } + + /** + * Lists have a funky encoding when sent to STS. + * + * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + */ + private decodeListKeysFromRequestBody(parameter: string, body: Record): string[] { + return Object.entries(body) + .filter(([key]) => key.startsWith(`${parameter}.member.`)) + .map(([, value]) => value); + } + private handleAssumeRole(identity: RegisteredIdentity, mockRequest: MockRequest): Record { this.checkForFailure(mockRequest.parsedBody.RoleArn); @@ -166,6 +190,8 @@ export class FakeSts { roleSessionName: mockRequest.parsedBody.RoleSessionName, serialNumber: mockRequest.parsedBody.SerialNumber, tokenCode: mockRequest.parsedBody.TokenCode, + tags: this.decodeMapFromRequestBody('Tags', mockRequest.parsedBody), + transitiveTagKeys: this.decodeListKeysFromRequestBody('TransitiveTagKeys', mockRequest.parsedBody), }); const roleArn = mockRequest.parsedBody.RoleArn; @@ -255,6 +281,7 @@ interface MockRequest { readonly uri: string; readonly headers: Record; readonly parsedBody: Record; + readonly sessionTags?: { [key: string]: string }; } function urldecode(body: string): Record { diff --git a/packages/aws-cdk/test/api/sdk-provider.test.ts b/packages/aws-cdk/test/api/sdk-provider.test.ts index 22bf095de4ec7..05f3cba57b4bf 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -342,6 +342,39 @@ describe('with intercepted network calls', () => { }); }); + test('session tags can be passed when assuming a role', async () => { + // GIVEN + prepareCreds({ + fakeSts, + config: { + default: { aws_access_key_id: 'foo', $account: '11111' }, + }, + }); + + await withMocked(os, 'userInfo', async (userInfo) => { + userInfo.mockReturnValue({ username: 'skål', uid: 1, gid: 1, homedir: '/here', shell: '/bin/sh' }); + + // WHEN + const provider = await providerFromProfile(undefined); + + const sdk = (await provider.forEnvironment(env(uniq('88888')), Mode.ForReading, + { + assumeRoleArn: 'arn:aws:role', + assumeRoleExternalId: 'bruh', + assumeRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }], + }, + })).sdk as SDK; + await sdk.currentAccount(); + + // THEN + expect(fakeSts.assumedRoles[0]).toEqual(expect.objectContaining({ + tags: [{ Key: 'Department', Value: 'Engineering' }], + transitiveTagKeys: ['Department'], + })); + }); + }); + test('assuming a role does not fail when OS username cannot be read', async () => { // GIVEN prepareCreds({