From 4f7a8204aeda40fc2435fa57bfbea01a16fa93e8 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 12 Aug 2024 11:33:09 -0400 Subject: [PATCH 01/78] WIP: add placeholder integ test Signed-off-by: Sumu --- .../cli-integ/lib/with-cdk-app.ts | 8 + .../cli-integ/resources/cdk-apps/app/app.js | 29 + .../custom-bootstrap-session-tags.yaml | 658 ++++++++++++++++++ .../bootstrapping.integtest.ts | 19 + 4 files changed, 714 insertions(+) create mode 100644 packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml 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 16226c4cde259..758c7c5039913 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 @@ -231,6 +231,11 @@ export async function cloneDirectory(source: string, target: string, output?: No } interface CommonCdkBootstrapCommandOptions { + /** + * @default - the default CDK bootstrap template. + */ + readonly bootstrapTemplate?: string; + readonly toolkitStackName: string; /** @@ -420,6 +425,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/cdk-apps/app/app.js b/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js index d094055795e27..964fae4cb62db 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 @@ -422,6 +422,31 @@ class LambdaStack extends cdk.Stack { } } +class SessionTagsLambdaStack extends cdk.Stack { + constructor(parent, id, props) { + // we will update the synthesizer to make the test pass later: + // const synthesizer = parent.node.tryGetContext('legacySynth') === 'true' ? + // new LegacyStackSynthesizer({ + // fileAssetsBucketName: parent.node.tryGetContext('bootstrapBucket'), + // }) + // : new DefaultStackSynthesizer({ + // fileAssetsBucketName: parent.node.tryGetContext('bootstrapBucket'), + // }) + super(parent, id, { + ...props, + // synthesizer: synthesizer, + }); + + const fn = new lambda.Function(this, 'my-function', { + code: lambda.Code.asset(path.join(__dirname, 'lambda')), + runtime: lambda.Runtime.NODEJS_LATEST, + handler: 'index.handler' + }); + + new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); + } +} + class LambdaHotswapStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); @@ -643,6 +668,9 @@ const app = new cdk.App({ }, }); +// const sessionTagApp = new cdk.App({ +// }); + const defaultEnv = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION @@ -672,6 +700,7 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); + new SessionTagsLambdaStack(app, `${stackPrefix}-sesion-tags-lambda`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml new file mode 100644 index 0000000000000..dace6e7977a4a --- /dev/null +++ b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml @@ -0,0 +1,658 @@ +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 + - 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 + - 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 + - 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 + - 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 + - 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: '21' +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/tests/cli-integ-tests/bootstrapping.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts index ab56611bf39e0..c64c6ecdcb67c 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 @@ -82,6 +82,25 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); +// Session Tags Bootstrapping test: +integTest('can and deploy with session tags', withoutBootstrap(async (fixture) => { + const bootstrapStackName = fixture.bootstrapStackName; + + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + bootstrapTemplate: 'bootstrap-templates/custom-bootstrap-template.yaml', + }); + + // Deploy stack that uses file assets + await fixture.cdkDeploy('lambda', { + 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; From d02e7d214f2d615db77a4e45214b8ef200192fe5 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 12 Aug 2024 14:46:51 -0400 Subject: [PATCH 02/78] WIP: CLI bootstrap integ test is passing, even though it should be failing with tags in custom bootstrap Signed-off-by: Sumu --- .../bootstrap-templates/custom-bootstrap-session-tags.yaml | 6 ++++++ .../tests/cli-integ-tests/bootstrapping.integtest.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) rename packages/@aws-cdk-testing/cli-integ/{tests/cli-integ-tests => resources}/bootstrap-templates/custom-bootstrap-session-tags.yaml (99%) diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml similarity index 99% rename from packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml rename to packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml index dace6e7977a4a..06add7abf2a9f 100644 --- a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap-templates/custom-bootstrap-session-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml @@ -436,6 +436,9 @@ Resources: Principal: AWS: Ref: AWS::AccountId + Condition: + StringEquals: + aws:RequestTag/Department: "Engineering" - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -443,6 +446,9 @@ Resources: Principal: AWS: Ref: TrustedAccounts + Condition: + StringEquals: + aws:RequestTag/Department: "Engineering" - Ref: AWS::NoValue Policies: - PolicyDocument: 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 c64c6ecdcb67c..5de6244e08bf2 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 @@ -88,7 +88,7 @@ integTest('can and deploy with session tags', withoutBootstrap(async (fixture) = await fixture.cdkBootstrapModern({ toolkitStackName: bootstrapStackName, - bootstrapTemplate: 'bootstrap-templates/custom-bootstrap-template.yaml', + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-session-tags.yaml'), }); // Deploy stack that uses file assets From e50a57c7b05316684bf58c25ee793ebbef23d120 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 12 Aug 2024 14:50:05 -0400 Subject: [PATCH 03/78] cleanup comments in app.js Signed-off-by: Sumu --- .../cli-integ/resources/cdk-apps/app/app.js | 9 --------- 1 file changed, 9 deletions(-) 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 964fae4cb62db..1dfaf80fb92fd 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,17 +424,8 @@ class LambdaStack extends cdk.Stack { class SessionTagsLambdaStack extends cdk.Stack { constructor(parent, id, props) { - // we will update the synthesizer to make the test pass later: - // const synthesizer = parent.node.tryGetContext('legacySynth') === 'true' ? - // new LegacyStackSynthesizer({ - // fileAssetsBucketName: parent.node.tryGetContext('bootstrapBucket'), - // }) - // : new DefaultStackSynthesizer({ - // fileAssetsBucketName: parent.node.tryGetContext('bootstrapBucket'), - // }) super(parent, id, { ...props, - // synthesizer: synthesizer, }); const fn = new lambda.Function(this, 'my-function', { From 9f2e54b7df69127d45a2fa73980730aeac48c21e Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 12 Aug 2024 15:09:42 -0400 Subject: [PATCH 04/78] Integ test now 'successfully' fails as intended; switched session tags to cfnexecutionrole instead of deployrole Signed-off-by: Sumu --- .../custom-bootstrap-session-tags.yaml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml index 06add7abf2a9f..4b5ae14f4ad5c 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml @@ -436,9 +436,9 @@ Resources: Principal: AWS: Ref: AWS::AccountId - Condition: - StringEquals: - aws:RequestTag/Department: "Engineering" + # Condition: + # StringEquals: + # aws:RequestTag/Department: "Engineering" - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -446,9 +446,9 @@ Resources: Principal: AWS: Ref: TrustedAccounts - Condition: - StringEquals: - aws:RequestTag/Department: "Engineering" + # Condition: + # StringEquals: + # aws:RequestTag/Department: "Engineering" - Ref: AWS::NoValue Policies: - PolicyDocument: @@ -548,6 +548,9 @@ Resources: Effect: Allow Principal: Service: cloudformation.amazonaws.com + Condition: + StringEquals: + aws:RequestTag/Department: "Engineering" Version: '2012-10-17' ManagedPolicyArns: Fn::If: From da3ee2bbcc16d75cc011d174f273fbcd11094831 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 12:26:36 -0400 Subject: [PATCH 05/78] add principaltag to deployrole policy Signed-off-by: Sumu --- .../custom-bootstrap-session-tags.yaml | 15 ++++++--------- .../cli-integ/resources/cdk-apps/app/app.js | 3 +++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml index 4b5ae14f4ad5c..11f3f8e303c2c 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml @@ -436,9 +436,6 @@ Resources: Principal: AWS: Ref: AWS::AccountId - # Condition: - # StringEquals: - # aws:RequestTag/Department: "Engineering" - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -446,9 +443,6 @@ Resources: Principal: AWS: Ref: TrustedAccounts - # Condition: - # StringEquals: - # aws:RequestTag/Department: "Engineering" - Ref: AWS::NoValue Policies: - PolicyDocument: @@ -464,6 +458,9 @@ Resources: - cloudformation:CreateStack - cloudformation:UpdateStack Resource: "*" + 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. @@ -548,9 +545,9 @@ Resources: Effect: Allow Principal: Service: cloudformation.amazonaws.com - Condition: - StringEquals: - aws:RequestTag/Department: "Engineering" + # Condition: + # StringEquals: + # aws:RequestTag/Department: "Engineering" Version: '2012-10-17' ManagedPolicyArns: Fn::If: 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 1dfaf80fb92fd..4eaa68ce33b15 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 @@ -426,6 +426,9 @@ class SessionTagsLambdaStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, { ...props, + // synthesizer: new DefaultStackSynthesizer({ + // session + // }) }); const fn = new lambda.Function(this, 'my-function', { From 2720e81c2e848c33f48d39dccf761331f493f19b Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 14:12:53 -0400 Subject: [PATCH 06/78] WIP: make framework changes Signed-off-by: Sumu --- .../asset-manifest-builder.ts | 9 ++++++ .../stack-synthesizers/default-synthesizer.ts | 32 +++++++++++++++++++ .../stack-synthesizers/stack-synthesizer.ts | 8 +++++ 3 files changed, 49 insertions(+) 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..2dc4678f73d74 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, + assumeRoleSessionTags: target.role?.assumeRoleSessionTags, }); } @@ -82,6 +83,7 @@ export class AssetManifestBuilder { region: resolvedOr(stack.region, undefined), assumeRoleArn: target.role?.assumeRoleArn, assumeRoleExternalId: target.role?.assumeRoleExternalId, + assumeRoleSessionTags: target.role?.assumeRoleSessionTags, }); } @@ -231,6 +233,13 @@ export interface RoleOptions { * @default - No external ID */ readonly assumeRoleExternalId?: string; + + /** + * The session tags to use for the assume role + * + * @default - No Session Tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; } 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..d01893b61359e 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 @@ -211,6 +211,34 @@ export interface DefaultStackSynthesizerProps { * @default DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER */ readonly bootstrapStackVersionSsmParameter?: string; + + /** + * Session tags to be used for the deploy role + * + * @default - No Session Tags + */ + readonly deployRoleSessionTags?: { [key: string]: string }; + + /** + * Session tags to be used for the lookup role + * + * @default - No Session Tags + */ + readonly lookupRoleSessionTags?: { [key: string]: string }; + + /** + * Session tags to be used for the file asset publishing role + * + * @default - No Session Tags + */ + readonly fileAssetPublishingRoleSessionTags?: { [key: string]: string }; + + /** + * Session tags to be used for the image asset publishing role + * + * @default - No Session Tags + */ + readonly imageAssetPublishingRoleSessionTags?: { [key: string]: string }; } /** @@ -382,6 +410,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.fileAssetPublishingRoleArn ? { assumeRoleArn: this.fileAssetPublishingRoleArn, assumeRoleExternalId: this.props.fileAssetPublishingExternalId, + assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, } : undefined, }); return this.cloudFormationLocationFromFileAsset(location); @@ -396,6 +425,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.imageAssetPublishingRoleArn ? { assumeRoleArn: this.imageAssetPublishingRoleArn, assumeRoleExternalId: this.props.imageAssetPublishingExternalId, + assumeRoleSessionTags: this.props.imageAssetPublishingRoleSessionTags, } : undefined, }); return this.cloudFormationLocationFromDockerImageAsset(location); @@ -443,6 +473,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab this.emitArtifact(session, { assumeRoleExternalId: this.props.deployRoleExternalId, assumeRoleArn: this._deployRoleArn, + assumeRoleSessionTags: this.props.deployRoleSessionTags, cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn, stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, @@ -451,6 +482,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { arn: this.lookupRoleArn, assumeRoleExternalId: this.props.lookupRoleExternalId, + assumeRoleSessionTags: this.props.lookupRoleSessionTags, requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, } : undefined, 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..6918853fa7d1f 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 @@ -240,6 +240,14 @@ export interface SynthesizeStackArtifactOptions { */ readonly assumeRoleExternalId?: string; + /** + * Session tags to be used on the assume role + * + * @default - No session tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; + + /** * The role that is passed to CloudFormation to execute the change set * From 059502128863da7c01289886082958d18d7b20ba Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 15:43:35 -0400 Subject: [PATCH 07/78] add session tags to cloud assembly schema, fixing compiler errors Signed-off-by: Sumu --- .../cloud-assembly-schema/lib/assets/aws-destination.ts | 7 +++++++ .../lib/cloud-assembly/artifact-schema.ts | 7 +++++++ .../core/lib/stack-synthesizers/stack-synthesizer.ts | 1 - 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts index f419fde03c56d..ab586b13d9f14 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts +++ b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts @@ -22,4 +22,11 @@ export interface AwsDestination { * @default - No ExternalId will be supplied */ readonly assumeRoleExternalId?: string; + + /** + * The session tags to use for the assume role + * + * @default - No Session Tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; } \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts index 66872401251aa..09ce19d06d8a3 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts +++ b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts @@ -17,6 +17,13 @@ export interface BootstrapRole { */ readonly assumeRoleExternalId?: string; + /** + * The session tags to use for the assume role + * + * @default - No Session Tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; + /** * Version of bootstrap stack required to use this 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 6918853fa7d1f..4130afdedc2ec 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 @@ -247,7 +247,6 @@ export interface SynthesizeStackArtifactOptions { */ readonly assumeRoleSessionTags?: { [key: string]: string}; - /** * The role that is passed to CloudFormation to execute the change set * From 47e947b65a825e5ca0db9218bb850e3f5688e9f9 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 16:39:04 -0400 Subject: [PATCH 08/78] WIP: CLI changes in sdk-provider Signed-off-by: Sumu --- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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..92f950bfba26f 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -195,7 +195,7 @@ 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.assumeRoleSessionTags, env.region); // Exercise the AssumeRoleCredentialsProvider we've gotten at least once so // we can determine whether the AssumeRole call succeeds or not. @@ -358,15 +358,22 @@ export class SdkProvider { masterCredentials: Exclude, roleArn: string, externalId: string | undefined, + sessionTags: { [key: string]: string } | undefined, region: string | undefined) { debug(`Assuming role '${roleArn}'.`); region = region ?? this.defaultRegion; - const creds = new AWS.ChainableTemporaryCredentials({ params: { RoleArn: roleArn, ...externalId ? { ExternalId: externalId } : {}, + ...sessionTags ? { + Tags: Object.entries(sessionTags).map(([key, value]) => ({ + Key: key, + Value: value, + })), + } : {}, + ...sessionTags ? { TransitiveTagKeys: Object.keys(sessionTags) } : {}, RoleSessionName: `aws-cdk-${safeUsername()}`, }, stsConfig: { @@ -513,6 +520,11 @@ export interface CredentialsOptions { * External ID required to assume the given role. */ readonly assumeRoleExternalId?: string; + + /** + * Session tags required to assume the given role. + */ + readonly assumeRoleSessionTags?: { [key: string]: string }; } /** From 35a2c86c243024d7f30faabb5f37042e6eeb6356 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 22:43:44 -0400 Subject: [PATCH 09/78] Integ test successfully failing Signed-off-by: Sumu --- ...-tags.yaml => custom-bootstrap-deploy-role-tags.yaml} | 0 .../tests/cli-integ-tests/bootstrapping.integtest.ts | 9 ++++----- 2 files changed, 4 insertions(+), 5 deletions(-) rename packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/{custom-bootstrap-session-tags.yaml => custom-bootstrap-deploy-role-tags.yaml} (100%) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml similarity index 100% rename from packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-session-tags.yaml rename to packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml 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 5de6244e08bf2..2695cee459a39 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 @@ -82,17 +82,16 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); -// Session Tags Bootstrapping test: -integTest('can and deploy with session tags', withoutBootstrap(async (fixture) => { +// Custom Bootstrap test with Session Tags on the DeployRole +integTest('can deploy with session tags on the deploy role', withoutBootstrap(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; await fixture.cdkBootstrapModern({ toolkitStackName: bootstrapStackName, - bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-session-tags.yaml'), + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-deploy-role-tags.yaml'), }); - // Deploy stack that uses file assets - await fixture.cdkDeploy('lambda', { + await fixture.cdkDeploy('session-tags-lambda', { options: [ '--toolkit-stack-name', bootstrapStackName, '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`, From 53a70ee2f9e6fc8f5da3dfce918269dd3a240828 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 13 Aug 2024 23:23:26 -0400 Subject: [PATCH 10/78] add tags to lambda stack Signed-off-by: Sumu --- .../cli-integ/resources/cdk-apps/app/app.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 4eaa68ce33b15..3486df504191f 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 @@ -426,9 +426,11 @@ class SessionTagsLambdaStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, { ...props, - // synthesizer: new DefaultStackSynthesizer({ - // session - // }) + synthesizer: new DefaultStackSynthesizer({ + deployRoleSessionTags: { + ['Department'] : 'Engineering', + }, + }) }); const fn = new lambda.Function(this, 'my-function', { @@ -694,7 +696,7 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); - new SessionTagsLambdaStack(app, `${stackPrefix}-sesion-tags-lambda`); + new SessionTagsLambdaStack(app, `${stackPrefix}-session-tags-lambda`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); From c6ba8990c8da7d8e585b2de82ec31c5bb40ea1a2 Mon Sep 17 00:00:00 2001 From: Sumu Date: Wed, 14 Aug 2024 10:30:40 -0400 Subject: [PATCH 11/78] fix department session tag in app.js example Signed-off-by: Sumu --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3486df504191f..385be3ea23ab6 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 @@ -428,7 +428,7 @@ class SessionTagsLambdaStack extends cdk.Stack { ...props, synthesizer: new DefaultStackSynthesizer({ deployRoleSessionTags: { - ['Department'] : 'Engineering', + 'Department' : 'Engineering', }, }) }); From 7fd801b40432d19717a2162ae7acc5c46212310f Mon Sep 17 00:00:00 2001 From: Sumu Date: Thu, 15 Aug 2024 16:29:05 -0400 Subject: [PATCH 12/78] WIP: update CFN schema, add session tags prop in different places Signed-off-by: Sumu --- .../lib/cloud-assembly/artifact-schema.ts | 7 +++++++ .../schema/assets.schema.json | 14 ++++++++++++++ .../schema/cloud-assembly.schema.json | 14 ++++++++++++++ .../lib/artifacts/cloudformation-artifact.ts | 8 ++++++++ packages/aws-cdk/lib/api/deployments.ts | 2 ++ packages/aws-cdk/lib/util/asset-publishing.ts | 2 ++ 6 files changed, 47 insertions(+) diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts index 09ce19d06d8a3..535dd839a6170 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts +++ b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts @@ -89,6 +89,13 @@ export interface AwsCloudFormationStackProperties { */ readonly assumeRoleExternalId?: string; + /** + * The session tags to use for the assume role + * + * @default - No Session Tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; + /** * The role that is passed to CloudFormation to execute the change set * diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json index 980fbaf0d86de..d001be08f6cb5 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json +++ b/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json @@ -97,6 +97,13 @@ "assumeRoleExternalId": { "description": "The ExternalId that needs to be supplied while assuming this role (Default - No ExternalId will be supplied)", "type": "string" + }, + "assumeRoleSessionTags": { + "description": "The session tags to use for the assume role (Default - No Session Tags)", + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ @@ -241,6 +248,13 @@ "assumeRoleExternalId": { "description": "The ExternalId that needs to be supplied while assuming this role (Default - No ExternalId will be supplied)", "type": "string" + }, + "assumeRoleSessionTags": { + "description": "The session tags to use for the assume role (Default - No Session Tags)", + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json index 279dfbe369073..60a36e54d682a 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -362,6 +362,13 @@ "description": "External ID to use when assuming role for cloudformation deployments (Default - No external ID)", "type": "string" }, + "assumeRoleSessionTags": { + "description": "The session tags to use for the assume role (Default - No Session Tags)", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "cloudFormationExecutionRoleArn": { "description": "The role that is passed to CloudFormation to execute the change set (Default - No role is passed (currently assumed role/credentials are used))", "type": "string" @@ -403,6 +410,13 @@ "description": "External ID to use when assuming the bootstrap role (Default - No external ID)", "type": "string" }, + "assumeRoleSessionTags": { + "description": "The session tags to use for the assume role (Default - No Session Tags)", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "requiresBootstrapStackVersion": { "description": "Version of bootstrap stack required to use this role (Default - No bootstrap stack required)", "type": "number" 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 7cf279c96d924..14d6a237f95db 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 @@ -92,6 +92,13 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ readonly assumeRoleExternalId?: string; + /** + * The session tags to use for the assume role + * + * @default - No Session Tags + */ + readonly assumeRoleSessionTags?: { [key: string]: string}; + /** * The role that is passed to CloudFormation to execute the change set * @@ -160,6 +167,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.tags = properties.tags ?? this.tagsFromMetadata(); this.assumeRoleArn = properties.assumeRoleArn; this.assumeRoleExternalId = properties.assumeRoleExternalId; + this.assumeRoleSessionTags = properties.assumeRoleSessionTags; this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn; this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl; this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts index ee357e01b525c..985020e26dbb1 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, + assumeRoleSessionTags: stack.assumeRoleSessionTags, }); return { @@ -677,6 +678,7 @@ export class Deployments { `${mode}`, options?.assumeRoleArn ?? '', options?.assumeRoleExternalId ?? '', + options?.assumeRoleSessionTags ?? '', ].join(':'); const existing = this.sdkCache.get(cacheKey); if (existing) { diff --git a/packages/aws-cdk/lib/util/asset-publishing.ts b/packages/aws-cdk/lib/util/asset-publishing.ts index 47d83eb5c691c..3c90973aef49f 100644 --- a/packages/aws-cdk/lib/util/asset-publishing.ts +++ b/packages/aws-cdk/lib/util/asset-publishing.ts @@ -170,6 +170,7 @@ export class PublishingAws implements cdk_assets.IAws { env, // region, name, account assumeRuleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, + assumeRoleSessionTags: options.assumeRoleSessionTags, quiet: options.quiet, }); @@ -181,6 +182,7 @@ export class PublishingAws implements cdk_assets.IAws { const sdk = (await this.aws.forEnvironment(env, Mode.ForWriting, { assumeRoleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, + assumeRoleSessionTags: options.assumeRoleSessionTags, }, options.quiet)).sdk; this.sdkCache.set(cacheKey, sdk); From bc3f498e7ce6bb78a6ae4e26250108f365442560 Mon Sep 17 00:00:00 2001 From: Sumu Date: Thu, 15 Aug 2024 16:29:57 -0400 Subject: [PATCH 13/78] TEMP FIX - to use local source version of aws-cdk-lib in cli integ tests Signed-off-by: Sumu --- packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 758c7c5039913..139e94e6f445b 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 @@ -628,7 +628,7 @@ export async function installNpmPackages(fixture: TestFixture, packages: Record< }, undefined, 2), { encoding: 'utf-8' }); // Now install that `package.json` using NPM7 - await fixture.shell(['node', require.resolve('npm'), 'install']); + await fixture.shell(['node', '/Users/sumughan/Developer/aws-cdk/packages/@aws-cdk-testing/cli-integ/lib/package-sources/repo-tools/npm', 'install']); } const ALREADY_BOOTSTRAPPED_IN_THIS_RUN = new Set(); From b36fb70ad04fc66be54076b25c99d6a787a02012 Mon Sep 17 00:00:00 2001 From: Sumu Date: Fri, 16 Aug 2024 10:43:57 -0400 Subject: [PATCH 14/78] remove commented out code in app.js Signed-off-by: Sumu --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 3 --- 1 file changed, 3 deletions(-) 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 385be3ea23ab6..ca98b620ae786 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 @@ -664,9 +664,6 @@ const app = new cdk.App({ }, }); -// const sessionTagApp = new cdk.App({ -// }); - const defaultEnv = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION From 9cdcd2474b9ff2afbbd0e5972ead2539663837b0 Mon Sep 17 00:00:00 2001 From: Sumu Date: Fri, 16 Aug 2024 16:54:06 -0400 Subject: [PATCH 15/78] remove changes to cx-api/lib/artifacts/cloudformation-artifact.ts Signed-off-by: Sumu --- .../cx-api/lib/artifacts/cloudformation-artifact.ts | 8 -------- 1 file changed, 8 deletions(-) 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 14d6a237f95db..7cf279c96d924 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 @@ -92,13 +92,6 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ readonly assumeRoleExternalId?: string; - /** - * The session tags to use for the assume role - * - * @default - No Session Tags - */ - readonly assumeRoleSessionTags?: { [key: string]: string}; - /** * The role that is passed to CloudFormation to execute the change set * @@ -167,7 +160,6 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.tags = properties.tags ?? this.tagsFromMetadata(); this.assumeRoleArn = properties.assumeRoleArn; this.assumeRoleExternalId = properties.assumeRoleExternalId; - this.assumeRoleSessionTags = properties.assumeRoleSessionTags; this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn; this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl; this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; From 7197399c73055c2219f1197435c49414c30010b8 Mon Sep 17 00:00:00 2001 From: Sumu Date: Fri, 16 Aug 2024 17:40:37 -0400 Subject: [PATCH 16/78] add tags to file asset role in integ test Signed-off-by: Sumu --- .../custom-bootstrap-deploy-role-tags.yaml | 1 + .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml index 11f3f8e303c2c..e91c16fc34ac7 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml @@ -381,6 +381,7 @@ Resources: StringEquals: aws:ResourceAccount: - Fn::Sub: ${AWS::AccountId} + aws:PrincipalTag/Department: "Engineering" Effect: Allow - Action: - kms:Decrypt 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 ca98b620ae786..b387ac6ee5197 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 @@ -430,6 +430,9 @@ class SessionTagsLambdaStack extends cdk.Stack { deployRoleSessionTags: { 'Department' : 'Engineering', }, + fileAssetPublishingRoleSessionTags: { + 'Department' : 'Engineering', + }, }) }); @@ -442,7 +445,6 @@ class SessionTagsLambdaStack extends cdk.Stack { new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); } } - class LambdaHotswapStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, props); From 1d361bfb1bd96bf245f45f82aae7148e5d6bb44e Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:08:02 -0400 Subject: [PATCH 17/78] Update packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js Co-authored-by: Eli Polonsky --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b387ac6ee5197..82fb50764e8ba 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 @@ -422,7 +422,7 @@ class LambdaStack extends cdk.Stack { } } -class SessionTagsLambdaStack extends cdk.Stack { +class SessionTagsStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, { ...props, From c529bd90ddebabaaba66a13b2efd568cbb1afa11 Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:08:08 -0400 Subject: [PATCH 18/78] Update packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js Co-authored-by: Eli Polonsky --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 82fb50764e8ba..00352ab5826fd 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 @@ -695,7 +695,7 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); - new SessionTagsLambdaStack(app, `${stackPrefix}-session-tags-lambda`); + new SessionTagsLambdaStack(app, `${stackPrefix}-session-tags`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); From f195deee5be06d523666e9ec55fb609ad0253eee Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 19 Aug 2024 10:10:58 -0400 Subject: [PATCH 19/78] typo: rename sessiontagslambdastack Signed-off-by: Sumu --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 00352ab5826fd..0b41079e4ffec 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 @@ -695,7 +695,7 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); - new SessionTagsLambdaStack(app, `${stackPrefix}-session-tags`); + new SessionTagsStack(app, `${stackPrefix}-session-tags`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); From a4e3736d50cc7353939997a46aaca0cd4b3af41a Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 19 Aug 2024 10:26:19 -0400 Subject: [PATCH 20/78] Add (and assert on) session tags for the docker image role in Integ test stack Signed-off-by: Sumu --- ...yaml => custom-bootstrap-with-session-tags.yaml} | 3 +++ .../cli-integ/resources/cdk-apps/app/app.js | 13 +++++++++++++ .../cli-integ-tests/bootstrapping.integtest.ts | 6 +++--- 3 files changed, 19 insertions(+), 3 deletions(-) rename packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/{custom-bootstrap-deploy-role-tags.yaml => custom-bootstrap-with-session-tags.yaml} (99%) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml similarity index 99% rename from packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml rename to packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml index e91c16fc34ac7..1b56c9a9b36c0 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-deploy-role-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml @@ -418,6 +418,9 @@ Resources: Resource: Fn::Sub: "${ContainerAssetsRepository.Arn}" Effect: Allow + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" - Action: - ecr:GetAuthorizationToken Resource: "*" 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 0b41079e4ffec..fbebc541e67dc 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 @@ -433,6 +433,9 @@ class SessionTagsStack extends cdk.Stack { fileAssetPublishingRoleSessionTags: { 'Department' : 'Engineering', }, + imageAssetPublishingRoleSessionTags: { + 'Department' : 'Engineering', + }, }) }); @@ -442,6 +445,16 @@ class SessionTagsStack extends cdk.Stack { handler: 'index.handler' }); + new docker.DockerImageAsset(this, 'image', { + directory: path.join(__dirname, 'docker') + }); + + // Add at least a single resource (WaitConditionHandle), otherwise this stack will never + // be deployed (and its assets never built) + new cdk.CfnResource(this, 'Handle', { + type: 'AWS::CloudFormation::WaitConditionHandle' + }); + new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); } } 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 2695cee459a39..1be0553ca30f6 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 @@ -83,15 +83,15 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn })); // Custom Bootstrap test with Session Tags on the DeployRole -integTest('can deploy with session tags on the deploy role', withoutBootstrap(async (fixture) => { +integTest('can deploy with session tags on the deploy, 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', 'custom-bootstrap-deploy-role-tags.yaml'), + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-with-session-tags.yaml'), }); - await fixture.cdkDeploy('session-tags-lambda', { + await fixture.cdkDeploy('session-tags', { options: [ '--toolkit-stack-name', bootstrapStackName, '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`, From 7a3a80ed05bbf96bea73db0a28e80352abc5d78f Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 19 Aug 2024 12:19:58 -0400 Subject: [PATCH 21/78] Add session tags to core README Signed-off-by: Sumu --- packages/aws-cdk-lib/core/README.md | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 140d8920c44de..16f716687b314 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -155,6 +155,54 @@ 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). +## Session Tags + +Stack Synthesizers also support passing Session Tags to the different [IAM roles created by CDK during bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles): + +Here is an example of passing a session tag to the `DeploymentActionRole`: + +```ts +class MyStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new DefaultStackSynthesizer({ + deployRoleSessionTags: { + 'Department' : 'Engineering', + }, + }) + }); + } +} +``` + +The same applies for custom synthesizers. For example, if you have a custom bootstrap stack that does not use the `CloudFormationExecutionRole` but instead want to use the `DeploymentActionRole`, you could define a custom synthesizer like so: + +```ts +class CustomSynthesizer extends DefaultStackSynthesizer { + // This custom synthesizer does not use a CFN Exec Role + // It will use the Deploy Role by default instead + get cloudFormationExecutionRoleArn() { + return undefined + } +} + +class MyStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new CustomSynthesizer({ + deployRoleSessionTags: { + 'Department' : 'Engineering', + }, + }) + }); + } +} +``` + +For more information on Session Tags see [Define permissions based on attributes with ABAC authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html). + ## 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. From de1e557a6695e4339d3ab0d6251249a56e6d7d73 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 19 Aug 2024 23:08:43 -0400 Subject: [PATCH 22/78] WIP: add custom synthesizer integ test which is succeeding when it SHOULDN'T Signed-off-by: Sumu --- ...p-with-deploy-role-labmda-permissions.yaml | 670 ++++++++++++++++++ .../cli-integ/resources/cdk-apps/app/app.js | 105 ++- .../bootstrapping.integtest.ts | 20 +- 3 files changed, 791 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml new file mode 100644 index 0000000000000..d9c9213a3f25c --- /dev/null +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -0,0 +1,670 @@ +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 + - 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 + - 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 + - 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 + - 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 + # - Action: lambda:CreateFunction + # Effect: Allow + # Resource: '*' + # Condition: + # StringEquals: + # aws:PrincipalTag/Department: 'Engineering' + - Effect: Deny + Action: lambda:CreateFunction + Resource: "*" + Condition: + StringNotEquals: + "aws:PrincipalTag/Hello": "true" + - 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: '21' +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 fbebc541e67dc..f4f4ce161b360 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 @@ -17,7 +17,10 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') { var cdk = require('aws-cdk-lib'); var { DefaultStackSynthesizer, + StackSynthesizer, LegacyStackSynthesizer, + AssetManifestBuilder, + assertBound, aws_ec2: ec2, aws_ecs: ecs, aws_sso: sso, @@ -30,6 +33,9 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') { aws_ecr_assets: docker, Stack } = require('aws-cdk-lib'); + var { + StringSpecializer + } = require('aws-cdk-lib/core/lib/helpers-internal'); } const { Annotations } = cdk; @@ -439,21 +445,113 @@ class SessionTagsStack extends cdk.Stack { }) }); + // 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' }); + new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); + // DockerImageAsset to test ImageAssetPublishingRole new docker.DockerImageAsset(this, 'image', { directory: path.join(__dirname, 'docker') }); - - // Add at least a single resource (WaitConditionHandle), otherwise this stack will never - // be deployed (and its assets never built) new cdk.CfnResource(this, 'Handle', { type: 'AWS::CloudFormation::WaitConditionHandle' }); + } +} + +class CustomSynthesizer extends cdk.StackSynthesizer { + assetManifest = new AssetManifestBuilder(); + fileAssetPublishingRoleArn; + bucketName = 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}' + bucketPrefix = ''; + bootstrapStackVersionSsmParameter = '/cdk-bootstrap/hnb659fds/version'; + qualifier = ''; + props; + + DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; + BOOTSTRAP_QUALIFIER_CONTEXT = '@aws-cdk/core:bootstrapQualifier'; + DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}'; + DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}'; + + DEFAULT_QUALIFIER = 'hnb659fds'; + + constructor(props) { + super(); + this.props = props; + } + + bind(stack) { + super.bind(stack); + + const qualifier = stack.node.tryGetContext(this.BOOTSTRAP_QUALIFIER_CONTEXT) ?? this.DEFAULT_QUALIFIER; + this.qualifier = qualifier; + + this.fileAssetPublishingRoleArn = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; + } + + addFileAsset(asset) { + const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { + bucketName: this.bucketName, + bucketPrefix: this.bucketPrefix, + role: this.fileAssetPublishingRoleArn ? { + assumeRoleArn: this.fileAssetPublishingRoleArn, + assumeRoleExternalId: this.props.fileAssetPublishingExternalId, + assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, + } : undefined, + }); + return this.cloudFormationLocationFromFileAsset(location); + } + + synthesize(session) { + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAsset = this.addFileAsset(templateAssetSource); + + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { + requiresBootstrapStackVersion: 6, + bootstrapStackVersionSsmParameter: '/cdk-bootstrap/hnb659fds/version', + }); + + this.emitArtifact(session, { + assumeRoleExternalId: this.props.deployRoleExternalId, + assumeRoleArn: this.deployRoleArn, + assumeRoleSessionTags: this.props.deployRoleSessionTags, + // Pass in undefined for the CFN exec role: + cloudFormationExecutionRoleArn: undefined, + stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + requiresBootstrapStackVersion: 6, + bootstrapStackVersionSsmParameter: '/cdk-bootstrap/hnb659fds/version', + additionalDependencies: [assetManifestId], + lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { + arn: this.lookupRoleArn, + assumeRoleExternalId: this.props.lookupRoleExternalId, + assumeRoleSessionTags: this.props.lookupRoleSessionTags, + requiresBootstrapStackVersion: 6, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + } : undefined, + }); + } +} + +class SessionTagsWithCustomSynthesizerStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new CustomSynthesizer({ + // deployRoleSessionTags: { + // 'Department' : 'Engineering', + // }, + }) + }); + + const fn = new lambda.Function(this, 'my-function', { + code: lambda.Code.asset(path.join(__dirname, 'lambda')), + runtime: lambda.Runtime.NODEJS_LATEST, + handler: 'index.handler' + }); new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); } @@ -709,6 +807,7 @@ switch (stackSet) { new LambdaStack(app, `${stackPrefix}-lambda`); new SessionTagsStack(app, `${stackPrefix}-session-tags`); + new SessionTagsWithCustomSynthesizerStack(app, `${stackPrefix}-session-tags-with-custom-synthesizer`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); 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 1be0553ca30f6..c0000cc54fee4 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 @@ -82,7 +82,7 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); -// Custom Bootstrap test with Session Tags on the DeployRole +// Custom Bootstrap test with Session Tags on the Deploy, FileAssetPublishing and ImageAssetPublishing Roles integTest('can deploy with session tags on the deploy, file asset, and image asset publishing roles', withoutBootstrap(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; @@ -100,6 +100,24 @@ integTest('can deploy with session tags on the deploy, file asset, and image ass }); })); +// Custom Bootstrap test without CloudFormationExecutionRole and Session Tags on the DeployRole +integTest('can deploy with session tags using custom synthesizer', withoutBootstrap(async (fixture) => { + const bootstrapStackName = fixture.bootstrapStackName; + + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-with-session-tags.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; From 1ec5f87fd9a6dea2f27dd91d6e370d168b7feb58 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 19 Aug 2024 23:37:54 -0400 Subject: [PATCH 23/78] WIP Signed-off-by: Sumu --- ...p-with-deploy-role-labmda-permissions.yaml | 86 +++++++++---------- .../bootstrapping.integtest.ts | 2 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml index d9c9213a3f25c..6cacf81af060e 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -11,11 +11,11 @@ Parameters: environment Default: '' Type: CommaDelimitedList - # CloudFormationExecutionPolicies: - # Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation - # deployment role - # 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: '' @@ -71,13 +71,13 @@ Conditions: - Fn::Join: - '' - Ref: TrustedAccountsForLookup - # HasCloudFormationExecutionPolicies: - # Fn::Not: - # - Fn::Equals: - # - '' - # - Fn::Join: - # - '' - # - Ref: CloudFormationExecutionPolicies + HasCloudFormationExecutionPolicies: + Fn::Not: + - Fn::Equals: + - '' + - Fn::Join: + - '' + - Ref: CloudFormationExecutionPolicies HasCustomFileAssetsBucketName: Fn::Not: - Fn::Equals: @@ -491,9 +491,9 @@ Resources: StringEquals: kms:ViaService: Fn::Sub: s3.${AWS::Region}.amazonaws.com - # - Action: iam:PassRole - # Resource: - # Fn::Sub: "${CloudFormationExecutionRole.Arn}" + - Action: iam:PassRole + Resource: + Fn::Sub: "${CloudFormationExecutionRole.Arn}" # Effect: Allow # - Action: lambda:CreateFunction # Effect: Allow @@ -506,7 +506,7 @@ Resources: Resource: "*" Condition: StringNotEquals: - "aws:PrincipalTag/Hello": "true" + aws:PrincipalTag/Department: "Engineering" - Sid: CliPermissions Action: # Permissions needed by the CLI when doing `cdk deploy`. @@ -545,33 +545,33 @@ Resources: 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 + 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 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 c0000cc54fee4..a0772532d1055 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 @@ -106,7 +106,7 @@ integTest('can deploy with session tags using custom synthesizer', withoutBootst await fixture.cdkBootstrapModern({ toolkitStackName: bootstrapStackName, - bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-with-session-tags.yaml'), + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'custom-bootstrap-with-deploy-role-labmda-permissions.yaml'), }); await fixture.cdkDeploy('session-tags-with-custom-synthesizer', { From 251287b41446561926de343ebb4071e91ce1b437 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 10:35:55 -0400 Subject: [PATCH 24/78] add assumeRoleSessionTags to CloudFormationStackArtifact Signed-off-by: Sumu --- .../cx-api/lib/artifacts/cloudformation-artifact.ts | 8 ++++++++ 1 file changed, 8 insertions(+) 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 7cf279c96d924..7acd21e5c3205 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 @@ -92,6 +92,13 @@ export class CloudFormationStackArtifact extends CloudArtifact { */ readonly assumeRoleExternalId?: string; + /** + * Session tags to use when assuming role for cloudformation deployments + * + * @default - No external ID + */ + readonly assumeRoleSessionTags?: { [key: string]: string }; + /** * The role that is passed to CloudFormation to execute the change set * @@ -160,6 +167,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.tags = properties.tags ?? this.tagsFromMetadata(); this.assumeRoleArn = properties.assumeRoleArn; this.assumeRoleExternalId = properties.assumeRoleExternalId; + this.assumeRoleSessionTags = properties.assumeRoleSessionTags; this.cloudFormationExecutionRoleArn = properties.cloudFormationExecutionRoleArn; this.stackTemplateAssetObjectUrl = properties.stackTemplateAssetObjectUrl; this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion; From 0ad122feb1093f263b175da007eb158a0676c8ad Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 10:36:42 -0400 Subject: [PATCH 25/78] add assumeRoleSessionTags to ClientOptions interfact in cdk-assets/ Signed-off-by: Sumu --- packages/cdk-assets/lib/aws.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cdk-assets/lib/aws.ts b/packages/cdk-assets/lib/aws.ts index d78e29f24cc3e..48143f24b0c1f 100644 --- a/packages/cdk-assets/lib/aws.ts +++ b/packages/cdk-assets/lib/aws.ts @@ -18,6 +18,7 @@ export interface ClientOptions { region?: string; assumeRoleArn?: string; assumeRoleExternalId?: string; + assumeRoleSessionTags?: { [key: string]: string }; quiet?: boolean; } From f86fe9100b1cadbbbd7b0619b947be225237a1eb Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 13:48:42 -0400 Subject: [PATCH 26/78] WIP: custom synth integ test now deploys! (without session tags in bootstrap template yet) Signed-off-by: Sumu --- ...p-with-deploy-role-labmda-permissions.yaml | 20 +++--- .../cli-integ/resources/cdk-apps/app/app.js | 65 +++++-------------- 2 files changed, 24 insertions(+), 61 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml index 6cacf81af060e..39563307e71ae 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -494,19 +494,15 @@ Resources: - Action: iam:PassRole Resource: Fn::Sub: "${CloudFormationExecutionRole.Arn}" - # Effect: Allow - # - Action: lambda:CreateFunction - # Effect: Allow - # Resource: '*' - # Condition: - # StringEquals: - # aws:PrincipalTag/Department: 'Engineering' - - Effect: Deny - Action: lambda:CreateFunction + Effect: Allow + - Sid: ExtraIamPermissionsFromCFNRole + Action: iam:* Resource: "*" - Condition: - StringNotEquals: - aws:PrincipalTag/Department: "Engineering" + Effect: Allow + - Sid: LambdaPermissions + Action: lambda:* + Resource: "*" + Effect: Allow - Sid: CliPermissions Action: # Permissions needed by the CLI when doing `cdk deploy`. 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 f4f4ce161b360..89fd1d0d5b90c 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 @@ -463,73 +463,40 @@ class SessionTagsStack extends cdk.Stack { } } -class CustomSynthesizer extends cdk.StackSynthesizer { - assetManifest = new AssetManifestBuilder(); - fileAssetPublishingRoleArn; - bucketName = 'cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}' - bucketPrefix = ''; - bootstrapStackVersionSsmParameter = '/cdk-bootstrap/hnb659fds/version'; - qualifier = ''; - props; - - DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; - BOOTSTRAP_QUALIFIER_CONTEXT = '@aws-cdk/core:bootstrapQualifier'; - DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}'; - DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}'; - - DEFAULT_QUALIFIER = 'hnb659fds'; - - constructor(props) { - super(); - this.props = props; - } - - bind(stack) { - super.bind(stack); - - const qualifier = stack.node.tryGetContext(this.BOOTSTRAP_QUALIFIER_CONTEXT) ?? this.DEFAULT_QUALIFIER; - this.qualifier = qualifier; - - this.fileAssetPublishingRoleArn = this.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN; - } - - addFileAsset(asset) { - const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName: this.bucketName, - bucketPrefix: this.bucketPrefix, - role: this.fileAssetPublishingRoleArn ? { - assumeRoleArn: this.fileAssetPublishingRoleArn, - assumeRoleExternalId: this.props.fileAssetPublishingExternalId, - assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, - } : undefined, - }); - return this.cloudFormationLocationFromFileAsset(location); - } +MIN_BOOTSTRAP_STACK_VERSION = 6; +MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 6; +class CustomSynthesizer extends cdk.DefaultStackSynthesizer { + // This CustomSynthesizer is the same as the Default synthesizer, except that it passes + // undefined for the CloudFormationExecutionRole, since we want it to use the Deploy role instead. synthesize(session) { + if (this.props.generateBootstrapVersionRule ?? true) { + this.addBootstrapVersionRule(MIN_BOOTSTRAP_STACK_VERSION, this.bootstrapStackVersionSsmParameter); + } + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); const templateAsset = this.addFileAsset(templateAssetSource); const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: 6, - bootstrapStackVersionSsmParameter: '/cdk-bootstrap/hnb659fds/version', + requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, }); this.emitArtifact(session, { assumeRoleExternalId: this.props.deployRoleExternalId, - assumeRoleArn: this.deployRoleArn, + assumeRoleArn: this._deployRoleArn, assumeRoleSessionTags: this.props.deployRoleSessionTags, - // Pass in undefined for the CFN exec role: + // Pass in UNDEFINED for the CFN Execution Role Arn: cloudFormationExecutionRoleArn: undefined, stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: 6, - bootstrapStackVersionSsmParameter: '/cdk-bootstrap/hnb659fds/version', + requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, additionalDependencies: [assetManifestId], lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { arn: this.lookupRoleArn, assumeRoleExternalId: this.props.lookupRoleExternalId, assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: 6, + requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, } : undefined, }); From 2365552bc448502847685d377256fc7ba59e9519 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 13:53:38 -0400 Subject: [PATCH 27/78] add Session Tag conditional to Lambda policy statement - integ test now FAILS without tags passed in, as it should Signed-off-by: Sumu --- ...custom-bootstrap-with-deploy-role-labmda-permissions.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml index 39563307e71ae..d33f62807faaa 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -495,7 +495,7 @@ Resources: Resource: Fn::Sub: "${CloudFormationExecutionRole.Arn}" Effect: Allow - - Sid: ExtraIamPermissionsFromCFNRole + - Sid: IamPermissions # Need this since we no don't use the CloudFormationExecutionRole here Action: iam:* Resource: "*" Effect: Allow @@ -503,6 +503,9 @@ Resources: Action: lambda:* Resource: "*" Effect: Allow + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" - Sid: CliPermissions Action: # Permissions needed by the CLI when doing `cdk deploy`. From 05d2b79509213513d59f6e786a2a46a01842afcf Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 17:28:44 -0400 Subject: [PATCH 28/78] add assumeRoleSessionTags inside prepareSdkWithLookupRoleFor function Signed-off-by: Sumu --- packages/aws-cdk/lib/api/deployments.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts index 985020e26dbb1..88ae463acc8b2 100644 --- a/packages/aws-cdk/lib/api/deployments.ts +++ b/packages/aws-cdk/lib/api/deployments.ts @@ -539,6 +539,7 @@ export class Deployments { const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, Mode.ForReading, { assumeRoleArn: arns.lookupRoleArn, assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId, + assumeRoleSessionTags: stack.lookupRole?.assumeRoleSessionTags, }); const envResources = this.environmentResources.for(resolvedEnvironment, stackSdk.sdk); From 53eb52fabd0458b9381ee4ff86aa775b8575cb43 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 18:48:17 -0400 Subject: [PATCH 29/78] add unit test for aws-cdk-lib/core/lib/stack-synthesizers Signed-off-by: Sumu --- .../new-style-synthesis.test.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) 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..b7f670a4fc995 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 @@ -105,6 +105,50 @@ describe('new style synthesis', () => { }); + test('can set session tags on default stack synthesizer', () => { + // GIVEN + stack = new Stack(app, 'SessionTagsStack', { + synthesizer: new DefaultStackSynthesizer({ + // deployRoleExternalId: 'WOWOW0', + deployRoleSessionTags: { + Department: 'Engineering-DeployRoleTag', + }, + // fileAssetPublishingExternalId: 'WOWOW1', + fileAssetPublishingRoleSessionTags: { + Department: 'Engineering-FileAssetTag', + }, + // imageAssetPublishingExternalId: 'WOWOW2', + imageAssetPublishingRoleSessionTags: { + Department: 'Engineering-ImageAssetTag', + }, + }), + }); + + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'fileHash', + }); + + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'dockerHash', + }); + + // THEN + const asm = app.synth(); + const manifest = app.synth().getStackByName('SessionTagsStack').manifest; + // Validates that the deploy role session tags were set in the Manifest: + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleSessionTags).toEqual({ Department: 'Engineering-DeployRoleTag' }); + + 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['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-ImageAssetTag' }); + expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); + }); + test('customize version parameter', () => { // GIVEN const myapp = new App(); From 8927b6af11f4340a44f8a4783866b1b3d19c9997 Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 18:52:43 -0400 Subject: [PATCH 30/78] add lookup role to synth unit test Signed-off-by: Sumu --- .../test/stack-synthesis/new-style-synthesis.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 b7f670a4fc995..2a459b3326ad6 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 @@ -109,18 +109,18 @@ describe('new style synthesis', () => { // GIVEN stack = new Stack(app, 'SessionTagsStack', { synthesizer: new DefaultStackSynthesizer({ - // deployRoleExternalId: 'WOWOW0', deployRoleSessionTags: { Department: 'Engineering-DeployRoleTag', }, - // fileAssetPublishingExternalId: 'WOWOW1', fileAssetPublishingRoleSessionTags: { Department: 'Engineering-FileAssetTag', }, - // imageAssetPublishingExternalId: 'WOWOW2', imageAssetPublishingRoleSessionTags: { Department: 'Engineering-ImageAssetTag', }, + lookupRoleSessionTags: { + Department: 'Engineering-LookupRoleTag', + } }), }); @@ -138,8 +138,9 @@ describe('new style synthesis', () => { // THEN const asm = app.synth(); const manifest = app.synth().getStackByName('SessionTagsStack').manifest; - // Validates that the deploy role session tags were set in the Manifest: + // Validates that the deploy and lookup role session tags were set in the Manifest: expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleSessionTags).toEqual({ Department: 'Engineering-DeployRoleTag' }); + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).lookupRole?.assumeRoleSessionTags).toEqual({ Department: 'Engineering-LookupRoleTag' }); const assetManifest = getAssetManifest(asm); const assetManifestJSON = readAssetManifest(assetManifest); From 555255f5b1411ffafde38c5b030738840ba65e2c Mon Sep 17 00:00:00 2001 From: Sumu Date: Tue, 20 Aug 2024 23:00:11 -0400 Subject: [PATCH 31/78] add Engineering session tag to customsynth integ test stack Signed-off-by: Sumu --- .../cli-integ/resources/cdk-apps/app/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 89fd1d0d5b90c..606af4159f81c 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 @@ -508,9 +508,9 @@ class SessionTagsWithCustomSynthesizerStack extends cdk.Stack { super(parent, id, { ...props, synthesizer: new CustomSynthesizer({ - // deployRoleSessionTags: { - // 'Department' : 'Engineering', - // }, + deployRoleSessionTags: { + 'Department' : 'Engineering', + }, }) }); From 1cca0e2c8640b827812c489cec7d32398efd54f6 Mon Sep 17 00:00:00 2001 From: Sumu Date: Wed, 21 Aug 2024 10:08:16 -0400 Subject: [PATCH 32/78] missing comma Signed-off-by: Sumu --- .../core/test/stack-synthesis/new-style-synthesis.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2a459b3326ad6..68a27dca07d32 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 @@ -120,7 +120,7 @@ describe('new style synthesis', () => { }, lookupRoleSessionTags: { Department: 'Engineering-LookupRoleTag', - } + }, }), }); From fe327c0ce8dcf94c56798f1ddb33b060ccb6d690 Mon Sep 17 00:00:00 2001 From: Sumu Date: Fri, 23 Aug 2024 13:39:30 -0400 Subject: [PATCH 33/78] Add TagSession permission to roles in custom bootstrap stacks to make the integ tests behave as expected; use sqs in customsynth integ test instead of lambda Signed-off-by: Sumu --- ...p-with-deploy-role-labmda-permissions.yaml | 28 +++++++++++++++---- .../custom-bootstrap-with-session-tags.yaml | 20 +++++++++++++ .../cli-integ/resources/cdk-apps/app/app.js | 8 +----- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml index d33f62807faaa..aa1eba01f84da 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -277,6 +277,11 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -300,6 +305,11 @@ Resources: Properties: AssumeRolePolicyDocument: Statement: + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Action: sts:AssumeRole Effect: Allow Principal: @@ -328,6 +338,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccountsForLookup - Action: sts:AssumeRole @@ -436,6 +451,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -495,12 +515,8 @@ Resources: Resource: Fn::Sub: "${CloudFormationExecutionRole.Arn}" Effect: Allow - - Sid: IamPermissions # Need this since we no don't use the CloudFormationExecutionRole here - Action: iam:* - Resource: "*" - Effect: Allow - - Sid: LambdaPermissions - Action: lambda:* + - Sid: SQSPermissions + Action: sqs:* Resource: "*" Effect: Allow Condition: diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml index 1b56c9a9b36c0..0f7e3913e97f6 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml @@ -282,6 +282,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -305,6 +310,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole @@ -328,6 +338,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccountsForLookup - Action: sts:AssumeRole @@ -440,6 +455,11 @@ Resources: Principal: AWS: Ref: AWS::AccountId + - Action: sts:TagSession + Effect: Allow + Principal: + AWS: + Ref: AWS::AccountId - Fn::If: - HasTrustedAccounts - Action: sts:AssumeRole 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 606af4159f81c..1002541314466 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 @@ -514,13 +514,7 @@ class SessionTagsWithCustomSynthesizerStack extends cdk.Stack { }) }); - const fn = new lambda.Function(this, 'my-function', { - code: lambda.Code.asset(path.join(__dirname, 'lambda')), - runtime: lambda.Runtime.NODEJS_LATEST, - handler: 'index.handler' - }); - - new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); + new sqs.Queue(this, 'sessionTagsQueue'); } } class LambdaHotswapStack extends cdk.Stack { From 12e60f693bff787724160d79fcbf2cffc57c3538 Mon Sep 17 00:00:00 2001 From: Sumu Date: Fri, 23 Aug 2024 13:49:15 -0400 Subject: [PATCH 34/78] remove unused imports from app.js Signed-off-by: Sumu --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 3 --- 1 file changed, 3 deletions(-) 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 1002541314466..46455132f2b69 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 @@ -17,10 +17,7 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') { var cdk = require('aws-cdk-lib'); var { DefaultStackSynthesizer, - StackSynthesizer, LegacyStackSynthesizer, - AssetManifestBuilder, - assertBound, aws_ec2: ec2, aws_ecs: ecs, aws_sso: sso, From 6b92bd5bf8879deac93897b7e2f991ec303cc505 Mon Sep 17 00:00:00 2001 From: Sumu Date: Sat, 24 Aug 2024 16:56:59 -0400 Subject: [PATCH 35/78] add custom synthesizer in separate file, also unit test for it Signed-off-by: Sumu --- .../stack-synthesis/custom-synthesizer.ts | 260 ++++++++++++++++++ .../new-style-synthesis.test.ts | 48 +++- 2 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts diff --git a/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts b/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts new file mode 100644 index 0000000000000..cea6ddcb9857b --- /dev/null +++ b/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts @@ -0,0 +1,260 @@ +import { Stack, FileAssetSource, FileAssetLocation, DockerImageAssetSource, DockerImageAssetLocation } from '../../lib'; +import { StringSpecializer } from '../../lib/helpers-internal'; +import { AssetManifestBuilder, DefaultStackSynthesizerProps, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, StackSynthesizer } from '../../lib/stack-synthesizers'; +import { assertBound } from '../../lib/stack-synthesizers/_shared'; + +export const BOOTSTRAP_QUALIFIER_CONTEXT = '@aws-cdk/core:bootstrapQualifier'; + +/** + * The minimum bootstrap stack version required by this app. + */ +const MIN_BOOTSTRAP_STACK_VERSION = 6; + +/** + * The minimum bootstrap stack version required + * to use the lookup role. + */ +const MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 8; + +/** + * Uses conventionally named roles and asset storage locations + * + * This synthesizer: + * + * - Is a Custom Stack Synthesizer with pretty much the same implementation as the + * `DefaultStackSynthesizer` - it also takes in the `DefaultStackSynthesizer` props + * in the constructor, but the only difference is that we pass in undefined for the + * CloudFormationExecutionRoleArn inside emitArtifact() of the synthesize() function; + * the purpose of this to use the DeployRole for CloudFormation Execution instead. + * + * Requires the environment to have been bootstrapped with Bootstrap Stack V2 + * (also known as "modern bootstrap stack"). The synthesizer adds a version + * check to the template, to make sure the bootstrap stack is recent enough + * to support all features expected by this synthesizer. + */ +export class CustomSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { + /** + * Default ARN qualifier + */ + public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; + + /** + * Default CloudFormation role ARN. + */ + public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default deploy role ARN. + */ + public static readonly DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default asset publishing role ARN for file (S3) assets. + */ + public static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default asset publishing role ARN for image (ECR) assets. + */ + public static readonly DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default lookup role ARN for missing values. + */ + public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default image assets repository name + */ + public static readonly DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME = 'cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}'; + + /** + * Default file assets bucket name + */ + public static readonly DEFAULT_FILE_ASSETS_BUCKET_NAME = 'cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}'; + + /** + * Name of the CloudFormation Export with the asset key name + */ + public static readonly DEFAULT_FILE_ASSET_KEY_ARN_EXPORT_NAME = 'CdkBootstrap-${Qualifier}-FileAssetKeyArn'; + + /** + * Default file asset prefix + */ + public static readonly DEFAULT_FILE_ASSET_PREFIX = ''; + /** + * Default Docker asset prefix + */ + public static readonly DEFAULT_DOCKER_ASSET_PREFIX = ''; + + /** + * Default bootstrap stack version SSM parameter. + */ + public static readonly DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER = '/cdk-bootstrap/${Qualifier}/version'; + + private bucketName?: string; + private repositoryName?: string; + private _deployRoleArn?: string; + private _cloudFormationExecutionRoleArn?: string; + private fileAssetPublishingRoleArn?: string; + private imageAssetPublishingRoleArn?: string; + private lookupRoleArn?: string; + private useLookupRoleForStackOperations: boolean; + private qualifier?: string; + private bucketPrefix?: string; + private dockerTagPrefix?: string; + private bootstrapStackVersionSsmParameter?: string; + private assetManifest = new AssetManifestBuilder(); + + constructor(private readonly props: DefaultStackSynthesizerProps = {}) { + super(); + this.useLookupRoleForStackOperations = props.useLookupRoleForStackOperations ?? true; + } + + /** + * Produce a bound Stack Synthesizer for the given stack. + * + * This method may be called more than once on the same object. + */ + public reusableBind(stack: Stack): IBoundStackSynthesizer { + // Create a copy of the current object and bind that + const copy = Object.create(this); + copy.bind(stack); + return copy; + } + + /** + * The qualifier used to bootstrap this stack + */ + public get bootstrapQualifier(): string | undefined { + return this.qualifier; + } + + /** + * The role used to lookup for this stack + */ + public get lookupRole(): string | undefined { + return this.lookupRoleArn; + } + + public bind(stack: Stack): void { + super.bind(stack); + + const qualifier = this.props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? CustomSynthesizer.DEFAULT_QUALIFIER; + this.qualifier = qualifier; + + const spec = new StringSpecializer(stack, qualifier); + + /* eslint-disable max-len */ + this.bucketName = spec.specialize(this.props.fileAssetsBucketName ?? CustomSynthesizer.DEFAULT_FILE_ASSETS_BUCKET_NAME); + this.repositoryName = spec.specialize(this.props.imageAssetsRepositoryName ?? CustomSynthesizer.DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME); + this._deployRoleArn = spec.specialize(this.props.deployRoleArn ?? CustomSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); + this._cloudFormationExecutionRoleArn = spec.specialize(this.props.cloudFormationExecutionRole ?? CustomSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); + this.fileAssetPublishingRoleArn = spec.specialize(this.props.fileAssetPublishingRoleArn ?? CustomSynthesizer.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN); + this.imageAssetPublishingRoleArn = spec.specialize(this.props.imageAssetPublishingRoleArn ?? CustomSynthesizer.DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN); + this.lookupRoleArn = spec.specialize(this.props.lookupRoleArn ?? CustomSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); + this.bucketPrefix = spec.specialize(this.props.bucketPrefix ?? CustomSynthesizer.DEFAULT_FILE_ASSET_PREFIX); + this.dockerTagPrefix = spec.specialize(this.props.dockerTagPrefix ?? CustomSynthesizer.DEFAULT_DOCKER_ASSET_PREFIX); + this.bootstrapStackVersionSsmParameter = spec.qualifierOnly(this.props.bootstrapStackVersionSsmParameter ?? CustomSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER); + /* eslint-enable max-len */ + } + + public addFileAsset(asset: FileAssetSource): FileAssetLocation { + assertBound(this.bucketName); + + const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { + bucketName: this.bucketName, + bucketPrefix: this.bucketPrefix, + role: this.fileAssetPublishingRoleArn ? { + assumeRoleArn: this.fileAssetPublishingRoleArn, + assumeRoleExternalId: this.props.fileAssetPublishingExternalId, + assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, + } : undefined, + }); + return this.cloudFormationLocationFromFileAsset(location); + } + + public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { + assertBound(this.repositoryName); + + const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { + repositoryName: this.repositoryName, + dockerTagPrefix: this.dockerTagPrefix, + role: this.imageAssetPublishingRoleArn ? { + assumeRoleArn: this.imageAssetPublishingRoleArn, + assumeRoleExternalId: this.props.imageAssetPublishingExternalId, + assumeRoleSessionTags: this.props.imageAssetPublishingRoleSessionTags, + } : undefined, + }); + return this.cloudFormationLocationFromDockerImageAsset(location); + } + + /** + * 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); + } + + /** + * Return the currently bound stack + * + * @deprecated Use `boundStack` instead. + */ + protected get stack(): Stack | undefined { + return this.boundStack; + } + + /** + * Synthesize the associated stack to the session + */ + public synthesize(session: ISynthesisSession): void { + assertBound(this.qualifier); + + // Must be done here -- if it's done in bind() (called in the Stack's constructor) + // then it will become impossible to set context after that. + // + // 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!); + } + + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAsset = this.addFileAsset(templateAssetSource); + + const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { + requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + }); + + this.emitArtifact(session, { + assumeRoleExternalId: this.props.deployRoleExternalId, + assumeRoleArn: this._deployRoleArn, + assumeRoleSessionTags: this.props.deployRoleSessionTags, + // Sets CFN Execution Role Arn to undefined: + cloudFormationExecutionRoleArn: undefined, + stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + additionalDependencies: [assetManifestId], + lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { + arn: this.lookupRoleArn, + assumeRoleExternalId: this.props.lookupRoleExternalId, + assumeRoleSessionTags: this.props.lookupRoleSessionTags, + requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, + } : undefined, + }); + } + + /** + * Returns the ARN of the deploy Role. + */ + public get deployRoleArn(): string { + if (!this._deployRoleArn) { + throw new Error('deployRoleArn getter can only be called after the synthesizer has been bound to a Stack'); + } + return this._deployRoleArn; + } +} 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 68a27dca07d32..e17fda04fa402 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 @@ -1,8 +1,9 @@ import * as fs from 'fs'; +import { CustomSynthesizer } from './custom-synthesizer.ts'; 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, DefaultStackSynthesizerProps, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, AssetManifestBuilder, CfnParameter } from '../../lib'; import { ISynthesisSession } from '../../lib/stack-synthesizers/types'; import { evaluateCFN } from '../evaluate-cfn'; @@ -150,6 +151,51 @@ describe('new style synthesis', () => { expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); }); + test('can set session tags on a custom stack synthesizer', () => { + // GIVEN + stack = new Stack(app, 'SessionTagsCustomSynthesizerStack', { + synthesizer: new CustomSynthesizer({ + deployRoleSessionTags: { + Department: 'Engineering-DeployRoleTag', + }, + fileAssetPublishingRoleSessionTags: { + Department: 'Engineering-FileAssetTag', + }, + imageAssetPublishingRoleSessionTags: { + Department: 'Engineering-ImageAssetTag', + }, + lookupRoleSessionTags: { + Department: 'Engineering-LookupRoleTag', + }, + }), + }); + + stack.synthesizer.addFileAsset({ + fileName: __filename, + packaging: FileAssetPackaging.FILE, + sourceHash: 'fileHash', + }); + + stack.synthesizer.addDockerImageAsset({ + directoryName: '.', + sourceHash: 'dockerHash', + }); + + // THEN + const asm = app.synth(); + const manifest = app.synth().getStackByName('SessionTagsCustomSynthesizerStack').manifest; + // Validates that the deploy and lookup role session tags were set in the Manifest: + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleSessionTags).toEqual({ Department: 'Engineering-DeployRoleTag' }); + expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).lookupRole?.assumeRoleSessionTags).toEqual({ Department: '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['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-ImageAssetTag' }); + expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); + }); + test('customize version parameter', () => { // GIVEN const myapp = new App(); From 11195e16dea8585b427e693222564a3cc1576a39 Mon Sep 17 00:00:00 2001 From: Sumu Date: Sat, 24 Aug 2024 17:56:02 -0400 Subject: [PATCH 36/78] update core README Signed-off-by: Sumu --- packages/aws-cdk-lib/core/README.md | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 16f716687b314..323ca7ae1f972 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -176,30 +176,7 @@ class MyStack extends cdk.Stack { } ``` -The same applies for custom synthesizers. For example, if you have a custom bootstrap stack that does not use the `CloudFormationExecutionRole` but instead want to use the `DeploymentActionRole`, you could define a custom synthesizer like so: - -```ts -class CustomSynthesizer extends DefaultStackSynthesizer { - // This custom synthesizer does not use a CFN Exec Role - // It will use the Deploy Role by default instead - get cloudFormationExecutionRoleArn() { - return undefined - } -} - -class MyStack extends cdk.Stack { - constructor(parent, id, props) { - super(parent, id, { - ...props, - synthesizer: new CustomSynthesizer({ - deployRoleSessionTags: { - 'Department' : 'Engineering', - }, - }) - }); - } -} -``` +You can also do the same with a custom stack synthesizer. For more information on Session Tags see [Define permissions based on attributes with ABAC authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html). From ab9b26c11e253d059114845e156a9069ea2d99b2 Mon Sep 17 00:00:00 2001 From: Sumu Date: Sat, 24 Aug 2024 17:58:49 -0400 Subject: [PATCH 37/78] remove hardcoded path from lib/with-cdk-app.ts Signed-off-by: Sumu --- packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 139e94e6f445b..758c7c5039913 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 @@ -628,7 +628,7 @@ export async function installNpmPackages(fixture: TestFixture, packages: Record< }, undefined, 2), { encoding: 'utf-8' }); // Now install that `package.json` using NPM7 - await fixture.shell(['node', '/Users/sumughan/Developer/aws-cdk/packages/@aws-cdk-testing/cli-integ/lib/package-sources/repo-tools/npm', 'install']); + await fixture.shell(['node', require.resolve('npm'), 'install']); } const ALREADY_BOOTSTRAPPED_IN_THIS_RUN = new Set(); From 45a86d7f7f84dca7fc47392468d1558a905341d7 Mon Sep 17 00:00:00 2001 From: Sumu Date: Sat, 24 Aug 2024 18:00:42 -0400 Subject: [PATCH 38/78] remove cdk-assets and cloud-assembly-schema directories Signed-off-by: Sumu --- .../cloud-assembly-schema/.jsiirc.json | 13 - .../cloud-assembly-schema/CONTRIBUTING.md | 67 -- .../cloud-assembly-schema/README.md | 55 - .../cloud-assembly-schema/index.ts | 1 - .../lib/assets/aws-destination.ts | 32 - .../lib/assets/docker-image-asset.ts | 175 ---- .../lib/assets/file-asset.ts | 76 -- .../cloud-assembly-schema/lib/assets/index.ts | 4 - .../lib/assets/schema.ts | 26 - .../lib/cloud-assembly/artifact-schema.ts | 218 ---- .../lib/cloud-assembly/context-queries.ts | 514 ---------- .../lib/cloud-assembly/index.ts | 4 - .../lib/cloud-assembly/metadata-schema.ts | 325 ------ .../lib/cloud-assembly/schema.ts | 140 --- .../cloud-assembly-schema/lib/index.ts | 4 - .../lib/integ-tests/commands/common.ts | 201 ---- .../lib/integ-tests/commands/deploy.ts | 104 -- .../lib/integ-tests/commands/destroy.ts | 20 - .../lib/integ-tests/commands/index.ts | 3 - .../lib/integ-tests/index.ts | 3 - .../lib/integ-tests/schema.ts | 35 - .../lib/integ-tests/test-case.ts | 206 ---- .../cloud-assembly-schema/lib/manifest.ts | 292 ------ .../cloud-assembly-schema/schema/README.md | 5 - .../schema/assets.schema.json | 267 ----- .../schema/cloud-assembly.schema.json | 953 ------------------ .../schema/cloud-assembly.version.json | 1 - .../schema/integ.schema.json | 493 --------- .../scripts/update-schema.sh | 11 - .../scripts/update-schema.ts | 151 --- .../test/__snapshots__/manifest.test.ts.snap | 7 - .../cloud-assembly-schema/test/assets.test.ts | 194 ---- .../test/fixtures/high-version/manifest.json | 3 - .../invalid-artifact-type/manifest.json | 9 - .../invalid-nested-property/manifest.json | 6 - .../fixtures/invalid-version/manifest.json | 3 - .../test/fixtures/only-version/manifest.json | 3 - .../fixtures/random-metadata/manifest.json | 35 - .../fixtures/unknown-property/manifest.json | 4 - .../fixtures/with-stack-tags/manifest.json | 38 - .../test/integ-tests.test.ts | 137 --- .../test/manifest.test.ts | 162 --- .../cloud-assembly-schema/test/schema.test.ts | 47 - packages/cdk-assets/.eslintrc.js | 3 - packages/cdk-assets/.gitignore | 28 - packages/cdk-assets/.npmignore | 30 - packages/cdk-assets/LICENSE | 201 ---- packages/cdk-assets/NOTICE | 2 - packages/cdk-assets/README.md | 190 ---- packages/cdk-assets/bin/cdk-assets | 2 - packages/cdk-assets/bin/cdk-assets.ts | 67 -- .../bin/docker-credential-cdk-assets | 2 - .../bin/docker-credential-cdk-assets.ts | 42 - packages/cdk-assets/bin/list.ts | 9 - packages/cdk-assets/bin/logging.ts | 24 - packages/cdk-assets/bin/publish.ts | 56 - packages/cdk-assets/jest.config.js | 11 - packages/cdk-assets/lib/asset-manifest.ts | 313 ------ packages/cdk-assets/lib/aws.ts | 164 --- packages/cdk-assets/lib/index.ts | 4 - packages/cdk-assets/lib/private/archive.ts | 92 -- .../cdk-assets/lib/private/asset-handler.ts | 38 - .../lib/private/docker-credentials.ts | 93 -- packages/cdk-assets/lib/private/docker.ts | 279 ----- packages/cdk-assets/lib/private/fs-extra.ts | 31 - .../lib/private/handlers/container-images.ts | 238 ----- .../cdk-assets/lib/private/handlers/files.ts | 292 ------ .../cdk-assets/lib/private/handlers/index.ts | 15 - .../cdk-assets/lib/private/placeholders.ts | 34 - packages/cdk-assets/lib/private/shell.ts | 127 --- packages/cdk-assets/lib/private/util.ts | 12 - packages/cdk-assets/lib/progress.ts | 86 -- packages/cdk-assets/lib/publishing.ts | 256 ----- packages/cdk-assets/package.json | 82 -- packages/cdk-assets/test/archive.test.ts | 94 -- .../cdk-assets/test/docker-images.test.ts | 706 ------------- packages/cdk-assets/test/fake-listener.ts | 17 - packages/cdk-assets/test/files.test.ts | 344 ------- packages/cdk-assets/test/manifest.test.ts | 117 --- packages/cdk-assets/test/mock-aws.ts | 74 -- .../cdk-assets/test/mock-child_process.ts | 69 -- packages/cdk-assets/test/placeholders.test.ts | 82 -- .../test/private/docker-credentials.test.ts | 220 ---- .../cdk-assets/test/private/docker.test.ts | 94 -- packages/cdk-assets/test/progress.test.ts | 85 -- .../test/test-archive-follow/data/one.txt | 1 - .../test/test-archive-follow/linked/two.txt | 1 - .../test/test-archive/executable.txt | 0 .../cdk-assets/test/test-archive/file1.txt | 1 - .../cdk-assets/test/test-archive/file2.txt | 2 - .../test/test-archive/subdir/file3.txt | 1 - packages/cdk-assets/test/util.test.ts | 32 - packages/cdk-assets/test/zipping.test.ts | 53 - packages/cdk-assets/tsconfig.json | 28 - 94 files changed, 9891 deletions(-) delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/.jsiirc.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/CONTRIBUTING.md delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/README.md delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/docker-image-asset.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/file-asset.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/common.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/deploy.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/destroy.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/index.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/test-case.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/lib/manifest.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/schema/README.md delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.version.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/schema/integ.schema.json delete mode 100755 packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.sh delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/__snapshots__/manifest.test.ts.snap delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/assets.test.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/high-version/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-artifact-type/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-nested-property/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-version/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/only-version/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/random-metadata/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/unknown-property/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/with-stack-tags/manifest.json delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/integ-tests.test.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/manifest.test.ts delete mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/test/schema.test.ts delete mode 100644 packages/cdk-assets/.eslintrc.js delete mode 100644 packages/cdk-assets/.gitignore delete mode 100644 packages/cdk-assets/.npmignore delete mode 100644 packages/cdk-assets/LICENSE delete mode 100644 packages/cdk-assets/NOTICE delete mode 100644 packages/cdk-assets/README.md delete mode 100755 packages/cdk-assets/bin/cdk-assets delete mode 100644 packages/cdk-assets/bin/cdk-assets.ts delete mode 100755 packages/cdk-assets/bin/docker-credential-cdk-assets delete mode 100644 packages/cdk-assets/bin/docker-credential-cdk-assets.ts delete mode 100644 packages/cdk-assets/bin/list.ts delete mode 100644 packages/cdk-assets/bin/logging.ts delete mode 100644 packages/cdk-assets/bin/publish.ts delete mode 100644 packages/cdk-assets/jest.config.js delete mode 100644 packages/cdk-assets/lib/asset-manifest.ts delete mode 100644 packages/cdk-assets/lib/aws.ts delete mode 100644 packages/cdk-assets/lib/index.ts delete mode 100644 packages/cdk-assets/lib/private/archive.ts delete mode 100644 packages/cdk-assets/lib/private/asset-handler.ts delete mode 100644 packages/cdk-assets/lib/private/docker-credentials.ts delete mode 100644 packages/cdk-assets/lib/private/docker.ts delete mode 100644 packages/cdk-assets/lib/private/fs-extra.ts delete mode 100644 packages/cdk-assets/lib/private/handlers/container-images.ts delete mode 100644 packages/cdk-assets/lib/private/handlers/files.ts delete mode 100644 packages/cdk-assets/lib/private/handlers/index.ts delete mode 100644 packages/cdk-assets/lib/private/placeholders.ts delete mode 100644 packages/cdk-assets/lib/private/shell.ts delete mode 100644 packages/cdk-assets/lib/private/util.ts delete mode 100644 packages/cdk-assets/lib/progress.ts delete mode 100644 packages/cdk-assets/lib/publishing.ts delete mode 100644 packages/cdk-assets/package.json delete mode 100644 packages/cdk-assets/test/archive.test.ts delete mode 100644 packages/cdk-assets/test/docker-images.test.ts delete mode 100644 packages/cdk-assets/test/fake-listener.ts delete mode 100644 packages/cdk-assets/test/files.test.ts delete mode 100644 packages/cdk-assets/test/manifest.test.ts delete mode 100644 packages/cdk-assets/test/mock-aws.ts delete mode 100644 packages/cdk-assets/test/mock-child_process.ts delete mode 100644 packages/cdk-assets/test/placeholders.test.ts delete mode 100644 packages/cdk-assets/test/private/docker-credentials.test.ts delete mode 100644 packages/cdk-assets/test/private/docker.test.ts delete mode 100644 packages/cdk-assets/test/progress.test.ts delete mode 100644 packages/cdk-assets/test/test-archive-follow/data/one.txt delete mode 100644 packages/cdk-assets/test/test-archive-follow/linked/two.txt delete mode 100755 packages/cdk-assets/test/test-archive/executable.txt delete mode 100644 packages/cdk-assets/test/test-archive/file1.txt delete mode 100644 packages/cdk-assets/test/test-archive/file2.txt delete mode 100644 packages/cdk-assets/test/test-archive/subdir/file3.txt delete mode 100644 packages/cdk-assets/test/util.test.ts delete mode 100644 packages/cdk-assets/test/zipping.test.ts delete mode 100644 packages/cdk-assets/tsconfig.json diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/.jsiirc.json b/packages/aws-cdk-lib/cloud-assembly-schema/.jsiirc.json deleted file mode 100644 index 21db8e13672e1..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/.jsiirc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "targets": { - "java": { - "package": "software.amazon.awscdk.cloudassembly.schema" - }, - "dotnet": { - "namespace": "Amazon.CDK.CloudAssembly.Schema" - }, - "python": { - "module": "aws_cdk.cloud_assembly_schema" - } - } -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/CONTRIBUTING.md b/packages/aws-cdk-lib/cloud-assembly-schema/CONTRIBUTING.md deleted file mode 100644 index 0a96c52f7ca1a..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/CONTRIBUTING.md +++ /dev/null @@ -1,67 +0,0 @@ -## Cloud Assembly Schema - -Making changes to this module should only happen when you introduce new cloud assembly capabilities. - -> For example: supporting the `--target` option when building docker containers. - -If you decided these changes are necessary, simply go ahead and make the necessary modifications to -the interfaces that describe the schema. Our tests and validation mechanisms will ensure you make those -changes correctly. - -### Module Structure - -There are two main things to understand about the files in this module: - -- [`lib/manifest.ts`](./lib/manifest.ts) - - This is the typescript code that defines our schema. It is solely comprised of structs (property only interfaces). - It directly maps to the way we want manifest files to be stored on disk. When you want to make changes to the schema, - this is the file you should be editing. - -- [`lib/schema`](./schema/) - - This directory contains the generated json [schema](./schema/cloud-assembly.schema.json) from the aforementioned - typescript code. It also contains a [version](./schema/cloud-assembly.version.json) file that holds the current version - of the schema. These files are **not** intended for manual editing. Keep reading to understand how they change and when. - -### Schema Generation - -The schema can be generated by running `yarn update-schema`. It reads the [`manifest.ts`](./lib/manifest.ts) file and writes -an updated json schema to [`cloud-assembly.schema.json`](./schema/cloud-assembly.schema.json). -In addition, this command also performs a `major` version bump on the [version](./schema/cloud-assembly.version.json) file. - -Note that it is not generated as part of the build, this is to ensure developers will be intentional when making -changes to the schema. If changes to the code are performed, without generating a new schema, the tests will fail: - -```console -$ yarn test -FAIL test/schema.test.js (5.902s) - ✓ manifest save (7ms) - ✕ cloud-assembly.json.schema is correct (5304ms) - ✓ manifest load (4ms) - ✓ manifest load fails for invalid nested property (5ms) - ✓ manifest load fails for invalid artifact type (1ms) - ✓ stack-tags are deserialized properly (1ms) - ✓ can access random metadata (1ms) - - ● cloud-assembly.json.schema is correct - - Whoops, Looks like the schema has changed. Did you forget to run 'yarn update-schema'? -``` - -### Schema Validation - -Being a **stable** `jsii` module, it undergoes strict API compatibility checks with the help -of [`jsii-diff`](https://github.com/aws/jsii/tree/master/packages/jsii-diff). -This means that breaking changes will be rejected. These include: - -- Adding a required property. (same as changing from *optional* to *required*) -- Changing the type of the property. - -In addition, the interfaces defined here are programmatically exposed to users, via the `manifest` -property of the [`CloudAssembly`](../cx-api/lib/cloud-assembly.ts) class. This means that the following are -also considered breaking changes: - -- Changing a property from *required* to *optional*. -- Removing an optional property. -- Removing a required property. diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/README.md b/packages/aws-cdk-lib/cloud-assembly-schema/README.md deleted file mode 100644 index a102522dba4fd..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Cloud Assembly Schema - - -This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. - -## Cloud Assembly - -The *Cloud Assembly* is the output of the synthesis operation. It is produced as part of the -[`cdk synth`](https://github.com/aws/aws-cdk/tree/main/packages/aws-cdk#cdk-synthesize) -command, or the [`app.synth()`](https://github.com/aws/aws-cdk/blob/main/packages/@aws-cdk/core/lib/app.ts#L135) method invocation. - -Its essentially a set of files and directories, one of which is the `manifest.json` file. It defines the set of instructions that are -needed in order to deploy the assembly directory. - -> For example, when `cdk deploy` is executed, the CLI reads this file and performs its instructions: -> -> - Build container images. -> - Upload assets. -> - Deploy CloudFormation templates. - -Therefore, the assembly is how the CDK class library and CDK CLI (or any other consumer) communicate. To ensure compatibility -between the assembly and its consumers, we treat the manifest file as a well defined, versioned schema. - -## Schema - -This module contains the typescript structs that comprise the `manifest.json` file, as well as the -generated [*json-schema*](./schema/cloud-assembly.schema.json). - -## Versioning - -The schema version is specified in the [`cloud-assembly.version.json`](./schema/cloud-assembly.schema.json) file, under the `version` property. -It follows semantic versioning, but with a small twist. - -When we add instructions to the assembly, they are reflected in the manifest file and the *json-schema* accordingly. -Every such instruction, is crucial for ensuring the correct deployment behavior. This means that to properly deploy a cloud assembly, -consumers must be aware of every such instruction modification. - -For this reason, every change to the schema, even though it might not strictly break validation of the *json-schema* format, -is considered `major` version bump. - -## How to consume - -If you'd like to consume the [schema file](./schema/cloud-assembly.schema.json) in order to do validations on `manifest.json` files, -simply download it from this repo and run it against standard *json-schema* validators, such as [jsonschema](https://www.npmjs.com/package/jsonschema). - -Consumers must take into account the `major` version of the schema they are consuming. They should reject cloud assemblies -with a `major` version that is higher than what they expect. While schema validation might pass on such assemblies, the deployment integrity -cannot be guaranteed because some instructions will be ignored. - -> For example, if your consumer was built when the schema version was 2.0.0, you should reject deploying cloud assemblies with a -> manifest version of 3.0.0. - -## Contributing - -See [Contribution Guide](./CONTRIBUTING.md) diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/index.ts deleted file mode 100644 index f41a696fd204d..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts deleted file mode 100644 index ab586b13d9f14..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/aws-destination.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Destination for assets that need to be uploaded to AWS - */ -export interface AwsDestination { - /** - * The region where this asset will need to be published - * - * @default - Current region - */ - readonly region?: string; - - /** - * The role that needs to be assumed while publishing this asset - * - * @default - No role will be assumed - */ - readonly assumeRoleArn?: string; - - /** - * The ExternalId that needs to be supplied while assuming this role - * - * @default - No ExternalId will be supplied - */ - readonly assumeRoleExternalId?: string; - - /** - * The session tags to use for the assume role - * - * @default - No Session Tags - */ - readonly assumeRoleSessionTags?: { [key: string]: string}; -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/docker-image-asset.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/docker-image-asset.ts deleted file mode 100644 index 70c9761f47c70..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/docker-image-asset.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { AwsDestination } from './aws-destination'; - -/** - * A file asset - */ -export interface DockerImageAsset { - /** - * Source description for file assets - */ - readonly source: DockerImageSource; - - /** - * Destinations for this file asset - */ - readonly destinations: { [id: string]: DockerImageDestination }; -} - -/** - * Properties for how to produce a Docker image from a source - */ -export interface DockerImageSource { - /** - * The directory containing the Docker image build instructions. - * - * This path is relative to the asset manifest location. - * - * @default - Exactly one of `directory` and `executable` is required - */ - readonly directory?: string; - - /** - * A command-line executable that returns the name of a local - * Docker image on stdout after being run. - * - * @default - Exactly one of `directory` and `executable` is required - */ - readonly executable?: string[]; - - /** - * The name of the file with build instructions - * - * Only allowed when `directory` is set. - * - * @default "Dockerfile" - */ - readonly dockerFile?: string; - - /** - * Target build stage in a Dockerfile with multiple build stages - * - * Only allowed when `directory` is set. - * - * @default - The last stage in the Dockerfile - */ - readonly dockerBuildTarget?: string; - - /** - * Additional build arguments - * - * Only allowed when `directory` is set. - * - * @default - No additional build arguments - */ - readonly dockerBuildArgs?: { [name: string]: string }; - - /** - * SSH agent socket or keys - * - * Requires building with docker buildkit. - * - * @default - No ssh flag is set - */ - readonly dockerBuildSsh?: string; - - /** - * Additional build secrets - * - * Only allowed when `directory` is set. - * - * @default - No additional build secrets - */ - readonly dockerBuildSecrets?: { [name: string]: string }; - - /** - * Networking mode for the RUN commands during build. _Requires Docker Engine API v1.25+_. - * - * Specify this property to build images on a specific networking mode. - * - * @default - no networking mode specified - */ - readonly networkMode?: string; - - /** - * Platform to build for. _Requires Docker Buildx_. - * - * Specify this property to build images on a specific platform/architecture. - * - * @default - current machine platform - */ - readonly platform?: string; - - /** - * Outputs - * - * @default - no outputs are passed to the build command (default outputs are used) - * @see https://docs.docker.com/engine/reference/commandline/build/#custom-build-outputs - */ - readonly dockerOutputs?: string[]; - - /** - * Cache from options to pass to the `docker build` command. - * - * @default - no cache from options are passed to the build command - * @see https://docs.docker.com/build/cache/backends/ - */ - readonly cacheFrom?: DockerCacheOption[]; - - /** - * Cache to options to pass to the `docker build` command. - * - * @default - no cache to options are passed to the build command - * @see https://docs.docker.com/build/cache/backends/ - */ - readonly cacheTo?: DockerCacheOption; - - /** - * Disable the cache and pass `--no-cache` to the `docker build` command. - * - * @default - cache is used - */ - readonly cacheDisabled?: boolean; -} - -/** - * Where to publish docker images - */ -export interface DockerImageDestination extends AwsDestination { - /** - * Name of the ECR repository to publish to - */ - readonly repositoryName: string; - - /** - * Tag of the image to publish - */ - readonly imageTag: string; -} - -/** - * Options for configuring the Docker cache backend - */ -export interface DockerCacheOption { - /** - * The type of cache to use. - * Refer to https://docs.docker.com/build/cache/backends/ for full list of backends. - * @default - unspecified - * - * @example 'registry' - */ - readonly type: string; - /** - * Any parameters to pass into the docker cache backend configuration. - * Refer to https://docs.docker.com/build/cache/backends/ for cache backend configuration. - * @default {} No options provided - * - * @example - * declare const branch: string; - * - * const params = { - * ref: `12345678.dkr.ecr.us-west-2.amazonaws.com/cache:${branch}`, - * mode: "max", - * }; - */ - readonly params?: { [key: string]: string }; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/file-asset.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/file-asset.ts deleted file mode 100644 index 58c7e0cc93ebc..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/file-asset.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { AwsDestination } from './aws-destination'; - -/** - * A file asset - */ -export interface FileAsset { - /** - * Source description for file assets - */ - readonly source: FileSource; - - /** - * Destinations for this file asset - */ - readonly destinations: { [id: string]: FileDestination }; -} - -/** - * Packaging strategy for file assets - */ -export enum FileAssetPackaging { - /** - * Upload the given path as a file - */ - FILE = 'file', - - /** - * The given path is a directory, zip it and upload - */ - ZIP_DIRECTORY = 'zip', -} - -/** - * Describe the source of a file asset - */ -export interface FileSource { - /** - * External command which will produce the file asset to upload. - * - * @default - Exactly one of `executable` and `path` is required. - */ - readonly executable?: string[]; - - /** - * The filesystem object to upload - * - * This path is relative to the asset manifest location. - * - * @default - Exactly one of `executable` and `path` is required. - */ - readonly path?: string; - - /** - * Packaging method - * - * Only allowed when `path` is specified. - * - * @default FILE - */ - readonly packaging?: FileAssetPackaging; -} - -/** - * Where in S3 a file asset needs to be published - */ -export interface FileDestination extends AwsDestination { - /** - * The name of the bucket - */ - readonly bucketName: string; - - /** - * The destination object key - */ - readonly objectKey: string; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/index.ts deleted file mode 100644 index 49c126e3f2d9b..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './schema'; -export * from './docker-image-asset'; -export * from './file-asset'; -export * from './aws-destination'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/schema.ts deleted file mode 100644 index 7f5b33da127cd..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/assets/schema.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DockerImageAsset } from './docker-image-asset'; -import { FileAsset } from './file-asset'; - -/** - * Definitions for the asset manifest - */ -export interface AssetManifest { - /** - * Version of the manifest - */ - readonly version: string; - - /** - * The file assets in this manifest - * - * @default - No files - */ - readonly files?: { [id: string]: FileAsset }; - - /** - * The Docker image assets in this manifest - * - * @default - No Docker images - */ - readonly dockerImages?: { [id: string]: DockerImageAsset }; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts deleted file mode 100644 index 535dd839a6170..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/artifact-schema.ts +++ /dev/null @@ -1,218 +0,0 @@ - -/** - * Information needed to access an IAM role created - * as part of the bootstrap process - */ -export interface BootstrapRole { - /** - * The ARN of the IAM role created as part of bootrapping - * e.g. lookupRoleArn - */ - readonly arn: string; - - /** - * External ID to use when assuming the bootstrap role - * - * @default - No external ID - */ - readonly assumeRoleExternalId?: string; - - /** - * The session tags to use for the assume role - * - * @default - No Session Tags - */ - readonly assumeRoleSessionTags?: { [key: string]: string}; - - /** - * Version of bootstrap stack required to use this role - * - * @default - No bootstrap stack required - */ - readonly requiresBootstrapStackVersion?: number; - - /** - * Name of SSM parameter with bootstrap stack version - * - * @default - Discover SSM parameter by reading stack - */ - readonly bootstrapStackVersionSsmParameter?: string; -} - -/** - * Artifact properties for CloudFormation stacks. - */ -export interface AwsCloudFormationStackProperties { - /** - * A file relative to the assembly root which contains the CloudFormation template for this stack. - */ - readonly templateFile: string; - - /** - * Values for CloudFormation stack parameters that should be passed when the stack is deployed. - * - * @default - No parameters - */ - readonly parameters?: { [id: string]: string }; - - /** - * Values for CloudFormation stack tags that should be passed when the stack is deployed. - * - * @default - No tags - */ - readonly tags?: { [id: string]: string }; - - /** - * The name to use for the CloudFormation stack. - * @default - name derived from artifact ID - */ - readonly stackName?: string; - - /** - * Whether to enable termination protection for this stack. - * - * @default false - */ - readonly terminationProtection?: boolean; - - /** - * The role that needs to be assumed to deploy the stack - * - * @default - No role is assumed (current credentials are used) - */ - readonly assumeRoleArn?: string; - - /** - * External ID to use when assuming role for cloudformation deployments - * - * @default - No external ID - */ - readonly assumeRoleExternalId?: string; - - /** - * The session tags to use for the assume role - * - * @default - No Session Tags - */ - readonly assumeRoleSessionTags?: { [key: string]: string}; - - /** - * The role that is passed to CloudFormation to execute the change set - * - * @default - No role is passed (currently assumed role/credentials are used) - */ - readonly cloudFormationExecutionRoleArn?: string; - - /** - * The role to use to look up values from the target AWS account - * - * @default - No role is assumed (current credentials are used) - */ - readonly lookupRole?: BootstrapRole; - - /** - * If the stack template has already been included in the asset manifest, its asset URL - * - * @default - Not uploaded yet, upload just before deploying - */ - readonly stackTemplateAssetObjectUrl?: string; - - /** - * Version of bootstrap stack required to deploy this stack - * - * @default - No bootstrap stack required - */ - readonly requiresBootstrapStackVersion?: number; - - /** - * SSM parameter where the bootstrap stack version number can be found - * - * Only used if `requiresBootstrapStackVersion` is set. - * - * - If this value is not set, the bootstrap stack name must be known at - * deployment time so the stack version can be looked up from the stack - * outputs. - * - If this value is set, the bootstrap stack can have any name because - * we won't need to look it up. - * - * @default - Bootstrap stack version number looked up - */ - readonly bootstrapStackVersionSsmParameter?: string; - - /** - * Whether this stack should be validated by the CLI after synthesis - * - * @default - false - */ - readonly validateOnSynth?: boolean; -} - -/** - * Configuration options for the Asset Manifest - */ -export interface AssetManifestOptions { - /** - * Version of bootstrap stack required to deploy this stack - * - * @default - Version 1 (basic modern bootstrap stack) - */ - readonly requiresBootstrapStackVersion?: number; - - /** - * SSM parameter where the bootstrap stack version number can be found - * - * - If this value is not set, the bootstrap stack name must be known at - * deployment time so the stack version can be looked up from the stack - * outputs. - * - If this value is set, the bootstrap stack can have any name because - * we won't need to look it up. - * - * @default - Bootstrap stack version number looked up - */ - readonly bootstrapStackVersionSsmParameter?: string; -} - -/** - * Artifact properties for the Asset Manifest - */ -export interface AssetManifestProperties extends AssetManifestOptions { - /** - * Filename of the asset manifest - */ - readonly file: string; -} - -/** - * Artifact properties for the Construct Tree Artifact - */ -export interface TreeArtifactProperties { - /** - * Filename of the tree artifact - */ - readonly file: string; -} - -/** - * Artifact properties for nested cloud assemblies - */ -export interface NestedCloudAssemblyProperties { - /** - * Relative path to the nested cloud assembly - */ - readonly directoryName: string; - - /** - * Display name for the cloud assembly - * - * @default - The artifact ID - */ - readonly displayName?: string; -} - -/** - * Properties for manifest artifacts - */ -export type ArtifactProperties = AwsCloudFormationStackProperties -| AssetManifestProperties -| TreeArtifactProperties -| NestedCloudAssemblyProperties; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts deleted file mode 100644 index bd35d023dfe69..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ /dev/null @@ -1,514 +0,0 @@ -import { Tag } from './metadata-schema'; - -/** - * Identifier for the context provider - */ -export enum ContextProvider { - /** - * AMI provider - */ - AMI_PROVIDER = 'ami', - - /** - * AZ provider - */ - AVAILABILITY_ZONE_PROVIDER = 'availability-zones', - - /** - * Route53 Hosted Zone provider - */ - HOSTED_ZONE_PROVIDER = 'hosted-zone', - - /** - * SSM Parameter Provider - */ - SSM_PARAMETER_PROVIDER = 'ssm', - - /** - * VPC Provider - */ - VPC_PROVIDER = 'vpc-provider', - - /** - * VPC Endpoint Service AZ Provider - */ - ENDPOINT_SERVICE_AVAILABILITY_ZONE_PROVIDER = 'endpoint-service-availability-zones', - - /** - * Load balancer provider - */ - LOAD_BALANCER_PROVIDER = 'load-balancer', - - /** - * Load balancer listener provider - */ - LOAD_BALANCER_LISTENER_PROVIDER = 'load-balancer-listener', - - /** - * Security group provider - */ - SECURITY_GROUP_PROVIDER = 'security-group', - - /** - * KMS Key Provider - */ - KEY_PROVIDER = 'key-provider', - - /** - * A plugin provider (the actual plugin name will be in the properties) - */ - PLUGIN = 'plugin', -} - -/** - * Query to AMI context provider - */ -export interface AmiContextQuery { - /** - * Account to query - */ - readonly account: string; - - /** - * Region to query - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Owners to DescribeImages call - * - * @default - All owners - */ - readonly owners?: string[]; - - /** - * Filters to DescribeImages call - */ - readonly filters: {[key: string]: string[]}; -} - -/** - * Query to availability zone context provider - */ -export interface AvailabilityZonesContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - -} - -/** - * Query to hosted zone context provider - */ -export interface HostedZoneContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * The domain name e.g. example.com to lookup - */ - readonly domainName: string; - - /** - * True if the zone you want to find is a private hosted zone - * - * @default false - */ - readonly privateZone?: boolean; - - /** - * The VPC ID to that the private zone must be associated with - * - * If you provide VPC ID and privateZone is false, this will return no results - * and raise an error. - * - * @default - Required if privateZone=true - */ - readonly vpcId?: string; -} - -/** - * Query to SSM Parameter Context Provider - */ -export interface SSMParameterContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Parameter name to query - */ - readonly parameterName: string; -} - -/** - * Query input for looking up a VPC - */ -export interface VpcContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Filters to apply to the VPC - * - * Filter parameters are the same as passed to DescribeVpcs. - * - * @see https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeVpcs.html - */ - readonly filter: {[key: string]: string}; - - /** - * Whether to populate the subnetGroups field of the `VpcContextResponse`, - * which contains potentially asymmetric subnet groups. - * - * @default false - */ - readonly returnAsymmetricSubnets?: boolean; - - /** - * Optional tag for subnet group name. - * If not provided, we'll look at the aws-cdk:subnet-name tag. - * If the subnet does not have the specified tag, - * we'll use its type as the name. - * - * @default 'aws-cdk:subnet-name' - */ - readonly subnetGroupNameTag?: string; - - /** - * Whether to populate the `vpnGatewayId` field of the `VpcContextResponse`, - * which contains the VPN Gateway ID, if one exists. You can explicitly - * disable this in order to avoid the lookup if you know the VPC does not have - * a VPN Gatway attached. - * - * @default true - */ - readonly returnVpnGateways?: boolean; -} - -/** - * Query to endpoint service context provider - */ -export interface EndpointServiceAvailabilityZonesContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Query service name - */ - readonly serviceName: string; -} - -/** - * Type of load balancer - */ -export enum LoadBalancerType { - /** - * Network load balancer - */ - NETWORK = 'network', - - /** - * Application load balancer - */ - APPLICATION = 'application', -} - -/** - * Filters for selecting load balancers - */ -export interface LoadBalancerFilter { - /** - * Filter load balancers by their type - */ - readonly loadBalancerType: LoadBalancerType; - - /** - * Find by load balancer's ARN - * @default - does not search by load balancer arn - */ - readonly loadBalancerArn?: string; - - /** - * Match load balancer tags - * @default - does not match load balancers by tags - */ - readonly loadBalancerTags?: Tag[]; -} - -/** - * Query input for looking up a load balancer - */ -export interface LoadBalancerContextQuery extends LoadBalancerFilter { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; -} - -/** - * The protocol for connections from clients to the load balancer - */ -export enum LoadBalancerListenerProtocol { - /** - * HTTP protocol - */ - HTTP = 'HTTP', - - /** - * HTTPS protocol - */ - HTTPS = 'HTTPS', - - /** - * TCP protocol - */ - TCP = 'TCP', - - /** - * TLS protocol - */ - TLS = 'TLS', - - /** - * UDP protocol - * */ - UDP = 'UDP', - - /** - * TCP and UDP protocol - * */ - TCP_UDP = 'TCP_UDP', -} - -/** - * Query input for looking up a load balancer listener - */ -export interface LoadBalancerListenerContextQuery extends LoadBalancerFilter { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Find by listener's arn - * @default - does not find by listener arn - */ - readonly listenerArn?: string; - - /** - * Filter by listener protocol - * @default - does not filter by listener protocol - */ - readonly listenerProtocol?: LoadBalancerListenerProtocol; - - /** - * Filter listeners by listener port - * @default - does not filter by a listener port - */ - readonly listenerPort?: number; -} - -/** - * Query input for looking up a security group - */ -export interface SecurityGroupContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Security group id - * - * @default - None - */ - readonly securityGroupId?: string; - - /** - * Security group name - * - * @default - None - */ - readonly securityGroupName?: string; - - /** - * VPC ID - * - * @default - None - */ - readonly vpcId?: string; -} - -/** - * Query input for looking up a KMS Key - */ -export interface KeyContextQuery { - /** - * Query account - */ - readonly account: string; - - /** - * Query region - */ - readonly region: string; - - /** - * The ARN of the role that should be used to look up the missing values - * - * @default - None - */ - readonly lookupRoleArn?: string; - - /** - * Alias name used to search the Key - */ - readonly aliasName: string; -} - -/** - * Query input for plugins - * - * This alternate branch is necessary because it needs to be able to escape all type checking - * we do on on the cloud assembly -- we cannot know the properties that will be used a priori. - */ -export interface PluginContextQuery { - /** - * The name of the plugin - */ - readonly pluginName: string; - - /** - * Arbitrary other arguments for the plugin. - * - * This index signature is not usable in non-TypeScript/JavaScript languages. - * - * @jsii ignore - */ - [key: string]: any; -} - -export type ContextQueryProperties = AmiContextQuery -| AvailabilityZonesContextQuery -| HostedZoneContextQuery -| SSMParameterContextQuery -| VpcContextQuery -| EndpointServiceAvailabilityZonesContextQuery -| LoadBalancerContextQuery -| LoadBalancerListenerContextQuery -| SecurityGroupContextQuery -| KeyContextQuery -| PluginContextQuery; - diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/index.ts deleted file mode 100644 index 931538d80cf11..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './schema'; -export * from './metadata-schema'; -export * from './artifact-schema'; -export * from './context-queries'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts deleted file mode 100644 index a96df56d49b58..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/metadata-schema.ts +++ /dev/null @@ -1,325 +0,0 @@ -/** - * Common properties for asset metadata. - */ -interface BaseAssetMetadataEntry { - /** - * Requested packaging style - */ - readonly packaging: string; - - /** - * Logical identifier for the asset - */ - readonly id: string; - - /** - * The hash of the asset source. - */ - readonly sourceHash: string; - - /** - * Path on disk to the asset - */ - readonly path: string; -} - -/** - * Metadata Entry spec for files. - * - * @example - * const entry = { - * packaging: 'file', - * s3BucketParameter: 'bucket-parameter', - * s3KeyParamenter: 'key-parameter', - * artifactHashParameter: 'hash-parameter', - * } - */ -export interface FileAssetMetadataEntry extends BaseAssetMetadataEntry { - /** - * Requested packaging style - */ - readonly packaging: 'zip' | 'file'; - - /** - * Name of parameter where S3 bucket should be passed in - */ - readonly s3BucketParameter: string; - - /** - * Name of parameter where S3 key should be passed in - */ - readonly s3KeyParameter: string; - - /** - * The name of the parameter where the hash of the bundled asset should be passed in. - */ - readonly artifactHashParameter: string; -} - -/** - * Metadata Entry spec for stack tag. - */ -export interface Tag { - /** - * Tag key. - * - * (In the actual file on disk this will be cased as "Key", and the structure is - * patched to match this structure upon loading: - * https://github.com/aws/aws-cdk/blob/4aadaa779b48f35838cccd4e25107b2338f05547/packages/%40aws-cdk/cloud-assembly-schema/lib/manifest.ts#L137) - */ - readonly key: string; - - /** - * Tag value. - * - * (In the actual file on disk this will be cased as "Value", and the structure is - * patched to match this structure upon loading: - * https://github.com/aws/aws-cdk/blob/4aadaa779b48f35838cccd4e25107b2338f05547/packages/%40aws-cdk/cloud-assembly-schema/lib/manifest.ts#L137) - */ - readonly value: string; -} - -/** - * Options for configuring the Docker cache backend - */ -export interface ContainerImageAssetCacheOption { - /** - * The type of cache to use. - * Refer to https://docs.docker.com/build/cache/backends/ for full list of backends. - * @default - unspecified - * - * @example 'registry' - */ - readonly type: string; - /** - * Any parameters to pass into the docker cache backend configuration. - * Refer to https://docs.docker.com/build/cache/backends/ for cache backend configuration. - * @default {} No options provided - * - * @example - * declare const branch: string; - * - * const params = { - * ref: `12345678.dkr.ecr.us-west-2.amazonaws.com/cache:${branch}`, - * mode: "max", - * }; - */ - readonly params?: { [key: string]: string }; -} - -/** - * Metadata Entry spec for container images. - * - * @example - * const entry = { - * packaging: 'container-image', - * repositoryName: 'repository-name', - * imageTag: 'tag', - * } - */ -export interface ContainerImageAssetMetadataEntry extends BaseAssetMetadataEntry { - /** - * Type of asset - */ - readonly packaging: 'container-image'; - - /** - * ECR Repository name and repo digest (separated by "@sha256:") where this - * image is stored. - * - * @default undefined If not specified, `repositoryName` and `imageTag` are - * required because otherwise how will the stack know where to find the asset, - * ha? - * @deprecated specify `repositoryName` and `imageTag` instead, and then you - * know where the image will go. - */ - readonly imageNameParameter?: string; - - /** - * ECR repository name, if omitted a default name based on the asset's ID is - * used instead. Specify this property if you need to statically address the - * image, e.g. from a Kubernetes Pod. Note, this is only the repository name, - * without the registry and the tag parts. - * - * @default - this parameter is REQUIRED after 1.21.0 - */ - readonly repositoryName?: string; - - /** - * The docker image tag to use for tagging pushed images. This field is - * required if `imageParameterName` is ommited (otherwise, the app won't be - * able to find the image). - * - * @default - this parameter is REQUIRED after 1.21.0 - */ - readonly imageTag?: string; - - /** - * Build args to pass to the `docker build` command - * - * @default no build args are passed - */ - readonly buildArgs?: { [key: string]: string }; - - /** - * SSH agent socket or keys to pass to the `docker build` command - * - * @default no ssh arg is passed - */ - readonly buildSsh?: string; - - /** - * Build secrets to pass to the `docker build` command - * - * @default no build secrets are passed - */ - readonly buildSecrets?: { [key: string]: string }; - - /** - * Docker target to build to - * - * @default no build target - */ - readonly target?: string; - - /** - * Path to the Dockerfile (relative to the directory). - * - * @default - no file is passed - */ - readonly file?: string; - - /** - * Networking mode for the RUN commands during build. - * - * @default - no networking mode specified - */ - readonly networkMode?: string; - - /** - * Platform to build for. _Requires Docker Buildx_. - * - * @default - current machine platform - */ - readonly platform?: string; - - /** - * Outputs to pass to the `docker build` command. - * - * @default - no outputs are passed to the build command (default outputs are used) - * @see https://docs.docker.com/engine/reference/commandline/build/#custom-build-outputs - */ - readonly outputs?: string[]; - - /** - * Cache from options to pass to the `docker build` command. - * - * @default - no cache from options are passed to the build command - * @see https://docs.docker.com/build/cache/backends/ - */ - readonly cacheFrom?: ContainerImageAssetCacheOption[]; - - /** - * Cache to options to pass to the `docker build` command. - * - * @default - no cache to options are passed to the build command - * @see https://docs.docker.com/build/cache/backends/ - */ - readonly cacheTo?: ContainerImageAssetCacheOption; - - /** - * Disable the cache and pass `--no-cache` to the `docker build` command. - * - * @default - cache is used - */ - readonly cacheDisabled?: boolean; -} - -/** - * @see ArtifactMetadataEntryType.ASSET - */ -export type AssetMetadataEntry = FileAssetMetadataEntry | ContainerImageAssetMetadataEntry; - -// Type aliases for metadata entries. -// Used simply to assign names to data types for more clarity. - -/** - * @see ArtifactMetadataEntryType.INFO - * @see ArtifactMetadataEntryType.WARN - * @see ArtifactMetadataEntryType.ERROR - */ -export type LogMessageMetadataEntry = string; - -/** - * @see ArtifactMetadataEntryType.LOGICAL_ID - */ -export type LogicalIdMetadataEntry = string; - -/** - * @see ArtifactMetadataEntryType.STACK_TAGS - */ -export type StackTagsMetadataEntry = Tag[]; - -/** - * Union type for all metadata entries that might exist in the manifest. - */ -export type MetadataEntryData = AssetMetadataEntry | LogMessageMetadataEntry | LogicalIdMetadataEntry | StackTagsMetadataEntry; - -/** - * Type of artifact metadata entry. - */ -export enum ArtifactMetadataEntryType { - /** - * Asset in metadata. - */ - ASSET = 'aws:cdk:asset', - - /** - * Metadata key used to print INFO-level messages by the toolkit when an app is syntheized. - */ - INFO = 'aws:cdk:info', - - /** - * Metadata key used to print WARNING-level messages by the toolkit when an app is syntheized. - */ - WARN = 'aws:cdk:warning', - - /** - * Metadata key used to print ERROR-level messages by the toolkit when an app is syntheized. - */ - ERROR = 'aws:cdk:error', - - /** - * Represents the CloudFormation logical ID of a resource at a certain path. - */ - LOGICAL_ID = 'aws:cdk:logicalId', - - /** - * Represents tags of a stack. - */ - STACK_TAGS = 'aws:cdk:stack-tags', -} - -/** - * A metadata entry in a cloud assembly artifact. - */ -export interface MetadataEntry { - /** - * The type of the metadata entry. - */ - readonly type: string; - - /** - * The data. - * - * @default - no data. - */ - readonly data?: MetadataEntryData; - - /** - * A stack trace for when the entry was created. - * - * @default - no trace. - */ - readonly trace?: string[]; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/schema.ts deleted file mode 100644 index b6c9ba4ba39cd..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/cloud-assembly/schema.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { ArtifactProperties } from './artifact-schema'; -import { ContextProvider, ContextQueryProperties } from './context-queries'; -import { MetadataEntry } from './metadata-schema'; - -/** - * Type of cloud artifact. - */ -export enum ArtifactType { - /** - * Stub required because of JSII. - */ - NONE = 'none', // required due to a jsii bug - - /** - * The artifact is an AWS CloudFormation stack. - */ - AWS_CLOUDFORMATION_STACK = 'aws:cloudformation:stack', - - /** - * The artifact contains the CDK application's construct tree. - */ - CDK_TREE = 'cdk:tree', - - /** - * Manifest for all assets in the Cloud Assembly - */ - ASSET_MANIFEST = 'cdk:asset-manifest', - - /** - * Nested Cloud Assembly - */ - NESTED_CLOUD_ASSEMBLY = 'cdk:cloud-assembly', -} - -/** - * Information about the application's runtime components. - */ -export interface RuntimeInfo { - /** - * The list of libraries loaded in the application, associated with their versions. - */ - readonly libraries: { [name: string]: string }; -} - -/** - * Represents a missing piece of context. - */ -export interface MissingContext { - /** - * The missing context key. - */ - readonly key: string; - - /** - * The provider from which we expect this context key to be obtained. - */ - readonly provider: ContextProvider; - - /** - * A set of provider-specific options. - */ - readonly props: ContextQueryProperties; -} - -/** - * A manifest for a single artifact within the cloud assembly. - */ -export interface ArtifactManifest { - /** - * The type of artifact. - */ - readonly type: ArtifactType; - - /** - * The environment into which this artifact is deployed. - * - * @default - no envrionment. - */ - readonly environment?: string; // format: aws://account/region - - /** - * Associated metadata. - * - * @default - no metadata. - */ - readonly metadata?: { [path: string]: MetadataEntry[] }; - - /** - * IDs of artifacts that must be deployed before this artifact. - * - * @default - no dependencies. - */ - readonly dependencies?: string[]; - - /** - * The set of properties for this artifact (depends on type) - * - * @default - no properties. - */ - readonly properties?: ArtifactProperties; - - /** - * A string that represents this artifact. Should only be used in user interfaces. - * - * @default - no display name - */ - readonly displayName?: string; -} - -/** - * A manifest which describes the cloud assembly. - */ -export interface AssemblyManifest { - /** - * Protocol version - */ - readonly version: string; - - /** - * The set of artifacts in this assembly. - * - * @default - no artifacts. - */ - readonly artifacts?: { [id: string]: ArtifactManifest }; - - /** - * Missing context information. If this field has values, it means that the - * cloud assembly is not complete and should not be deployed. - * - * @default - no missing context. - */ - readonly missing?: MissingContext[]; - - /** - * Runtime information. - * - * @default - no info. - */ - readonly runtime?: RuntimeInfo; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/index.ts deleted file mode 100644 index 5fd6eb6ceda07..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './cloud-assembly'; -export * from './assets'; -export * from './manifest'; -export * from './integ-tests'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/common.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/common.ts deleted file mode 100644 index 393307c260e5b..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/common.ts +++ /dev/null @@ -1,201 +0,0 @@ -/** - * In what scenarios should the CLI ask for approval - */ -export enum RequireApproval { - /** - * Never ask for approval - */ - NEVER = 'never', - - /** - * Prompt for approval for any type of change to the stack - */ - ANYCHANGE = 'any-change', - - /** - * Only prompt for approval if there are security related changes - */ - BROADENING = 'broadening', -} - -/** - * Default CDK CLI options that apply to all commands - */ -export interface DefaultCdkOptions { - /** - * List of stacks to deploy - * - * Requried if `all` is not set - * - * @default - [] - */ - readonly stacks?: string[]; - - /** - * Deploy all stacks - * - * Requried if `stacks` is not set - * - * @default - false - */ - readonly all?: boolean; - - /** - * command-line for executing your app or a cloud assembly directory - * e.g. "node bin/my-app.js" - * or - * "cdk.out" - * - * @default - read from cdk.json - */ - readonly app?: string; - - /** - * Role to pass to CloudFormation for deployment - * - * @default - use the bootstrap cfn-exec role - */ - readonly roleArn?: string; - - /** - * Additional context - * - * @default - no additional context - */ - readonly context?: { [name: string]: string }; - - /** - * Print trace for stack warnings - * - * @default false - */ - readonly trace?: boolean; - - /** - * Do not construct stacks with warnings - * - * @default false - */ - readonly strict?: boolean; - - /** - * Perform context lookups. - * - * Synthesis fails if this is disabled and context lookups need - * to be performed - * - * @default true - */ - readonly lookups?: boolean; - - /** - * Ignores synthesis errors, which will likely produce an invalid output - * - * @default false - */ - readonly ignoreErrors?: boolean; - - /** - * Use JSON output instead of YAML when templates are printed - * to STDOUT - * - * @default false - */ - readonly json?: boolean; - - /** - * show debug logs - * - * @default false - */ - readonly verbose?: boolean; - - /** - * enable emission of additional debugging information, such as creation stack - * traces of tokens - * - * @default false - */ - readonly debug?: boolean; - - /** - * Use the indicated AWS profile as the default environment - * - * @default - no profile is used - */ - readonly profile?: string; - - /** - * Use the indicated proxy. Will read from - * HTTPS_PROXY environment if specified - * - * @default - no proxy - */ - readonly proxy?: string; - - /** - * Path to CA certificate to use when validating HTTPS - * requests. - * - * @default - read from AWS_CA_BUNDLE environment variable - */ - readonly caBundlePath?: string; - - /** - * Force trying to fetch EC2 instance credentials - * - * @default - guess EC2 instance status - */ - readonly ec2Creds?: boolean; - - /** - * Include "AWS::CDK::Metadata" resource in synthesized templates - * - * @default true - */ - readonly versionReporting?: boolean; - - /** - * Include "aws:cdk:path" CloudFormation metadata for each resource - * - * @default true - */ - readonly pathMetadata?: boolean; - - /** - * Include "aws:asset:*" CloudFormation metadata for resources that use assets - * - * @default true - */ - readonly assetMetadata?: boolean; - - /** - * Copy assets to the output directory - * - * Needed for local debugging the source files with SAM CLI - * - * @default false - */ - readonly staging?: boolean; - - /** - * Emits the synthesized cloud assembly into a directory - * - * @default cdk.out - */ - readonly output?: string; - - /** - * Show relevant notices - * - * @default true - */ - readonly notices?: boolean; - - /** - * Show colors and other style from console output - * - * @default true - */ - readonly color?: boolean; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/deploy.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/deploy.ts deleted file mode 100644 index 8d63a7d9644f9..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/deploy.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { DefaultCdkOptions, RequireApproval } from './common'; - -/** - * Options to use with cdk deploy - */ -export interface DeployOptions extends DefaultCdkOptions { - /** - * Only perform action on the given stack - * - * @default false - */ - readonly exclusively?: boolean; - - /** - * Name of the toolkit stack to use/deploy - * - * @default CDKToolkit - */ - readonly toolkitStackName?: string; - - /** - * Reuse the assets with the given asset IDs - * - * @default - do not reuse assets - */ - readonly reuseAssets?: string[]; - - /** - * Optional name to use for the CloudFormation change set. - * If not provided, a name will be generated automatically. - * - * @default - auto generate a name - */ - readonly changeSetName?: string; - - /** - * Always deploy, even if templates are identical. - * @default false - */ - readonly force?: boolean; - - /** - * Rollback failed deployments - * - * @default true - */ - readonly rollback?: boolean; - - /** - * ARNs of SNS topics that CloudFormation will notify with stack related events - * - * @default - no notifications - */ - readonly notificationArns?: string[]; - - /** - * What kind of security changes require approval - * - * @default RequireApproval.Never - */ - readonly requireApproval?: RequireApproval; - - /** - * Whether to execute the ChangeSet - * Not providing `execute` parameter will result in execution of ChangeSet - * @default true - */ - readonly execute?: boolean; - - /** - * Additional parameters for CloudFormation at deploy time - * @default {} - */ - readonly parameters?: { [name: string]: string }; - - /** - * Use previous values for unspecified parameters - * - * If not set, all parameters must be specified for every deployment. - * - * @default true - */ - readonly usePreviousParameters?: boolean; - - /** - * Path to file where stack outputs will be written after a successful deploy as JSON - * @default - Outputs are not written to any file - */ - readonly outputsFile?: string; - - /** - * Whether we are on a CI system - * - * @default false - */ - readonly ci?: boolean; - - /** - * Deploy multiple stacks in parallel - * - * @default 1 - */ - readonly concurrency?: number; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/destroy.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/destroy.ts deleted file mode 100644 index 9dfe8f267c6db..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/destroy.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DefaultCdkOptions } from './common'; - -/** - * Options to use with cdk destroy - */ -export interface DestroyOptions extends DefaultCdkOptions { - /** - * Do not ask for permission before destroying stacks - * - * @default false - */ - readonly force?: boolean; - - /** - * Only destroy the given stack - * - * @default false - */ - readonly exclusively?: boolean; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/index.ts deleted file mode 100644 index 528980446938b..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/commands/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './common'; -export * from './deploy'; -export * from './destroy'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/index.ts deleted file mode 100644 index 5a08e62a47958..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './schema'; -export * from './commands'; -export * from './test-case'; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/schema.ts deleted file mode 100644 index 1a617e23cf959..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/schema.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TestCase } from './test-case'; -/** - * Definitions for the integration testing manifest - */ -export interface IntegManifest { - /** - * Version of the manifest - */ - readonly version: string; - - /** - * Enable lookups for this test. If lookups are enabled - * then `stackUpdateWorkflow` must be set to false. - * Lookups should only be enabled when you are explicitely testing - * lookups. - * - * @default false - */ - readonly enableLookups?: boolean; - - /** - * Additional context to use when performing - * a synth. Any context provided here will override - * any default context - * - * @default - no additional context - */ - readonly synthContext?: { [name: string]: string }; - - /** - * test cases - */ - readonly testCases: { [testName: string]: TestCase }; -} - diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/test-case.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/test-case.ts deleted file mode 100644 index 6878f9da2563c..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/integ-tests/test-case.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { DeployOptions, DestroyOptions } from './commands'; - -/** - * The set of options to control the workflow of the test runner - */ -export interface TestOptions { - /** - * Run update workflow on this test case - * This should only be set to false to test scenarios - * that are not possible to test as part of the update workflow - * - * @default true - */ - readonly stackUpdateWorkflow?: boolean; - - /** - * Additional options to use for each CDK command - * - * @default - runner default options - */ - readonly cdkCommandOptions?: CdkCommands; - - /** - * Additional commands to run at predefined points in the test workflow - * - * e.g. { postDeploy: ['yarn', 'test'] } - * - * @default - no hooks - */ - readonly hooks?: Hooks; - - /** - * Whether or not to include asset hashes in the diff - * Asset hashes can introduces a lot of unneccessary noise into tests, - * but there are some cases where asset hashes _should_ be included. For example - * any tests involving custom resources or bundling - * - * @default false - */ - readonly diffAssets?: boolean; - - /** - * List of CloudFormation resource types in this stack that can - * be destroyed as part of an update without failing the test. - * - * This list should only include resources that for this specific - * integration test we are sure will not cause errors or an outage if - * destroyed. For example, maybe we know that a new resource will be created - * first before the old resource is destroyed which prevents any outage. - * - * e.g. ['AWS::IAM::Role'] - * - * @default - do not allow destruction of any resources on update - */ - readonly allowDestroy?: string[]; - - /** - * Limit deployment to these regions - * - * @default - can run in any region - */ - readonly regions?: string[]; -} - -/** - * Represents an integration test case - */ -export interface TestCase extends TestOptions { - /** - * Stacks that should be tested as part of this test case - * The stackNames will be passed as args to the cdk commands - * so dependent stacks will be automatically deployed unless - * `exclusively` is passed - */ - readonly stacks: string[]; - - /** - * The node id of the stack that contains assertions. - * This is the value that can be used to deploy the stack with the CDK CLI - * - * @default - no assertion stack - */ - readonly assertionStack?: string; - - /** - * The name of the stack that contains assertions - * - * @default - no assertion stack - */ - readonly assertionStackName?: string; -} - -/** - * Commands to run at predefined points during the - * integration test workflow - */ -export interface Hooks { - /** - * Commands to run prior to deploying the cdk stacks - * in the integration test - * - * @default - no commands - */ - readonly preDeploy?: string[]; - - /** - * Commands to run prior after deploying the cdk stacks - * in the integration test - * - * @default - no commands - */ - readonly postDeploy?: string[]; - - /** - * Commands to run prior to destroying the cdk stacks - * in the integration test - * - * @default - no commands - */ - readonly preDestroy?: string[]; - - /** - * Commands to run after destroying the cdk stacks - * in the integration test - * - * @default - no commands - */ - readonly postDestroy?: string[]; -} - -/** - * Represents a cdk command - * i.e. `synth`, `deploy`, & `destroy` - */ -export interface CdkCommand { - /** - * Whether or not to run this command as part of the workflow - * This can be used if you only want to test some of the workflow - * for example enable `synth` and disable `deploy` & `destroy` in order - * to limit the test to synthesis - * - * @default true - */ - readonly enabled?: boolean; - - /** - * If the runner should expect this command to fail - * - * @default false - */ - readonly expectError?: boolean; - - /** - * This can be used in combination with `expectedError` - * to validate that a specific message is returned. - * - * @default - do not validate message - */ - readonly expectedMessage?: string; -} - -/** - * Represents a cdk deploy command - */ -export interface DeployCommand extends CdkCommand { - /** - * Additional arguments to pass to the command - * This can be used to test specific CLI functionality - * - * @default - only default args are used - */ - readonly args?: DeployOptions; -} - -/** - * Represents a cdk destroy command - */ -export interface DestroyCommand extends CdkCommand { - /** - * Additional arguments to pass to the command - * This can be used to test specific CLI functionality - * - * @default - only default args are used - */ - readonly args?: DestroyOptions; -} - -/** - * Options for specific cdk commands that are run - * as part of the integration test workflow - */ -export interface CdkCommands { - /** - * Options to for the cdk deploy command - * - * @default - default deploy options - */ - readonly deploy?: DeployCommand; - - /** - * Options to for the cdk destroy command - * - * @default - default destroy options - */ - readonly destroy?: DestroyCommand; -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/lib/manifest.ts b/packages/aws-cdk-lib/cloud-assembly-schema/lib/manifest.ts deleted file mode 100644 index 76069e0187d4c..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/lib/manifest.ts +++ /dev/null @@ -1,292 +0,0 @@ -import * as fs from 'fs'; -import * as jsonschema from 'jsonschema'; -import * as semver from 'semver'; -import * as assets from './assets'; -import * as assembly from './cloud-assembly'; -import * as integ from './integ-tests'; - -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable @typescript-eslint/no-require-imports */ - -// this prefix is used by the CLI to identify this specific error. -// in which case we want to instruct the user to upgrade his CLI. -// see exec.ts#createAssembly -export const VERSION_MISMATCH: string = 'Cloud assembly schema version mismatch'; - -const ASSETS_SCHEMA = require('../schema/assets.schema.json'); - -const ASSEMBLY_SCHEMA = require('../schema/cloud-assembly.schema.json'); - -/** - * Version is shared for both manifests - */ -const SCHEMA_VERSION = require('../schema/cloud-assembly.version.json').version; - -const INTEG_SCHEMA = require('../schema/integ.schema.json'); - -/** - * Options for the loadManifest operation - */ -export interface LoadManifestOptions { - /** - * Skip the version check - * - * This means you may read a newer cloud assembly than the CX API is designed - * to support, and your application may not be aware of all features that in use - * in the Cloud Assembly. - * - * @default false - */ - readonly skipVersionCheck?: boolean; - - /** - * Skip enum checks - * - * This means you may read enum values you don't know about yet. Make sure to always - * check the values of enums you encounter in the manifest. - * - * @default false - */ - readonly skipEnumCheck?: boolean; - - /** - * Topologically sort all artifacts - * - * This parameter is only respected by the constructor of `CloudAssembly`. The - * property lives here for backwards compatibility reasons. - * - * @default true - */ - readonly topoSort?: boolean; -} - -/** - * Protocol utility class. - */ -export class Manifest { - /** - * Validates and saves the cloud assembly manifest to file. - * - * @param manifest - manifest. - * @param filePath - output file path. - */ - public static saveAssemblyManifest(manifest: assembly.AssemblyManifest, filePath: string) { - Manifest.saveManifest(manifest, filePath, ASSEMBLY_SCHEMA, Manifest.patchStackTagsOnWrite); - } - - /** - * Load and validates the cloud assembly manifest from file. - * - * @param filePath - path to the manifest file. - */ - public static loadAssemblyManifest(filePath: string, options?: LoadManifestOptions): assembly.AssemblyManifest { - return Manifest.loadManifest(filePath, ASSEMBLY_SCHEMA, Manifest.patchStackTagsOnRead, options); - } - - /** - * Validates and saves the asset manifest to file. - * - * @param manifest - manifest. - * @param filePath - output file path. - */ - public static saveAssetManifest(manifest: assets.AssetManifest, filePath: string) { - Manifest.saveManifest(manifest, filePath, ASSETS_SCHEMA, Manifest.patchStackTagsOnRead); - } - - /** - * Load and validates the asset manifest from file. - * - * @param filePath - path to the manifest file. - */ - public static loadAssetManifest(filePath: string): assets.AssetManifest { - return this.loadManifest(filePath, ASSETS_SCHEMA); - } - - /** - * Validates and saves the integ manifest to file. - * - * @param manifest - manifest. - * @param filePath - output file path. - */ - public static saveIntegManifest(manifest: integ.IntegManifest, filePath: string) { - Manifest.saveManifest(manifest, filePath, INTEG_SCHEMA); - } - - /** - * Load and validates the integ manifest from file. - * - * @param filePath - path to the manifest file. - */ - public static loadIntegManifest(filePath: string): integ.IntegManifest { - return this.loadManifest(filePath, INTEG_SCHEMA); - } - - /** - * Fetch the current schema version number. - */ - public static version(): string { - return SCHEMA_VERSION; - } - - /** - * Deprecated - * @deprecated use `saveAssemblyManifest()` - */ - public static save(manifest: assembly.AssemblyManifest, filePath: string) { return this.saveAssemblyManifest(manifest, filePath); } - - /** - * Deprecated - * @deprecated use `loadAssemblyManifest()` - */ - public static load(filePath: string): assembly.AssemblyManifest { return this.loadAssemblyManifest(filePath); } - - private static validate(manifest: { version: string }, schema: jsonschema.Schema, options?: LoadManifestOptions) { - function parseVersion(version: string) { - const ver = semver.valid(version); - if (!ver) { - throw new Error(`Invalid semver string: "${version}"`); - } - return ver; - } - - const maxSupported = parseVersion(Manifest.version()); - const actual = parseVersion(manifest.version); - - // first validate the version should be accepted. - if (semver.gt(actual, maxSupported) && !options?.skipVersionCheck) { - // we use a well known error prefix so that the CLI can identify this specific error - // and print some more context to the user. - throw new Error(`${VERSION_MISMATCH}: Maximum schema version supported is ${maxSupported}, but found ${actual}`); - } - - // now validate the format is good. - const validator = new jsonschema.Validator(); - const result = validator.validate(manifest, schema, { - - // does exist but is not in the TypeScript definitions - nestedErrors: true, - - allowUnknownAttributes: false, - - } as any); - - let errors = result.errors; - if (options?.skipEnumCheck) { - // Enum validations aren't useful when - errors = stripEnumErrors(errors); - } - - if (errors.length > 0) { - throw new Error(`Invalid assembly manifest:\n${errors.map(e => e.stack).join('\n')}`); - } - } - - private static saveManifest(manifest: any, filePath: string, schema: jsonschema.Schema, preprocess?: (obj: any) => any) { - let withVersion = { ...manifest, version: Manifest.version() }; - Manifest.validate(withVersion, schema); - if (preprocess) { - withVersion = preprocess(withVersion); - } - fs.writeFileSync(filePath, JSON.stringify(withVersion, undefined, 2)); - } - - private static loadManifest(filePath: string, schema: jsonschema.Schema, preprocess?: (obj: any) => any, options?: LoadManifestOptions) { - const contents = fs.readFileSync(filePath, { encoding: 'utf-8' }); - let obj; - try { - obj = JSON.parse(contents); - } catch (e: any) { - throw new Error(`${e.message}, while parsing ${JSON.stringify(contents)}`); - } - if (preprocess) { - obj = preprocess(obj); - } - Manifest.validate(obj, schema, options); - return obj; - } - - /** - * This requires some explaining... - * - * We previously used `{ Key, Value }` for the object that represents a stack tag. (Notice the casing) - * @link https://github.com/aws/aws-cdk/blob/v1.27.0/packages/aws-cdk/lib/api/cxapp/stacks.ts#L427. - * - * When that object moved to this package, it had to be JSII compliant, which meant the property - * names must be `camelCased`, and not `PascalCased`. This meant it no longer matches the structure in the `manifest.json` file. - * In order to support current manifest files, we have to translate the `PascalCased` representation to the new `camelCased` one. - * - * Note that the serialization itself still writes `PascalCased` because it relates to how CloudFormation expects it. - * - * Ideally, we would start writing the `camelCased` and translate to how CloudFormation expects it when needed. But this requires nasty - * backwards-compatibility code and it just doesn't seem to be worth the effort. - */ - private static patchStackTagsOnRead(manifest: assembly.AssemblyManifest) { - return Manifest.replaceStackTags(manifest, tags => tags.map((diskTag: any) => ({ - key: diskTag.Key, - value: diskTag.Value, - }))); - } - - /** - * See explanation on `patchStackTagsOnRead` - * - * Translate stack tags metadata if it has the "right" casing. - */ - private static patchStackTagsOnWrite(manifest: assembly.AssemblyManifest) { - return Manifest.replaceStackTags(manifest, tags => tags.map(memTag => - // Might already be uppercased (because stack synthesis generates it in final form yet) - ('Key' in memTag ? memTag : { Key: memTag.key, Value: memTag.value }) as any, - )); - } - - /** - * Recursively replace stack tags in the stack metadata - */ - private static replaceStackTags(manifest: assembly.AssemblyManifest, fn: Endofunctor): assembly.AssemblyManifest { - // Need to add in the `noUndefined`s because otherwise jest snapshot tests are going to freak out - // about the keys with values that are `undefined` (even though they would never be JSON.stringified) - return noUndefined({ - ...manifest, - artifacts: mapValues(manifest.artifacts, artifact => { - if (artifact.type !== assembly.ArtifactType.AWS_CLOUDFORMATION_STACK) { return artifact; } - return noUndefined({ - ...artifact, - metadata: mapValues(artifact.metadata, metadataEntries => metadataEntries.map(metadataEntry => { - if (metadataEntry.type !== assembly.ArtifactMetadataEntryType.STACK_TAGS || !metadataEntry.data) { return metadataEntry; } - return { - ...metadataEntry, - data: fn(metadataEntry.data as assembly.StackTagsMetadataEntry), - }; - })), - } as assembly.ArtifactManifest); - }), - }); - } - - private constructor() {} -} - -type Endofunctor = (x: A) => A; - -function mapValues(xs: Record | undefined, fn: (x: A) => B): Record | undefined { - if (!xs) { return undefined; } - const ret: Record | undefined = {}; - for (const [k, v] of Object.entries(xs)) { - ret[k] = fn(v); - } - return ret; -} - -function noUndefined(xs: A): A { - const ret: any = {}; - for (const [k, v] of Object.entries(xs)) { - if (v !== undefined) { - ret[k] = v; - } - } - return ret; -} - -function stripEnumErrors(errors: jsonschema.ValidationError[]) { - return errors.filter(e => typeof e.schema ==='string' || !('enum' in e.schema)); -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/README.md b/packages/aws-cdk-lib/cloud-assembly-schema/schema/README.md deleted file mode 100644 index ae58ce74ee4e8..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## Cloud Assembly JSON Schema - -**DO NOT MODIFY FILES IN THIS DIRECTORY BY HAND** - -To modify, run `yarn update-schema`. \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json deleted file mode 100644 index d001be08f6cb5..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/assets.schema.json +++ /dev/null @@ -1,267 +0,0 @@ -{ - "$ref": "#/definitions/AssetManifest", - "definitions": { - "AssetManifest": { - "description": "Definitions for the asset manifest", - "type": "object", - "properties": { - "version": { - "description": "Version of the manifest", - "type": "string" - }, - "files": { - "description": "The file assets in this manifest (Default - No files)", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/FileAsset" - } - }, - "dockerImages": { - "description": "The Docker image assets in this manifest (Default - No Docker images)", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/DockerImageAsset" - } - } - }, - "required": [ - "version" - ] - }, - "FileAsset": { - "description": "A file asset", - "type": "object", - "properties": { - "source": { - "$ref": "#/definitions/FileSource", - "description": "Source description for file assets" - }, - "destinations": { - "description": "Destinations for this file asset", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/FileDestination" - } - } - }, - "required": [ - "destinations", - "source" - ] - }, - "FileSource": { - "description": "Describe the source of a file asset", - "type": "object", - "properties": { - "executable": { - "description": "External command which will produce the file asset to upload. (Default - Exactly one of `executable` and `path` is required.)", - "type": "array", - "items": { - "type": "string" - } - }, - "path": { - "description": "The filesystem object to upload\n\nThis path is relative to the asset manifest location. (Default - Exactly one of `executable` and `path` is required.)", - "type": "string" - }, - "packaging": { - "description": "Packaging method\n\nOnly allowed when `path` is specified. (Default FILE)", - "enum": [ - "file", - "zip" - ], - "type": "string" - } - } - }, - "FileDestination": { - "description": "Where in S3 a file asset needs to be published", - "type": "object", - "properties": { - "bucketName": { - "description": "The name of the bucket", - "type": "string" - }, - "objectKey": { - "description": "The destination object key", - "type": "string" - }, - "region": { - "description": "The region where this asset will need to be published (Default - Current region)", - "type": "string" - }, - "assumeRoleArn": { - "description": "The role that needs to be assumed while publishing this asset (Default - No role will be assumed)", - "type": "string" - }, - "assumeRoleExternalId": { - "description": "The ExternalId that needs to be supplied while assuming this role (Default - No ExternalId will be supplied)", - "type": "string" - }, - "assumeRoleSessionTags": { - "description": "The session tags to use for the assume role (Default - No Session Tags)", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "bucketName", - "objectKey" - ] - }, - "DockerImageAsset": { - "description": "A file asset", - "type": "object", - "properties": { - "source": { - "$ref": "#/definitions/DockerImageSource", - "description": "Source description for file assets" - }, - "destinations": { - "description": "Destinations for this file asset", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/DockerImageDestination" - } - } - }, - "required": [ - "destinations", - "source" - ] - }, - "DockerImageSource": { - "description": "Properties for how to produce a Docker image from a source", - "type": "object", - "properties": { - "directory": { - "description": "The directory containing the Docker image build instructions.\n\nThis path is relative to the asset manifest location. (Default - Exactly one of `directory` and `executable` is required)", - "type": "string" - }, - "executable": { - "description": "A command-line executable that returns the name of a local\nDocker image on stdout after being run. (Default - Exactly one of `directory` and `executable` is required)", - "type": "array", - "items": { - "type": "string" - } - }, - "dockerFile": { - "description": "The name of the file with build instructions\n\nOnly allowed when `directory` is set. (Default Dockerfile)", - "type": "string" - }, - "dockerBuildTarget": { - "description": "Target build stage in a Dockerfile with multiple build stages\n\nOnly allowed when `directory` is set. (Default - The last stage in the Dockerfile)", - "type": "string" - }, - "dockerBuildArgs": { - "description": "Additional build arguments\n\nOnly allowed when `directory` is set. (Default - No additional build arguments)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "dockerBuildSsh": { - "description": "SSH agent socket or keys\n\nRequires building with docker buildkit. (Default - No ssh flag is set)", - "type": "string" - }, - "dockerBuildSecrets": { - "description": "Additional build secrets\n\nOnly allowed when `directory` is set. (Default - No additional build secrets)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "networkMode": { - "description": "Networking mode for the RUN commands during build. _Requires Docker Engine API v1.25+_.\n\nSpecify this property to build images on a specific networking mode. (Default - no networking mode specified)", - "type": "string" - }, - "platform": { - "description": "Platform to build for. _Requires Docker Buildx_.\n\nSpecify this property to build images on a specific platform/architecture. (Default - current machine platform)", - "type": "string" - }, - "dockerOutputs": { - "description": "Outputs (Default - no outputs are passed to the build command (default outputs are used))", - "type": "array", - "items": { - "type": "string" - } - }, - "cacheFrom": { - "description": "Cache from options to pass to the `docker build` command. (Default - no cache from options are passed to the build command)", - "type": "array", - "items": { - "$ref": "#/definitions/DockerCacheOption" - } - }, - "cacheTo": { - "description": "Cache to options to pass to the `docker build` command. (Default - no cache to options are passed to the build command)", - "$ref": "#/definitions/DockerCacheOption" - }, - "cacheDisabled": { - "description": "Disable the cache and pass `--no-cache` to the `docker build` command. (Default - cache is used)", - "type": "boolean" - } - } - }, - "DockerCacheOption": { - "description": "Options for configuring the Docker cache backend", - "type": "object", - "properties": { - "type": { - "description": "The type of cache to use.\nRefer to https://docs.docker.com/build/cache/backends/ for full list of backends. (Default - unspecified)", - "type": "string" - }, - "params": { - "description": "Any parameters to pass into the docker cache backend configuration.\nRefer to https://docs.docker.com/build/cache/backends/ for cache backend configuration. (Default {} No options provided)", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "type" - ] - }, - "DockerImageDestination": { - "description": "Where to publish docker images", - "type": "object", - "properties": { - "repositoryName": { - "description": "Name of the ECR repository to publish to", - "type": "string" - }, - "imageTag": { - "description": "Tag of the image to publish", - "type": "string" - }, - "region": { - "description": "The region where this asset will need to be published (Default - Current region)", - "type": "string" - }, - "assumeRoleArn": { - "description": "The role that needs to be assumed while publishing this asset (Default - No role will be assumed)", - "type": "string" - }, - "assumeRoleExternalId": { - "description": "The ExternalId that needs to be supplied while assuming this role (Default - No ExternalId will be supplied)", - "type": "string" - }, - "assumeRoleSessionTags": { - "description": "The session tags to use for the assume role (Default - No Session Tags)", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "imageTag", - "repositoryName" - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json deleted file mode 100644 index 60a36e54d682a..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ /dev/null @@ -1,953 +0,0 @@ -{ - "$ref": "#/definitions/AssemblyManifest", - "definitions": { - "AssemblyManifest": { - "description": "A manifest which describes the cloud assembly.", - "type": "object", - "properties": { - "version": { - "description": "Protocol version", - "type": "string" - }, - "artifacts": { - "description": "The set of artifacts in this assembly. (Default - no artifacts.)", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ArtifactManifest" - } - }, - "missing": { - "description": "Missing context information. If this field has values, it means that the\ncloud assembly is not complete and should not be deployed. (Default - no missing context.)", - "type": "array", - "items": { - "$ref": "#/definitions/MissingContext" - } - }, - "runtime": { - "description": "Runtime information. (Default - no info.)", - "$ref": "#/definitions/RuntimeInfo" - } - }, - "required": [ - "version" - ] - }, - "ArtifactManifest": { - "description": "A manifest for a single artifact within the cloud assembly.", - "type": "object", - "properties": { - "type": { - "$ref": "#/definitions/ArtifactType", - "description": "The type of artifact." - }, - "environment": { - "description": "The environment into which this artifact is deployed. (Default - no envrionment.)", - "type": "string" - }, - "metadata": { - "description": "Associated metadata. (Default - no metadata.)", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/MetadataEntry" - } - } - }, - "dependencies": { - "description": "IDs of artifacts that must be deployed before this artifact. (Default - no dependencies.)", - "type": "array", - "items": { - "type": "string" - } - }, - "properties": { - "description": "The set of properties for this artifact (depends on type) (Default - no properties.)", - "anyOf": [ - { - "$ref": "#/definitions/AwsCloudFormationStackProperties" - }, - { - "$ref": "#/definitions/AssetManifestProperties" - }, - { - "$ref": "#/definitions/TreeArtifactProperties" - }, - { - "$ref": "#/definitions/NestedCloudAssemblyProperties" - } - ] - }, - "displayName": { - "description": "A string that represents this artifact. Should only be used in user interfaces. (Default - no display name)", - "type": "string" - } - }, - "required": [ - "type" - ] - }, - "ArtifactType": { - "description": "Type of cloud artifact.", - "type": "string", - "enum": [ - "none", - "aws:cloudformation:stack", - "cdk:tree", - "cdk:asset-manifest", - "cdk:cloud-assembly" - ] - }, - "MetadataEntry": { - "description": "A metadata entry in a cloud assembly artifact.", - "type": "object", - "properties": { - "type": { - "description": "The type of the metadata entry.", - "type": "string" - }, - "data": { - "description": "The data. (Default - no data.)", - "anyOf": [ - { - "$ref": "#/definitions/FileAssetMetadataEntry" - }, - { - "$ref": "#/definitions/ContainerImageAssetMetadataEntry" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/Tag" - } - }, - { - "type": "string" - }, - { - "description": "Free form data." - } - ] - }, - "trace": { - "description": "A stack trace for when the entry was created. (Default - no trace.)", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "type" - ] - }, - "FileAssetMetadataEntry": { - "description": "Metadata Entry spec for files.", - "type": "object", - "properties": { - "packaging": { - "description": "Requested packaging style", - "enum": [ - "file", - "zip" - ], - "type": "string" - }, - "s3BucketParameter": { - "description": "Name of parameter where S3 bucket should be passed in", - "type": "string" - }, - "s3KeyParameter": { - "description": "Name of parameter where S3 key should be passed in", - "type": "string" - }, - "artifactHashParameter": { - "description": "The name of the parameter where the hash of the bundled asset should be passed in.", - "type": "string" - }, - "id": { - "description": "Logical identifier for the asset", - "type": "string" - }, - "sourceHash": { - "description": "The hash of the asset source.", - "type": "string" - }, - "path": { - "description": "Path on disk to the asset", - "type": "string" - } - }, - "required": [ - "artifactHashParameter", - "id", - "packaging", - "path", - "s3BucketParameter", - "s3KeyParameter", - "sourceHash" - ] - }, - "ContainerImageAssetMetadataEntry": { - "description": "Metadata Entry spec for container images.", - "type": "object", - "properties": { - "packaging": { - "description": "Type of asset", - "type": "string", - "const": "container-image" - }, - "imageNameParameter": { - "description": "ECR Repository name and repo digest (separated by \"@sha256:\") where this\nimage is stored. (Default undefined If not specified, `repositoryName` and `imageTag` are\nrequired because otherwise how will the stack know where to find the asset,\nha?)", - "type": "string" - }, - "repositoryName": { - "description": "ECR repository name, if omitted a default name based on the asset's ID is\nused instead. Specify this property if you need to statically address the\nimage, e.g. from a Kubernetes Pod. Note, this is only the repository name,\nwithout the registry and the tag parts. (Default - this parameter is REQUIRED after 1.21.0)", - "type": "string" - }, - "imageTag": { - "description": "The docker image tag to use for tagging pushed images. This field is\nrequired if `imageParameterName` is ommited (otherwise, the app won't be\nable to find the image). (Default - this parameter is REQUIRED after 1.21.0)", - "type": "string" - }, - "buildArgs": { - "description": "Build args to pass to the `docker build` command (Default no build args are passed)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "buildSsh": { - "description": "SSH agent socket or keys to pass to the `docker build` command (Default no ssh arg is passed)", - "type": "string" - }, - "buildSecrets": { - "description": "Build secrets to pass to the `docker build` command (Default no build secrets are passed)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "target": { - "description": "Docker target to build to (Default no build target)", - "type": "string" - }, - "file": { - "description": "Path to the Dockerfile (relative to the directory). (Default - no file is passed)", - "type": "string" - }, - "networkMode": { - "description": "Networking mode for the RUN commands during build. (Default - no networking mode specified)", - "type": "string" - }, - "platform": { - "description": "Platform to build for. _Requires Docker Buildx_. (Default - current machine platform)", - "type": "string" - }, - "outputs": { - "description": "Outputs to pass to the `docker build` command. (Default - no outputs are passed to the build command (default outputs are used))", - "type": "array", - "items": { - "type": "string" - } - }, - "cacheFrom": { - "description": "Cache from options to pass to the `docker build` command. (Default - no cache from options are passed to the build command)", - "type": "array", - "items": { - "$ref": "#/definitions/ContainerImageAssetCacheOption" - } - }, - "cacheTo": { - "description": "Cache to options to pass to the `docker build` command. (Default - no cache to options are passed to the build command)", - "$ref": "#/definitions/ContainerImageAssetCacheOption" - }, - "cacheDisabled": { - "description": "Disable the cache and pass `--no-cache` to the `docker build` command. (Default - cache is used)", - "type": "boolean" - }, - "id": { - "description": "Logical identifier for the asset", - "type": "string" - }, - "sourceHash": { - "description": "The hash of the asset source.", - "type": "string" - }, - "path": { - "description": "Path on disk to the asset", - "type": "string" - } - }, - "required": [ - "id", - "packaging", - "path", - "sourceHash" - ] - }, - "ContainerImageAssetCacheOption": { - "description": "Options for configuring the Docker cache backend", - "type": "object", - "properties": { - "type": { - "description": "The type of cache to use.\nRefer to https://docs.docker.com/build/cache/backends/ for full list of backends. (Default - unspecified)", - "type": "string" - }, - "params": { - "description": "Any parameters to pass into the docker cache backend configuration.\nRefer to https://docs.docker.com/build/cache/backends/ for cache backend configuration. (Default {} No options provided)", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "type" - ] - }, - "Tag": { - "description": "Metadata Entry spec for stack tag.", - "type": "object", - "properties": { - "key": { - "description": "Tag key.\n\n(In the actual file on disk this will be cased as \"Key\", and the structure is\npatched to match this structure upon loading:\nhttps://github.com/aws/aws-cdk/blob/4aadaa779b48f35838cccd4e25107b2338f05547/packages/%40aws-cdk/cloud-assembly-schema/lib/manifest.ts#L137)", - "type": "string" - }, - "value": { - "description": "Tag value.\n\n(In the actual file on disk this will be cased as \"Value\", and the structure is\npatched to match this structure upon loading:\nhttps://github.com/aws/aws-cdk/blob/4aadaa779b48f35838cccd4e25107b2338f05547/packages/%40aws-cdk/cloud-assembly-schema/lib/manifest.ts#L137)", - "type": "string" - } - }, - "required": [ - "key", - "value" - ] - }, - "AwsCloudFormationStackProperties": { - "description": "Artifact properties for CloudFormation stacks.", - "type": "object", - "properties": { - "templateFile": { - "description": "A file relative to the assembly root which contains the CloudFormation template for this stack.", - "type": "string" - }, - "parameters": { - "description": "Values for CloudFormation stack parameters that should be passed when the stack is deployed. (Default - No parameters)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "tags": { - "description": "Values for CloudFormation stack tags that should be passed when the stack is deployed. (Default - No tags)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "stackName": { - "description": "The name to use for the CloudFormation stack. (Default - name derived from artifact ID)", - "type": "string" - }, - "terminationProtection": { - "description": "Whether to enable termination protection for this stack.", - "default": false, - "type": "boolean" - }, - "assumeRoleArn": { - "description": "The role that needs to be assumed to deploy the stack (Default - No role is assumed (current credentials are used))", - "type": "string" - }, - "assumeRoleExternalId": { - "description": "External ID to use when assuming role for cloudformation deployments (Default - No external ID)", - "type": "string" - }, - "assumeRoleSessionTags": { - "description": "The session tags to use for the assume role (Default - No Session Tags)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "cloudFormationExecutionRoleArn": { - "description": "The role that is passed to CloudFormation to execute the change set (Default - No role is passed (currently assumed role/credentials are used))", - "type": "string" - }, - "lookupRole": { - "description": "The role to use to look up values from the target AWS account (Default - No role is assumed (current credentials are used))", - "$ref": "#/definitions/BootstrapRole" - }, - "stackTemplateAssetObjectUrl": { - "description": "If the stack template has already been included in the asset manifest, its asset URL (Default - Not uploaded yet, upload just before deploying)", - "type": "string" - }, - "requiresBootstrapStackVersion": { - "description": "Version of bootstrap stack required to deploy this stack (Default - No bootstrap stack required)", - "type": "number" - }, - "bootstrapStackVersionSsmParameter": { - "description": "SSM parameter where the bootstrap stack version number can be found\n\nOnly used if `requiresBootstrapStackVersion` is set.\n\n- If this value is not set, the bootstrap stack name must be known at\n deployment time so the stack version can be looked up from the stack\n outputs.\n- If this value is set, the bootstrap stack can have any name because\n we won't need to look it up. (Default - Bootstrap stack version number looked up)", - "type": "string" - }, - "validateOnSynth": { - "description": "Whether this stack should be validated by the CLI after synthesis (Default - false)", - "type": "boolean" - } - }, - "required": [ - "templateFile" - ] - }, - "BootstrapRole": { - "description": "Information needed to access an IAM role created\nas part of the bootstrap process", - "type": "object", - "properties": { - "arn": { - "description": "The ARN of the IAM role created as part of bootrapping\ne.g. lookupRoleArn", - "type": "string" - }, - "assumeRoleExternalId": { - "description": "External ID to use when assuming the bootstrap role (Default - No external ID)", - "type": "string" - }, - "assumeRoleSessionTags": { - "description": "The session tags to use for the assume role (Default - No Session Tags)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "requiresBootstrapStackVersion": { - "description": "Version of bootstrap stack required to use this role (Default - No bootstrap stack required)", - "type": "number" - }, - "bootstrapStackVersionSsmParameter": { - "description": "Name of SSM parameter with bootstrap stack version (Default - Discover SSM parameter by reading stack)", - "type": "string" - } - }, - "required": [ - "arn" - ] - }, - "AssetManifestProperties": { - "description": "Artifact properties for the Asset Manifest", - "type": "object", - "properties": { - "file": { - "description": "Filename of the asset manifest", - "type": "string" - }, - "requiresBootstrapStackVersion": { - "description": "Version of bootstrap stack required to deploy this stack (Default - Version 1 (basic modern bootstrap stack))", - "type": "number" - }, - "bootstrapStackVersionSsmParameter": { - "description": "SSM parameter where the bootstrap stack version number can be found\n\n- If this value is not set, the bootstrap stack name must be known at\n deployment time so the stack version can be looked up from the stack\n outputs.\n- If this value is set, the bootstrap stack can have any name because\n we won't need to look it up. (Default - Bootstrap stack version number looked up)", - "type": "string" - } - }, - "required": [ - "file" - ] - }, - "TreeArtifactProperties": { - "description": "Artifact properties for the Construct Tree Artifact", - "type": "object", - "properties": { - "file": { - "description": "Filename of the tree artifact", - "type": "string" - } - }, - "required": [ - "file" - ] - }, - "NestedCloudAssemblyProperties": { - "description": "Artifact properties for nested cloud assemblies", - "type": "object", - "properties": { - "directoryName": { - "description": "Relative path to the nested cloud assembly", - "type": "string" - }, - "displayName": { - "description": "Display name for the cloud assembly (Default - The artifact ID)", - "type": "string" - } - }, - "required": [ - "directoryName" - ] - }, - "MissingContext": { - "description": "Represents a missing piece of context.", - "type": "object", - "properties": { - "key": { - "description": "The missing context key.", - "type": "string" - }, - "provider": { - "$ref": "#/definitions/ContextProvider", - "description": "The provider from which we expect this context key to be obtained." - }, - "props": { - "$ref": "#/definitions/ContextQueryProperties", - "description": "A set of provider-specific options." - } - }, - "required": [ - "key", - "props", - "provider" - ] - }, - "ContextProvider": { - "description": "Identifier for the context provider", - "type": "string", - "enum": [ - "ami", - "availability-zones", - "hosted-zone", - "ssm", - "vpc-provider", - "endpoint-service-availability-zones", - "load-balancer", - "load-balancer-listener", - "security-group", - "key-provider", - "plugin" - ] - }, - "ContextQueryProperties": { - "anyOf": [ - { - "$ref": "#/definitions/AmiContextQuery" - }, - { - "$ref": "#/definitions/AvailabilityZonesContextQuery" - }, - { - "$ref": "#/definitions/HostedZoneContextQuery" - }, - { - "$ref": "#/definitions/SSMParameterContextQuery" - }, - { - "$ref": "#/definitions/VpcContextQuery" - }, - { - "$ref": "#/definitions/EndpointServiceAvailabilityZonesContextQuery" - }, - { - "$ref": "#/definitions/LoadBalancerContextQuery" - }, - { - "$ref": "#/definitions/LoadBalancerListenerContextQuery" - }, - { - "$ref": "#/definitions/SecurityGroupContextQuery" - }, - { - "$ref": "#/definitions/KeyContextQuery" - }, - { - "$ref": "#/definitions/PluginContextQuery" - } - ] - }, - "AmiContextQuery": { - "description": "Query to AMI context provider", - "type": "object", - "properties": { - "account": { - "description": "Account to query", - "type": "string" - }, - "region": { - "description": "Region to query", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "owners": { - "description": "Owners to DescribeImages call (Default - All owners)", - "type": "array", - "items": { - "type": "string" - } - }, - "filters": { - "description": "Filters to DescribeImages call", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "required": [ - "account", - "filters", - "region" - ] - }, - "AvailabilityZonesContextQuery": { - "description": "Query to availability zone context provider", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - } - }, - "required": [ - "account", - "region" - ] - }, - "HostedZoneContextQuery": { - "description": "Query to hosted zone context provider", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "domainName": { - "description": "The domain name e.g. example.com to lookup", - "type": "string" - }, - "privateZone": { - "description": "True if the zone you want to find is a private hosted zone", - "default": false, - "type": "boolean" - }, - "vpcId": { - "description": "The VPC ID to that the private zone must be associated with\n\nIf you provide VPC ID and privateZone is false, this will return no results\nand raise an error. (Default - Required if privateZone=true)", - "type": "string" - } - }, - "required": [ - "account", - "domainName", - "region" - ] - }, - "SSMParameterContextQuery": { - "description": "Query to SSM Parameter Context Provider", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "parameterName": { - "description": "Parameter name to query", - "type": "string" - } - }, - "required": [ - "account", - "parameterName", - "region" - ] - }, - "VpcContextQuery": { - "description": "Query input for looking up a VPC", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "filter": { - "description": "Filters to apply to the VPC\n\nFilter parameters are the same as passed to DescribeVpcs.", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "returnAsymmetricSubnets": { - "description": "Whether to populate the subnetGroups field of the `VpcContextResponse`,\nwhich contains potentially asymmetric subnet groups.", - "default": false, - "type": "boolean" - }, - "subnetGroupNameTag": { - "description": "Optional tag for subnet group name.\nIf not provided, we'll look at the aws-cdk:subnet-name tag.\nIf the subnet does not have the specified tag,\nwe'll use its type as the name. (Default 'aws-cdk:subnet-name')", - "type": "string" - }, - "returnVpnGateways": { - "description": "Whether to populate the `vpnGatewayId` field of the `VpcContextResponse`,\nwhich contains the VPN Gateway ID, if one exists. You can explicitly\ndisable this in order to avoid the lookup if you know the VPC does not have\na VPN Gatway attached. (Default true)", - "type": "boolean" - } - }, - "required": [ - "account", - "filter", - "region" - ] - }, - "EndpointServiceAvailabilityZonesContextQuery": { - "description": "Query to endpoint service context provider", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "serviceName": { - "description": "Query service name", - "type": "string" - } - }, - "required": [ - "account", - "region", - "serviceName" - ] - }, - "LoadBalancerContextQuery": { - "description": "Query input for looking up a load balancer", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "loadBalancerType": { - "$ref": "#/definitions/LoadBalancerType", - "description": "Filter load balancers by their type" - }, - "loadBalancerArn": { - "description": "Find by load balancer's ARN (Default - does not search by load balancer arn)", - "type": "string" - }, - "loadBalancerTags": { - "description": "Match load balancer tags (Default - does not match load balancers by tags)", - "type": "array", - "items": { - "$ref": "#/definitions/Tag" - } - } - }, - "required": [ - "account", - "loadBalancerType", - "region" - ] - }, - "LoadBalancerType": { - "description": "Type of load balancer", - "type": "string", - "enum": [ - "network", - "application" - ] - }, - "LoadBalancerListenerContextQuery": { - "description": "Query input for looking up a load balancer listener", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "listenerArn": { - "description": "Find by listener's arn (Default - does not find by listener arn)", - "type": "string" - }, - "listenerProtocol": { - "description": "Filter by listener protocol (Default - does not filter by listener protocol)", - "enum": [ - "HTTP", - "HTTPS", - "TCP", - "TCP_UDP", - "TLS", - "UDP" - ], - "type": "string" - }, - "listenerPort": { - "description": "Filter listeners by listener port (Default - does not filter by a listener port)", - "type": "number" - }, - "loadBalancerType": { - "$ref": "#/definitions/LoadBalancerType", - "description": "Filter load balancers by their type" - }, - "loadBalancerArn": { - "description": "Find by load balancer's ARN (Default - does not search by load balancer arn)", - "type": "string" - }, - "loadBalancerTags": { - "description": "Match load balancer tags (Default - does not match load balancers by tags)", - "type": "array", - "items": { - "$ref": "#/definitions/Tag" - } - } - }, - "required": [ - "account", - "loadBalancerType", - "region" - ] - }, - "SecurityGroupContextQuery": { - "description": "Query input for looking up a security group", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "securityGroupId": { - "description": "Security group id (Default - None)", - "type": "string" - }, - "securityGroupName": { - "description": "Security group name (Default - None)", - "type": "string" - }, - "vpcId": { - "description": "VPC ID (Default - None)", - "type": "string" - } - }, - "required": [ - "account", - "region" - ] - }, - "KeyContextQuery": { - "description": "Query input for looking up a KMS Key", - "type": "object", - "properties": { - "account": { - "description": "Query account", - "type": "string" - }, - "region": { - "description": "Query region", - "type": "string" - }, - "lookupRoleArn": { - "description": "The ARN of the role that should be used to look up the missing values (Default - None)", - "type": "string" - }, - "aliasName": { - "description": "Alias name used to search the Key", - "type": "string" - } - }, - "required": [ - "account", - "aliasName", - "region" - ] - }, - "PluginContextQuery": { - "description": "Query input for plugins\n\nThis alternate branch is necessary because it needs to be able to escape all type checking\nwe do on on the cloud assembly -- we cannot know the properties that will be used a priori.", - "type": "object", - "additionalProperties": {}, - "properties": { - "pluginName": { - "description": "The name of the plugin", - "type": "string" - } - }, - "required": [ - "pluginName" - ] - }, - "RuntimeInfo": { - "description": "Information about the application's runtime components.", - "type": "object", - "properties": { - "libraries": { - "description": "The list of libraries loaded in the application, associated with their versions.", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "libraries" - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.version.json deleted file mode 100644 index 1f0068d32659a..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/cloud-assembly.version.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/schema/integ.schema.json b/packages/aws-cdk-lib/cloud-assembly-schema/schema/integ.schema.json deleted file mode 100644 index a43e4f30b6f64..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/schema/integ.schema.json +++ /dev/null @@ -1,493 +0,0 @@ -{ - "$ref": "#/definitions/IntegManifest", - "definitions": { - "IntegManifest": { - "description": "Definitions for the integration testing manifest", - "type": "object", - "properties": { - "version": { - "description": "Version of the manifest", - "type": "string" - }, - "enableLookups": { - "description": "Enable lookups for this test. If lookups are enabled\nthen `stackUpdateWorkflow` must be set to false.\nLookups should only be enabled when you are explicitely testing\nlookups.", - "default": false, - "type": "boolean" - }, - "synthContext": { - "description": "Additional context to use when performing\na synth. Any context provided here will override\nany default context (Default - no additional context)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "testCases": { - "description": "test cases", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TestCase" - } - } - }, - "required": [ - "testCases", - "version" - ] - }, - "TestCase": { - "description": "Represents an integration test case", - "type": "object", - "properties": { - "stacks": { - "description": "Stacks that should be tested as part of this test case\nThe stackNames will be passed as args to the cdk commands\nso dependent stacks will be automatically deployed unless\n`exclusively` is passed", - "type": "array", - "items": { - "type": "string" - } - }, - "assertionStack": { - "description": "The node id of the stack that contains assertions.\nThis is the value that can be used to deploy the stack with the CDK CLI (Default - no assertion stack)", - "type": "string" - }, - "assertionStackName": { - "description": "The name of the stack that contains assertions (Default - no assertion stack)", - "type": "string" - }, - "stackUpdateWorkflow": { - "description": "Run update workflow on this test case\nThis should only be set to false to test scenarios\nthat are not possible to test as part of the update workflow (Default true)", - "type": "boolean" - }, - "cdkCommandOptions": { - "description": "Additional options to use for each CDK command (Default - runner default options)", - "$ref": "#/definitions/CdkCommands" - }, - "hooks": { - "description": "Additional commands to run at predefined points in the test workflow\n\ne.g. { postDeploy: ['yarn', 'test'] } (Default - no hooks)", - "$ref": "#/definitions/Hooks" - }, - "diffAssets": { - "description": "Whether or not to include asset hashes in the diff\nAsset hashes can introduces a lot of unneccessary noise into tests,\nbut there are some cases where asset hashes _should_ be included. For example\nany tests involving custom resources or bundling", - "default": false, - "type": "boolean" - }, - "allowDestroy": { - "description": "List of CloudFormation resource types in this stack that can\nbe destroyed as part of an update without failing the test.\n\nThis list should only include resources that for this specific\nintegration test we are sure will not cause errors or an outage if\ndestroyed. For example, maybe we know that a new resource will be created\nfirst before the old resource is destroyed which prevents any outage.\n\ne.g. ['AWS::IAM::Role'] (Default - do not allow destruction of any resources on update)", - "type": "array", - "items": { - "type": "string" - } - }, - "regions": { - "description": "Limit deployment to these regions (Default - can run in any region)", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "stacks" - ] - }, - "CdkCommands": { - "description": "Options for specific cdk commands that are run\nas part of the integration test workflow", - "type": "object", - "properties": { - "deploy": { - "description": "Options to for the cdk deploy command (Default - default deploy options)", - "$ref": "#/definitions/DeployCommand" - }, - "destroy": { - "description": "Options to for the cdk destroy command (Default - default destroy options)", - "$ref": "#/definitions/DestroyCommand" - } - } - }, - "DeployCommand": { - "description": "Represents a cdk deploy command", - "type": "object", - "properties": { - "args": { - "description": "Additional arguments to pass to the command\nThis can be used to test specific CLI functionality (Default - only default args are used)", - "$ref": "#/definitions/DeployOptions" - }, - "enabled": { - "description": "Whether or not to run this command as part of the workflow\nThis can be used if you only want to test some of the workflow\nfor example enable `synth` and disable `deploy` & `destroy` in order\nto limit the test to synthesis (Default true)", - "type": "boolean" - }, - "expectError": { - "description": "If the runner should expect this command to fail", - "default": false, - "type": "boolean" - }, - "expectedMessage": { - "description": "This can be used in combination with `expectedError`\nto validate that a specific message is returned. (Default - do not validate message)", - "type": "string" - } - } - }, - "DeployOptions": { - "description": "Options to use with cdk deploy", - "type": "object", - "properties": { - "exclusively": { - "description": "Only perform action on the given stack", - "default": false, - "type": "boolean" - }, - "toolkitStackName": { - "description": "Name of the toolkit stack to use/deploy (Default CDKToolkit)", - "type": "string" - }, - "reuseAssets": { - "description": "Reuse the assets with the given asset IDs (Default - do not reuse assets)", - "type": "array", - "items": { - "type": "string" - } - }, - "changeSetName": { - "description": "Optional name to use for the CloudFormation change set.\nIf not provided, a name will be generated automatically. (Default - auto generate a name)", - "type": "string" - }, - "force": { - "description": "Always deploy, even if templates are identical.", - "default": false, - "type": "boolean" - }, - "rollback": { - "description": "Rollback failed deployments (Default true)", - "type": "boolean" - }, - "notificationArns": { - "description": "ARNs of SNS topics that CloudFormation will notify with stack related events (Default - no notifications)", - "type": "array", - "items": { - "type": "string" - } - }, - "requireApproval": { - "description": "What kind of security changes require approval (Default RequireApproval.Never)", - "enum": [ - "any-change", - "broadening", - "never" - ], - "type": "string" - }, - "execute": { - "description": "Whether to execute the ChangeSet\nNot providing `execute` parameter will result in execution of ChangeSet (Default true)", - "type": "boolean" - }, - "parameters": { - "description": "Additional parameters for CloudFormation at deploy time (Default [object Object])", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "usePreviousParameters": { - "description": "Use previous values for unspecified parameters\n\nIf not set, all parameters must be specified for every deployment. (Default true)", - "type": "boolean" - }, - "outputsFile": { - "description": "Path to file where stack outputs will be written after a successful deploy as JSON (Default - Outputs are not written to any file)", - "type": "string" - }, - "ci": { - "description": "Whether we are on a CI system", - "default": false, - "type": "boolean" - }, - "concurrency": { - "description": "Deploy multiple stacks in parallel (Default 1)", - "type": "number" - }, - "stacks": { - "description": "List of stacks to deploy\n\nRequried if `all` is not set (Default - [])", - "type": "array", - "items": { - "type": "string" - } - }, - "all": { - "description": "Deploy all stacks\n\nRequried if `stacks` is not set (Default - false)", - "type": "boolean" - }, - "app": { - "description": "command-line for executing your app or a cloud assembly directory\ne.g. \"node bin/my-app.js\"\nor\n\"cdk.out\" (Default - read from cdk.json)", - "type": "string" - }, - "roleArn": { - "description": "Role to pass to CloudFormation for deployment (Default - use the bootstrap cfn-exec role)", - "type": "string" - }, - "context": { - "description": "Additional context (Default - no additional context)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "trace": { - "description": "Print trace for stack warnings", - "default": false, - "type": "boolean" - }, - "strict": { - "description": "Do not construct stacks with warnings", - "default": false, - "type": "boolean" - }, - "lookups": { - "description": "Perform context lookups.\n\nSynthesis fails if this is disabled and context lookups need\nto be performed (Default true)", - "type": "boolean" - }, - "ignoreErrors": { - "description": "Ignores synthesis errors, which will likely produce an invalid output", - "default": false, - "type": "boolean" - }, - "json": { - "description": "Use JSON output instead of YAML when templates are printed\nto STDOUT", - "default": false, - "type": "boolean" - }, - "verbose": { - "description": "show debug logs", - "default": false, - "type": "boolean" - }, - "debug": { - "description": "enable emission of additional debugging information, such as creation stack\ntraces of tokens", - "default": false, - "type": "boolean" - }, - "profile": { - "description": "Use the indicated AWS profile as the default environment (Default - no profile is used)", - "type": "string" - }, - "proxy": { - "description": "Use the indicated proxy. Will read from\nHTTPS_PROXY environment if specified (Default - no proxy)", - "type": "string" - }, - "caBundlePath": { - "description": "Path to CA certificate to use when validating HTTPS\nrequests. (Default - read from AWS_CA_BUNDLE environment variable)", - "type": "string" - }, - "ec2Creds": { - "description": "Force trying to fetch EC2 instance credentials (Default - guess EC2 instance status)", - "type": "boolean" - }, - "versionReporting": { - "description": "Include \"AWS::CDK::Metadata\" resource in synthesized templates (Default true)", - "type": "boolean" - }, - "pathMetadata": { - "description": "Include \"aws:cdk:path\" CloudFormation metadata for each resource (Default true)", - "type": "boolean" - }, - "assetMetadata": { - "description": "Include \"aws:asset:*\" CloudFormation metadata for resources that use assets (Default true)", - "type": "boolean" - }, - "staging": { - "description": "Copy assets to the output directory\n\nNeeded for local debugging the source files with SAM CLI", - "default": false, - "type": "boolean" - }, - "output": { - "description": "Emits the synthesized cloud assembly into a directory (Default cdk.out)", - "type": "string" - }, - "notices": { - "description": "Show relevant notices (Default true)", - "type": "boolean" - }, - "color": { - "description": "Show colors and other style from console output (Default true)", - "type": "boolean" - } - } - }, - "DestroyCommand": { - "description": "Represents a cdk destroy command", - "type": "object", - "properties": { - "args": { - "description": "Additional arguments to pass to the command\nThis can be used to test specific CLI functionality (Default - only default args are used)", - "$ref": "#/definitions/DestroyOptions" - }, - "enabled": { - "description": "Whether or not to run this command as part of the workflow\nThis can be used if you only want to test some of the workflow\nfor example enable `synth` and disable `deploy` & `destroy` in order\nto limit the test to synthesis (Default true)", - "type": "boolean" - }, - "expectError": { - "description": "If the runner should expect this command to fail", - "default": false, - "type": "boolean" - }, - "expectedMessage": { - "description": "This can be used in combination with `expectedError`\nto validate that a specific message is returned. (Default - do not validate message)", - "type": "string" - } - } - }, - "DestroyOptions": { - "description": "Options to use with cdk destroy", - "type": "object", - "properties": { - "force": { - "description": "Do not ask for permission before destroying stacks", - "default": false, - "type": "boolean" - }, - "exclusively": { - "description": "Only destroy the given stack", - "default": false, - "type": "boolean" - }, - "stacks": { - "description": "List of stacks to deploy\n\nRequried if `all` is not set (Default - [])", - "type": "array", - "items": { - "type": "string" - } - }, - "all": { - "description": "Deploy all stacks\n\nRequried if `stacks` is not set (Default - false)", - "type": "boolean" - }, - "app": { - "description": "command-line for executing your app or a cloud assembly directory\ne.g. \"node bin/my-app.js\"\nor\n\"cdk.out\" (Default - read from cdk.json)", - "type": "string" - }, - "roleArn": { - "description": "Role to pass to CloudFormation for deployment (Default - use the bootstrap cfn-exec role)", - "type": "string" - }, - "context": { - "description": "Additional context (Default - no additional context)", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "trace": { - "description": "Print trace for stack warnings", - "default": false, - "type": "boolean" - }, - "strict": { - "description": "Do not construct stacks with warnings", - "default": false, - "type": "boolean" - }, - "lookups": { - "description": "Perform context lookups.\n\nSynthesis fails if this is disabled and context lookups need\nto be performed (Default true)", - "type": "boolean" - }, - "ignoreErrors": { - "description": "Ignores synthesis errors, which will likely produce an invalid output", - "default": false, - "type": "boolean" - }, - "json": { - "description": "Use JSON output instead of YAML when templates are printed\nto STDOUT", - "default": false, - "type": "boolean" - }, - "verbose": { - "description": "show debug logs", - "default": false, - "type": "boolean" - }, - "debug": { - "description": "enable emission of additional debugging information, such as creation stack\ntraces of tokens", - "default": false, - "type": "boolean" - }, - "profile": { - "description": "Use the indicated AWS profile as the default environment (Default - no profile is used)", - "type": "string" - }, - "proxy": { - "description": "Use the indicated proxy. Will read from\nHTTPS_PROXY environment if specified (Default - no proxy)", - "type": "string" - }, - "caBundlePath": { - "description": "Path to CA certificate to use when validating HTTPS\nrequests. (Default - read from AWS_CA_BUNDLE environment variable)", - "type": "string" - }, - "ec2Creds": { - "description": "Force trying to fetch EC2 instance credentials (Default - guess EC2 instance status)", - "type": "boolean" - }, - "versionReporting": { - "description": "Include \"AWS::CDK::Metadata\" resource in synthesized templates (Default true)", - "type": "boolean" - }, - "pathMetadata": { - "description": "Include \"aws:cdk:path\" CloudFormation metadata for each resource (Default true)", - "type": "boolean" - }, - "assetMetadata": { - "description": "Include \"aws:asset:*\" CloudFormation metadata for resources that use assets (Default true)", - "type": "boolean" - }, - "staging": { - "description": "Copy assets to the output directory\n\nNeeded for local debugging the source files with SAM CLI", - "default": false, - "type": "boolean" - }, - "output": { - "description": "Emits the synthesized cloud assembly into a directory (Default cdk.out)", - "type": "string" - }, - "notices": { - "description": "Show relevant notices (Default true)", - "type": "boolean" - }, - "color": { - "description": "Show colors and other style from console output (Default true)", - "type": "boolean" - } - } - }, - "Hooks": { - "description": "Commands to run at predefined points during the\nintegration test workflow", - "type": "object", - "properties": { - "preDeploy": { - "description": "Commands to run prior to deploying the cdk stacks\nin the integration test (Default - no commands)", - "type": "array", - "items": { - "type": "string" - } - }, - "postDeploy": { - "description": "Commands to run prior after deploying the cdk stacks\nin the integration test (Default - no commands)", - "type": "array", - "items": { - "type": "string" - } - }, - "preDestroy": { - "description": "Commands to run prior to destroying the cdk stacks\nin the integration test (Default - no commands)", - "type": "array", - "items": { - "type": "string" - } - }, - "postDestroy": { - "description": "Commands to run after destroying the cdk stacks\nin the integration test (Default - no commands)", - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.sh b/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.sh deleted file mode 100755 index 04a6710e54bdb..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -set -euo pipefail -scriptsdir=$(cd $(dirname $0) && pwd) -packagedir=$(cd ${scriptsdir}/.. && pwd) - -# Output -OUTPUT_DIR="${packagedir}/schema" -mkdir -p ${OUTPUT_DIR} - -# regenerate JSON schema and bumps the version -ts-node --prefer-ts-exts -e "require('${packagedir}/scripts/update-schema.ts').update()" diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.ts b/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.ts deleted file mode 100644 index 532a0774612f3..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/scripts/update-schema.ts +++ /dev/null @@ -1,151 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as semver from 'semver'; -// eslint-disable-next-line import/no-extraneous-dependencies -import * as tjs from 'typescript-json-schema'; - -function log(message: string) { - // eslint-disable-next-line no-console - console.log(message); -} - -/** - * Where schemas are committed. - */ -const SCHEMA_DIR = path.resolve(__dirname, '../schema'); - -const SCHEMA_DEFINITIONS: { - [schemaName: string]: { - /** - * The name of the root type. - */ - rootTypeName: string; - /** - * Files loaded to generate the schema. - * Should be relative to `cloud-assembly-schema/lib`. - * Usually this is just the file containing the root type. - */ - files: string[]; - }; -} = { - 'assets': { - rootTypeName: 'AssetManifest', - files: [path.join('assets', 'schema.ts')], - }, - 'cloud-assembly': { - rootTypeName: 'AssemblyManifest', - files: [path.join('cloud-assembly', 'schema.ts')], - }, - 'integ': { - rootTypeName: 'IntegManifest', - files: [path.join('integ-tests', 'schema.ts')], - }, -}; - -export const SCHEMAS = Object.keys(SCHEMA_DEFINITIONS); - -export function update() { - for (const s of SCHEMAS) { - generateSchema(s); - } - - bump(); -} - -export function bump() { - const versionFile = path.join(SCHEMA_DIR, 'cloud-assembly.version.json'); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const metadata = require(versionFile); - - const oldVersion = metadata.version; - const newVersion = semver.inc(oldVersion, 'major'); - - log(`Updating schema version: ${oldVersion} -> ${newVersion}`); - fs.writeFileSync(versionFile, JSON.stringify({ version: newVersion })); -} - -/** - * Generates a schema from typescript types. - * @returns JSON schema - * @param schemaName the schema to generate - * @param shouldBump writes a new version of the schema and bumps the major version - */ -export function generateSchema(schemaName: string, saveToFile: boolean = true) { - const spec = SCHEMA_DEFINITIONS[schemaName]; - const out = saveToFile ? path.join(SCHEMA_DIR, `${schemaName}.schema.json`) : ''; - - const settings: Partial = { - required: true, - ref: true, - topRef: true, - noExtraProps: false, - out, - }; - - const compilerOptions = { - strictNullChecks: true, - }; - - const program = tjs.getProgramFromFiles(spec.files.map(file =>path.join(__dirname, '..', 'lib', file)), compilerOptions); - const schema = tjs.generateSchema(program, spec.rootTypeName, settings); - - augmentDescription(schema); - addAnyMetadataEntry(schema); - - if (out) { - log(`Generating schema to ${out}`); - fs.writeFileSync(out, JSON.stringify(schema, null, 4)); - } - - return schema; -} - -/** - * Remove 'default' from the schema since its generated - * from the tsdocs, which are not necessarily actual values, - * but rather descriptive behavior. - * - * To keep this inforamtion in the schema, we append it to the - * 'description' of the property. - */ -function augmentDescription(schema: any) { - - function _recurse(o: any) { - for (const prop in o) { - - if (prop === 'description' && typeof o[prop] === 'string') { - - const description = o[prop]; - const defaultValue = o.default; - - if (!defaultValue) { - // property doesn't have a default value - // skip - continue; - } - - const descriptionWithDefault = `${description} (Default ${defaultValue})`; - - delete o.default; - o[prop] = descriptionWithDefault; - - } else if (typeof o[prop] === 'object') { - _recurse(o[prop]); - } - } - } - - _recurse(schema); - -} - -/** - * Patch the properties of MetadataEntry to allow - * specifying any free form data. This is needed since source - * code doesn't allow this in order to enforce stricter jsii - * compatibility checks. - */ -function addAnyMetadataEntry(schema: any) { - schema?.definitions?.MetadataEntry?.properties.data.anyOf.push({ description: 'Free form data.' }); -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/__snapshots__/manifest.test.ts.snap b/packages/aws-cdk-lib/cloud-assembly-schema/test/__snapshots__/manifest.test.ts.snap deleted file mode 100644 index 14a8ff8031165..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/__snapshots__/manifest.test.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`manifest load 1`] = ` -{ - "version": "0.0.0", -} -`; diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/assets.test.ts b/packages/aws-cdk-lib/cloud-assembly-schema/test/assets.test.ts deleted file mode 100644 index 24ddd465484b7..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/assets.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { FileAssetPackaging, Manifest } from '../lib'; - -describe('Docker image asset', () => { - test('valid input', () => { - expect(() => { - validate({ - version: Manifest.version(), - dockerImages: { - asset: { - source: { - directory: '.', - }, - destinations: { - dest: { - region: 'us-north-20', - repositoryName: 'REPO', - imageTag: 'TAG', - }, - }, - }, - externalAsset: { - source: { - executable: ['sometool'], - }, - destinations: { - dest: { - region: 'us-north-20', - repositoryName: 'REPO', - imageTag: 'TAG', - }, - }, - }, - }, - }); - }).not.toThrow(); - }); - - test('invalid input', () => { - expect(() => { - validate({ - version: Manifest.version(), - dockerImages: { - asset: { - source: { - directory: true, - }, - destinations: {}, - }, - externalAsset: { - source: {}, - destinations: {}, - }, - }, - }); - }).toThrow(/instance\.dockerImages\.asset\.source\.directory is not of a type\(s\) string/); - }); -}); - -describe('File asset', () => { - describe('valid input', () => { - test('without packaging', () => { - expect(() => { - validate({ - version: Manifest.version(), - files: { - asset: { - source: { - path: 'a/b/c', - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - externalAsset: { - source: { - executable: ['sometool'], - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - }, - }); - }).not.toThrow(); - }); - - for (const packaging of Object.values(FileAssetPackaging)) { - test(`with "${packaging}" packaging`, () => { - expect(() => { - validate({ - version: Manifest.version(), - files: { - asset: { - source: { - path: 'a/b/c', - packaging, - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - }, - }); - }).not.toThrow(); - }); - } - }); - - describe('invalid input', () => { - test('bad "source.path" property', () => { - expect(() => { - validate({ - version: Manifest.version(), - files: { - asset: { - source: { - path: 3, - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - externalAsset: { - source: { - executable: ['sometool'], - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - }, - }); - }).toThrow(/instance\.files\.asset\.source\.path is not of a type\(s\) string/); - }); - - test('bad "source.packaging" property', () => { - expect(() => { - validate({ - version: Manifest.version(), - files: { - asset: { - source: { - path: 'a/b/c', - packaging: 'BLACK_HOLE', - }, - destinations: { - dest: { - region: 'us-north-20', - bucketName: 'Bouquet', - objectKey: 'key', - }, - }, - }, - }, - }); - }).toThrow(/instance\.files\.asset\.source\.packaging is not one of enum values: file,zip/); - }); - }); -}); - -function validate(manifest: any) { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'assets.test.')); - const filePath = path.join(dir, 'manifest.json'); - fs.writeFileSync(filePath, JSON.stringify(manifest, undefined, 2)); - try { - Manifest.loadAssetManifest(filePath); - } finally { - fs.unlinkSync(filePath); - fs.rmdirSync(dir); - } -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/high-version/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/high-version/manifest.json deleted file mode 100644 index ef6fc1c901429..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/high-version/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "99.99.99" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-artifact-type/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-artifact-type/manifest.json deleted file mode 100644 index ea1558cb1e6f9..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-artifact-type/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": "0.0.0", - "artifacts": { - "MyArt": { - "type": "who:am:i", - "environment": "aws://37736633/us-region-1" - } - } -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-nested-property/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-nested-property/manifest.json deleted file mode 100644 index da1a33b17936e..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-nested-property/manifest.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "0.0.0", - "runtime": { - "libraries": ["should", "be", "a", "map"] - } -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-version/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-version/manifest.json deleted file mode 100644 index 36b2250cf8d33..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/invalid-version/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "version" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/only-version/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/only-version/manifest.json deleted file mode 100644 index c158d5be87422..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/only-version/manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "0.0.0" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/random-metadata/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/random-metadata/manifest.json deleted file mode 100644 index cd2209c526595..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/random-metadata/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "version": "0.0.0", - "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, - "stack": { - "type": "aws:cloudformation:stack", - "metadata": { - "AwsCdkPlaygroundBatch": [ - { - "type": "random-array", - "data": ["42"], - "trace": ["trace"] - }, - { - "type": "random-number", - "data": 42, - "trace": ["trace"] - }, - { - "type": "random-map", - "data": { - "key": "value" - }, - "trace": ["trace"] - } - ] - } - } - } - } \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/unknown-property/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/unknown-property/manifest.json deleted file mode 100644 index d1f0bca305c81..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/unknown-property/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "version": "0.0.0", - "who-am-i": "unknown" -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/with-stack-tags/manifest.json b/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/with-stack-tags/manifest.json deleted file mode 100644 index 4d18eed47c8a0..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/fixtures/with-stack-tags/manifest.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "0.0.0", - "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, - "stack": { - "type": "aws:cloudformation:stack", - "metadata": { - "AwsCdkPlaygroundBatch": [ - { - "type": "aws:cdk:stack-tags", - "data": [{ - "Key": "hello", - "Value": "world" - }], - "trace": ["trace"] - }, - { - "type": "aws:cdk:asset", - "data": { - "repositoryName": "repo", - "imageTag": "tag", - "id": "id", - "packaging": "container-image", - "path": "path", - "sourceHash": "hash" - }, - "trace": ["trace"] - } - ] - } - } - } -} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/integ-tests.test.ts b/packages/aws-cdk-lib/cloud-assembly-schema/test/integ-tests.test.ts deleted file mode 100644 index 3baefc89d750f..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/integ-tests.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { Manifest } from '../lib'; - -describe('Integration test', () => { - test('valid input', () => { - expect(() => { - validate({ - version: Manifest.version(), - testCases: { - testCase1: { - stacks: ['stack1', 'stack2'], - stackUpdateWorkflow: true, - cdkCommandOptions: { - deploy: { - enabled: true, - expectError: false, - expectedMessage: 'some message', - args: { - exclusively: true, - toolkitStackName: 'Stack', - reuseAssets: ['asset1', 'asset2'], - changeSetName: 'changeset', - force: true, - rollback: false, - notificationArns: ['arn1', 'arn2'], - execute: true, - parameters: { - 'MYPARAM': 'Value', - 'Stack1:OtherParam': 'OtherValue', - }, - usePreviousParameters: true, - outputsFile: 'outputs.json', - ci: true, - requireApproval: 'never', - app: 'node bin/my-app.js', - roleArn: 'roleArn', - context: { - KEY: 'value', - }, - trace: true, - strict: true, - lookups: true, - ignoreErrors: true, - json: true, - verbose: true, - debug: true, - profile: 'profile', - proxy: 'https://proxy', - caBundlePath: 'path/to/bundle', - ec2Creds: true, - versionReporting: false, - pathMetadata: false, - assetMetadata: true, - staging: false, - output: true, - notices: true, - color: false, - }, - }, - synth: { - enabled: true, - expectError: false, - expectedMessage: 'some message', - args: { - quiet: true, - exclusively: true, - validation: true, - }, - }, - destroy: { - enabled: true, - expectError: false, - expectedMessage: 'some message', - args: { - force: true, - exclusively: true, - }, - }, - }, - hooks: { - preDeploy: ['yarn test'], - postDeploy: ['some other command'], - preDestroy: ['command1', 'command2'], - postDestroy: ['command3', 'command4'], - }, - diffAssets: true, - allowDestroy: ['AWS::IAM::Role'], - region: ['us-east-1', 'us-east-2'], - }, - }, - }); - }); - }); - - test('invalid input', () => { - expect(() => { - validate({ - version: Manifest.version(), - testCases: { - stacks: true, - }, - }); - }).toThrow(/instance\.testCases\.stacks is not of a type\(s\) object/); - }); - - test('without command options', () => { - expect(() => { - validate({ - version: Manifest.version(), - testCases: { - testCase1: { - stacks: ['stack1', 'stack2'], - stackUpdateWorkflow: true, - hooks: { - preDeploy: ['yarn test'], - }, - diffAssets: true, - }, - }, - }); - }); - }); -}); - -function validate(manifest: any) { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'integ.test.')); - const filePath = path.join(dir, 'manifest.json'); - fs.writeFileSync(filePath, JSON.stringify(manifest, undefined, 2)); - try { - Manifest.loadIntegManifest(filePath); - } finally { - fs.unlinkSync(filePath); - fs.rmdirSync(dir); - } -} diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/manifest.test.ts b/packages/aws-cdk-lib/cloud-assembly-schema/test/manifest.test.ts deleted file mode 100644 index a8f1311a2dcc9..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/manifest.test.ts +++ /dev/null @@ -1,162 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import * as semver from 'semver'; -import { AssemblyManifest, Manifest, StackTagsMetadataEntry } from '../lib'; - -const FIXTURES = path.join(__dirname, 'fixtures'); - -function fixture(name: string) { - return path.join(FIXTURES, name, 'manifest.json'); -} - -test('manifest save', () => { - - const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-tests')); - const manifestFile = path.join(outdir, 'manifest.json'); - - const assemblyManifest: AssemblyManifest = { - version: 'version', - runtime: { - libraries: { lib1: '1.2.3' }, - }, - }; - - Manifest.saveAssemblyManifest(assemblyManifest, manifestFile); - - const saved = JSON.parse(fs.readFileSync(manifestFile, { encoding: 'utf-8' })); - - expect(saved).toEqual({ - ...assemblyManifest, - version: Manifest.version(), // version is forced - }); -}); - -test('manifest load', () => { - const loaded = Manifest.loadAssemblyManifest(fixture('only-version')); - expect(loaded).toMatchSnapshot(); -}); - -test('manifest load fails for invalid nested property', () => { - expect(() => Manifest.loadAssemblyManifest(fixture('invalid-nested-property'))).toThrow(/Invalid assembly manifest/); -}); - -test('manifest load fails for invalid artifact type', () => { - expect(() => Manifest.loadAssemblyManifest(fixture('invalid-artifact-type'))).toThrow(/Invalid assembly manifest/); -}); - -test('manifest load fails on higher major version', () => { - expect(() => Manifest.loadAssemblyManifest(fixture('high-version'))).toThrow(/Cloud assembly schema version mismatch/); -}); - -// once we start introducing minor version bumps that are considered -// non breaking, this test can be removed. -test('manifest load fails on higher minor version', () => { - - const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-tests')); - const manifestFile = path.join(outdir, 'manifest.json'); - - const newVersion = semver.inc(Manifest.version(), 'minor'); - expect(newVersion).toBeTruthy(); - - if (newVersion) { - const assemblyManifest: AssemblyManifest = { - version: newVersion, - }; - - // can't use saveAssemblyManifest because it will force the correct version - fs.writeFileSync(manifestFile, JSON.stringify(assemblyManifest)); - - expect(() => Manifest.loadAssemblyManifest(manifestFile)).toThrow(/Cloud assembly schema version mismatch/); - } -}); - -test('manifest load doesnt fail if version checking is disabled, and unknown properties are added', () => { - const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-tests')); - const manifestFile = path.join(outdir, 'manifest.json'); - const newVersion = semver.inc(Manifest.version(), 'major'); - expect(newVersion).toBeTruthy(); - - const assemblyManifest: AssemblyManifest = { - version: newVersion!, - artifacts: { - SomeArtifact: { - type: 'aws:cloudformation:stack', - thisPropertyWillNeverBeInTheManifest: 'i_hope', - } as any, - UnknownArtifact: { - type: 'unknown-artifact-type', - } as any, - }, - }; - - // can't use saveAssemblyManifest because it will force the correct version - fs.writeFileSync(manifestFile, JSON.stringify(assemblyManifest)); - - Manifest.loadAssemblyManifest(manifestFile, { skipVersionCheck: true, skipEnumCheck: true }); -}); - -// once we start introducing patch version bumps that are considered -// non breaking, this test can be removed. -test('manifest load fails on higher patch version', () => { - - const outdir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-tests')); - const manifestFile = path.join(outdir, 'manifest.json'); - - const newVersion = semver.inc(Manifest.version(), 'patch'); - expect(newVersion).toBeTruthy(); - - if (newVersion) { - const assemblyManifest: AssemblyManifest = { - version: newVersion, - }; - - // can't use saveAssemblyManifest because it will force the correct version - fs.writeFileSync(manifestFile, JSON.stringify(assemblyManifest)); - - expect(() => Manifest.loadAssemblyManifest(manifestFile)).toThrow(/Cloud assembly schema version mismatch/); - } -}); - -test('manifest load fails on invalid version', () => { - expect(() => Manifest.loadAssemblyManifest(fixture('invalid-version'))).toThrow(/Invalid semver string/); -}); - -test('manifest load succeeds on unknown properties', () => { - const manifest = Manifest.loadAssemblyManifest(fixture('unknown-property')); - expect(manifest.version).toEqual('0.0.0'); -}); - -test('stack-tags are deserialized properly', () => { - - const m: AssemblyManifest = Manifest.loadAssemblyManifest(fixture('with-stack-tags')); - - if (m.artifacts?.stack?.metadata?.AwsCdkPlaygroundBatch[0].data) { - const entry = m.artifacts.stack.metadata.AwsCdkPlaygroundBatch[0].data as StackTagsMetadataEntry; - expect(entry[0].key).toEqual('hello'); - expect(entry[0].value).toEqual('world'); - } - expect(m.version).toEqual('0.0.0'); - -}); - -test('can access random metadata', () => { - - const loaded = Manifest.loadAssemblyManifest(fixture('random-metadata')); - const randomArray = loaded.artifacts?.stack.metadata?.AwsCdkPlaygroundBatch[0].data; - const randomNumber = loaded.artifacts?.stack.metadata?.AwsCdkPlaygroundBatch[1].data; - const randomMap = loaded.artifacts?.stack.metadata?.AwsCdkPlaygroundBatch[2].data; - - expect(randomArray).toEqual(['42']); - expect(randomNumber).toEqual(42); - expect(randomMap).toEqual({ - key: 'value', - }); - - expect(randomMap).toBeTruthy(); - - if (randomMap) { - expect((randomMap as any).key).toEqual('value'); - } - -}); diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/test/schema.test.ts b/packages/aws-cdk-lib/cloud-assembly-schema/test/schema.test.ts deleted file mode 100644 index ad935bbe73789..0000000000000 --- a/packages/aws-cdk-lib/cloud-assembly-schema/test/schema.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { generateSchema, SCHEMAS } from '../scripts/update-schema'; - -test('if this test fails, run "yarn update-schema"', () => { - - // when we compare schemas we ignore changes the - // description that is generated from the ts docstrings. - const docStringFields = [ - 'description', - ]; - - for (const schemaName of SCHEMAS) { - const expected = removeStringKeys(generateSchema(schemaName, false), docStringFields); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const actual = removeStringKeys(require(`../schema/${schemaName}.schema.json`), docStringFields); - - try { - expect(actual).toEqual(expected); - } catch (err: any) { - // I couldn't for the life of me figure out how to provide additional error message - // to jest...any ideas? - err.message = `Whoops, Looks like the schema has changed. Did you forget to run 'yarn update-schema'?\n\n${err.message}`; - throw err; - } - } - -}); - -function removeStringKeys(obj: any, keys: string[]) { - - function _recurse(o: any) { - for (const prop in o) { - if (keys.includes(prop) && typeof o[prop] === 'string') { - delete o[prop]; - } else if (typeof o[prop] === 'object') { - _recurse(o[prop]); - } - } - } - const cloned = clone(obj); - _recurse(cloned); - return cloned; -} - -function clone(obj: any) { - return JSON.parse(JSON.stringify(obj)); -} diff --git a/packages/cdk-assets/.eslintrc.js b/packages/cdk-assets/.eslintrc.js deleted file mode 100644 index 2658ee8727166..0000000000000 --- a/packages/cdk-assets/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); -baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; -module.exports = baseConfig; diff --git a/packages/cdk-assets/.gitignore b/packages/cdk-assets/.gitignore deleted file mode 100644 index d24092a6feda2..0000000000000 --- a/packages/cdk-assets/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -*.js -*.js.map -*.d.ts -!lib/init-templates/**/javascript/**/* -node_modules -dist - -# Generated by generate.sh -build-info.json - -.LAST_BUILD -.nyc_output -coverage -nyc.config.js -.LAST_PACKAGE -*.snk - -!test/integ/run-wrappers/dist -!test/integ/cli/**/* -assets.json -npm-shrinkwrap.json -!.eslintrc.js -!jest.config.js - -junit.xml - -# Ignore this symlink, we recreate it at test time -test/test-archive-follow/data/linked diff --git a/packages/cdk-assets/.npmignore b/packages/cdk-assets/.npmignore deleted file mode 100644 index 45b8808bdd7ac..0000000000000 --- a/packages/cdk-assets/.npmignore +++ /dev/null @@ -1,30 +0,0 @@ -# Don't include original .ts files when doing `npm pack` -*.ts -!*.template.ts -!*.d.ts -coverage -.nyc_output -*.tgz - -dist -.LAST_PACKAGE -.LAST_BUILD -*.snk - -!lib/init-templates/*/*/tsconfig.json -!test/integ/cli/**/*.js -!test/integ/run-wrappers/dist - -*.tsbuildinfo - -tsconfig.json - -# init templates include default tsconfig.json files which we need -!lib/init-templates/**/tsconfig.json -.eslintrc.js -jest.config.js - -# exclude cdk artifacts -**/cdk.out -junit.xml -test/ \ No newline at end of file diff --git a/packages/cdk-assets/LICENSE b/packages/cdk-assets/LICENSE deleted file mode 100644 index dcf28b52a83af..0000000000000 --- a/packages/cdk-assets/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/cdk-assets/NOTICE b/packages/cdk-assets/NOTICE deleted file mode 100644 index c0b1f046c881a..0000000000000 --- a/packages/cdk-assets/NOTICE +++ /dev/null @@ -1,2 +0,0 @@ -AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/cdk-assets/README.md b/packages/cdk-assets/README.md deleted file mode 100644 index 7c8bc78aca51b..0000000000000 --- a/packages/cdk-assets/README.md +++ /dev/null @@ -1,190 +0,0 @@ -# cdk-assets - - ---- - -![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge) - ---- - - - - -A tool for publishing CDK assets to AWS environments. - -## Overview - -`cdk-assets` requires an asset manifest file called `assets.json`, in a CDK -CloudAssembly (`cdk.out/assets.json`). It will take the assets listed in the -manifest, prepare them as required and upload them to the locations indicated in -the manifest. - -Currently the following asset types are supported: - -* Files and archives, uploaded to S3 -* Docker Images, uploaded to ECR -* Files, archives, and Docker images built by external utilities - -S3 buckets and ECR repositories to upload to are expected to exist already. - -We expect assets to be immutable, and we expect that immutability to be -reflected both in the asset ID and in the destination location. This reflects -itself in the following behaviors: - -* If the indicated asset already exists in the given destination location, it - will not be packaged and uploaded. -* If some locally cached artifact (depending on the asset type a file or an - image in the local Docker cache) already exists named after the asset's ID, it - will not be packaged, but will be uploaded directly to the destination - location. - -For assets build by external utilities, the contract is such that cdk-assets -expects the utility to manage dedupe detection as well as path/image tag generation. -This means that cdk-assets will call the external utility every time generation -is warranted, and it is up to the utility to a) determine whether to do a -full rebuild; and b) to return only one thing on stdout: the path to the file/archive -asset, or the name of the local Docker image. - -## Usage - -The `cdk-asset` tool can be used programmatically and via the CLI. Use -programmatic access if you need more control over authentication than the -default [`aws-sdk`](https://github.com/aws/aws-sdk-js) implementation allows. - -Command-line use looks like this: - -```console -$ cdk-assets /path/to/cdk.out [ASSET:DEST] [ASSET] [:DEST] [...] -``` - -Credentials will be taken from the `AWS_ACCESS_KEY...` environment variables -or the `default` profile (or another profile if `AWS_PROFILE` is set). - -A subset of the assets and destinations can be uploaded by specifying their -asset IDs or destination IDs. - -## Manifest Example - -An asset manifest looks like this: - -```json -{ - "version": "1.22.0", - "files": { - "7aac5b80b050e7e4e168f84feffa5893": { - "source": { - "path": "some_directory", - "packaging": "zip" - }, - "destinations": { - "us-east-1": { - "region": "us-east-1", - "assumeRoleArn": "arn:aws:iam::12345789012:role/my-account", - "bucketName": "MyBucket", - "objectKey": "7aac5b80b050e7e4e168f84feffa5893.zip" - } - } - }, - "3dfe2b80b050e7e4e168f84feff678d4": { - "source": { - "executable": ["myzip"] - }, - "destinations": { - "us-east-1": { - "region": "us-east-1", - "assumeRoleArn": "arn:aws:iam::12345789012:role/my-account", - "bucketName": "MySpecialBucket", - "objectKey": "3dfe2b80b050e7e4e168f84feff678d4.zip" - } - } - }, - }, - "dockerImages": { - "b48783c58a86f7b8c68a4591c4f9be31": { - "source": { - "directory": "dockerdir", - }, - "destinations": { - "us-east-1": { - "region": "us-east-1", - "assumeRoleArn": "arn:aws:iam::12345789012:role/my-account", - "repositoryName": "MyRepository", - "imageTag": "b48783c58a86f7b8c68a4591c4f9be31", - "imageUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/MyRepository:1234567891b48783c58a86f7b8c68a4591c4f9be31", - } - } - }, - "d92753c58a86f7b8c68a4591c4f9cf28": { - "source": { - "executable": ["mytool", "package", "dockerdir"], - }, - "destinations": { - "us-east-1": { - "region": "us-east-1", - "assumeRoleArn": "arn:aws:iam::12345789012:role/my-account", - "repositoryName": "MyRepository2", - "imageTag": "d92753c58a86f7b8c68a4591c4f9cf28", - "imageUri": "123456789987.dkr.ecr.us-east-1.amazonaws.com/MyRepository2:1234567891b48783c58a86f7b8c68a4591c4f9be31", - } - } - } - } -} -``` - -### Placeholders - -The `destination` block of an asset manifest may contain the following region -and account placeholders: - -* `${AWS::Region}` -* `${AWS::AccountId}` - -These will be substituted with the region and account IDs currently configured -on the AWS SDK (through environment variables or `~/.aws/...` config files). - -* The `${AWS::AccountId}` placeholder will *not* be re-evaluated after - performing the `AssumeRole` call. -* If `${AWS::Region}` is used, it will principally be replaced with the value - in the `region` key. If the default region is intended, leave the `region` - key out of the manifest at all. - -## Docker image credentials - -For Docker image asset publishing, `cdk-assets` will `docker login` with -credentials from ECR GetAuthorizationToken prior to building and publishing, so -that the Dockerfile can reference images in the account's ECR repo. - -`cdk-assets` can also be configured to read credentials from both ECR and -SecretsManager prior to build by creating a credential configuration at -'~/.cdk/cdk-docker-creds.json' (override this location by setting the -CDK_DOCKER_CREDS_FILE environment variable). The credentials file has the -following format: - -```json -{ - "version": "1.0", - "domainCredentials": { - "domain1.example.com": { - "secretsManagerSecretId": "mySecret", // Can be the secret ID or full ARN - "roleArn": "arn:aws:iam::0123456789012:role/my-role" // (Optional) role with permissions to the secret - }, - "domain2.example.com": { - "ecrRepository": true, - "roleArn": "arn:aws:iam::0123456789012:role/my-role" // (Optional) role with permissions to the repo - } - } -} -``` - -If the credentials file is present, `docker` will be configured to use the -`docker-credential-cdk-assets` credential helper for each of the domains listed -in the file. This helper will assume the role provided (if present), and then fetch -the login credentials from either SecretsManager or ECR. - -## Using Drop-in Docker Replacements - -By default, the AWS CDK will build and publish Docker image assets using the -`docker` command. However, by specifying the `CDK_DOCKER` environment variable, -you can override the command that will be used to build and publish your -assets. diff --git a/packages/cdk-assets/bin/cdk-assets b/packages/cdk-assets/bin/cdk-assets deleted file mode 100755 index 09c08dd446846..0000000000000 --- a/packages/cdk-assets/bin/cdk-assets +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('./cdk-assets.js'); \ No newline at end of file diff --git a/packages/cdk-assets/bin/cdk-assets.ts b/packages/cdk-assets/bin/cdk-assets.ts deleted file mode 100644 index 4547051334449..0000000000000 --- a/packages/cdk-assets/bin/cdk-assets.ts +++ /dev/null @@ -1,67 +0,0 @@ -import * as yargs from 'yargs'; -import { list } from './list'; -import { setLogThreshold, VERSION } from './logging'; -import { publish } from './publish'; -import { AssetManifest } from '../lib'; - -async function main() { - const argv = yargs - .usage('$0 [args]') - .option('verbose', { - alias: 'v', - type: 'boolean', - desc: 'Increase logging verbosity', - count: true, - default: 0, - }) - .option('path', { - alias: 'p', - type: 'string', - desc: 'The path (file or directory) to load the assets from. If a directory, ' + - `the file '${AssetManifest.DEFAULT_FILENAME}' will be loaded from it.`, - default: '.', - requiresArg: true, - }) - .command('ls', 'List assets from the given manifest', command => command - , wrapHandler(async args => { - await list(args); - })) - .command('publish [ASSET..]', 'Publish assets in the given manifest', command => command - .option('profile', { type: 'string', describe: 'Profile to use from AWS Credentials file' }) - .positional('ASSET', { type: 'string', array: true, describe: 'Assets to publish (format: "ASSET[:DEST]"), default all' }) - , wrapHandler(async args => { - await publish({ - path: args.path, - assets: args.ASSET, - profile: args.profile, - }); - })) - .demandCommand() - .help() - .strict() // Error on wrong command - .version(VERSION) - .showHelpOnFail(false) - .argv; - - // Evaluating .argv triggers the parsing but the command gets implicitly executed, - // so we don't need the output. - Array.isArray(argv); -} - -/** - * Wrap a command's handler with standard pre- and post-work - */ -function wrapHandler(handler: (x: A) => Promise) { - return async (argv: A) => { - if (argv.verbose) { - setLogThreshold('verbose'); - } - await handler(argv); - }; -} - -main().catch(e => { - // eslint-disable-next-line no-console - console.error(e.stack); - process.exitCode = 1; -}); diff --git a/packages/cdk-assets/bin/docker-credential-cdk-assets b/packages/cdk-assets/bin/docker-credential-cdk-assets deleted file mode 100755 index 3829057860102..0000000000000 --- a/packages/cdk-assets/bin/docker-credential-cdk-assets +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('./docker-credential-cdk-assets.js'); diff --git a/packages/cdk-assets/bin/docker-credential-cdk-assets.ts b/packages/cdk-assets/bin/docker-credential-cdk-assets.ts deleted file mode 100644 index 6dccb5521cf55..0000000000000 --- a/packages/cdk-assets/bin/docker-credential-cdk-assets.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Docker Credential Helper to retrieve credentials based on an external configuration file. - * Supports loading credentials from ECR repositories and from Secrets Manager, - * optionally via an assumed role. - * - * The only operation currently supported by this credential helper at this time is the `get` - * command, which receives a domain name as input on stdin and returns a Username/Secret in - * JSON format on stdout. - * - * IMPORTANT - The credential helper must not output anything else besides the final credentials - * in any success case; doing so breaks docker's parsing of the output and causes the login to fail. - */ - -import * as fs from 'fs'; -import { DefaultAwsClient } from '../lib'; - -import { cdkCredentialsConfig, cdkCredentialsConfigFile, fetchDockerLoginCredentials } from '../lib/private/docker-credentials'; - -async function main() { - // Expected invocation is [node, docker-credential-cdk-assets, get] with input fed via STDIN - // For other valid docker commands (store, list, erase), we no-op. - if (process.argv.length !== 3 || process.argv[2] !== 'get') { - process.exit(0); - } - - const config = cdkCredentialsConfig(); - if (!config) { - throw new Error(`unable to find CDK Docker credentials at: ${cdkCredentialsConfigFile()}`); - } - - // Read the domain to fetch from stdin - let endpoint = fs.readFileSync(0, { encoding: 'utf-8' }).trim(); - const credentials = await fetchDockerLoginCredentials(new DefaultAwsClient(), config, endpoint); - // Write the credentials back to stdout - fs.writeFileSync(1, JSON.stringify(credentials)); -} - -main().catch(e => { - // eslint-disable-next-line no-console - console.error(e.stack); - process.exitCode = 1; -}); diff --git a/packages/cdk-assets/bin/list.ts b/packages/cdk-assets/bin/list.ts deleted file mode 100644 index e93358cd729fd..0000000000000 --- a/packages/cdk-assets/bin/list.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AssetManifest } from '../lib'; - -export async function list(args: { - path: string; -}) { - const manifest = AssetManifest.fromPath(args.path); - // eslint-disable-next-line no-console - console.log(manifest.list().join('\n')); -} \ No newline at end of file diff --git a/packages/cdk-assets/bin/logging.ts b/packages/cdk-assets/bin/logging.ts deleted file mode 100644 index ead34deeaa70c..0000000000000 --- a/packages/cdk-assets/bin/logging.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -export type LogLevel = 'verbose' | 'info' | 'error'; -let logThreshold: LogLevel = 'info'; - -export const VERSION = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' })).version; - -const LOG_LEVELS: Record = { - verbose: 1, - info: 2, - error: 3, -}; - -export function setLogThreshold(threshold: LogLevel) { - logThreshold = threshold; -} - -export function log(level: LogLevel, message: string) { - if (LOG_LEVELS[level] >= LOG_LEVELS[logThreshold]) { - // eslint-disable-next-line no-console - console.error(`${level.padEnd(7, ' ')}: ${message}`); - } -} \ No newline at end of file diff --git a/packages/cdk-assets/bin/publish.ts b/packages/cdk-assets/bin/publish.ts deleted file mode 100644 index 87ead6eac14ae..0000000000000 --- a/packages/cdk-assets/bin/publish.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { log, LogLevel } from './logging'; -import { - AssetManifest, AssetPublishing, DefaultAwsClient, DestinationPattern, EventType, - IPublishProgress, IPublishProgressListener, -} from '../lib'; - -export async function publish(args: { - path: string; - assets?: string[]; - profile?: string; -}) { - - let manifest = AssetManifest.fromPath(args.path); - log('verbose', `Loaded manifest from ${args.path}: ${manifest.entries.length} assets found`); - - if (args.assets && args.assets.length > 0) { - const selection = args.assets.map(a => DestinationPattern.parse(a)); - manifest = manifest.select(selection); - log('verbose', `Applied selection: ${manifest.entries.length} assets selected.`); - } - - const pub = new AssetPublishing(manifest, { - aws: new DefaultAwsClient(args.profile), - progressListener: new ConsoleProgress(), - throwOnError: false, - }); - - await pub.publish(); - - if (pub.hasFailures) { - for (const failure of pub.failures) { - // eslint-disable-next-line no-console - console.error('Failure:', failure.error.stack); - } - - process.exitCode = 1; - } -} - -const EVENT_TO_LEVEL: Record = { - build: 'verbose', - cached: 'verbose', - check: 'verbose', - debug: 'verbose', - fail: 'error', - found: 'verbose', - start: 'info', - success: 'info', - upload: 'verbose', -}; - -class ConsoleProgress implements IPublishProgressListener { - public onPublishEvent(type: EventType, event: IPublishProgress): void { - log(EVENT_TO_LEVEL[type], `[${event.percentComplete}%] ${type}: ${event.message}`); - } -} diff --git a/packages/cdk-assets/jest.config.js b/packages/cdk-assets/jest.config.js deleted file mode 100644 index 4147a830a714b..0000000000000 --- a/packages/cdk-assets/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); -module.exports = { - ...baseConfig, - coverageThreshold: { - global: { - ...baseConfig.coverageThreshold.global, - statements: 75, - branches: 60, - }, - }, -}; diff --git a/packages/cdk-assets/lib/asset-manifest.ts b/packages/cdk-assets/lib/asset-manifest.ts deleted file mode 100644 index 0cb92396ff424..0000000000000 --- a/packages/cdk-assets/lib/asset-manifest.ts +++ /dev/null @@ -1,313 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { - AssetManifest as AssetManifestSchema, DockerImageDestination, DockerImageSource, - FileDestination, FileSource, Manifest, -} from '@aws-cdk/cloud-assembly-schema'; - -/** - * A manifest of assets - */ -export class AssetManifest { - /** - * The default name of the asset manifest in a cdk.out directory - */ - public static readonly DEFAULT_FILENAME = 'assets.json'; - - /** - * Load an asset manifest from the given file - */ - public static fromFile(fileName: string) { - try { - const obj = Manifest.loadAssetManifest(fileName); - return new AssetManifest(path.dirname(fileName), obj); - } catch (e: any) { - throw new Error(`Canot read asset manifest '${fileName}': ${e.message}`); - } - } - - /** - * Load an asset manifest from the given file or directory - * - * If the argument given is a directoy, the default asset file name will be used. - */ - public static fromPath(filePath: string) { - let st; - try { - st = fs.statSync(filePath); - } catch (e: any) { - throw new Error(`Cannot read asset manifest at '${filePath}': ${e.message}`); - } - if (st.isDirectory()) { - return AssetManifest.fromFile(path.join(filePath, AssetManifest.DEFAULT_FILENAME)); - } - return AssetManifest.fromFile(filePath); - } - - /** - * The directory where the manifest was found - */ - public readonly directory: string; - - constructor(directory: string, private readonly manifest: AssetManifestSchema) { - this.directory = directory; - } - - /** - * Select a subset of assets and destinations from this manifest. - * - * Only assets with at least 1 selected destination are retained. - * - * If selection is not given, everything is returned. - */ - public select(selection?: DestinationPattern[]): AssetManifest { - if (selection === undefined) { return this; } - - const ret: AssetManifestSchema & Required> - = { version: this.manifest.version, dockerImages: {}, files: {} }; - - for (const assetType of ASSET_TYPES) { - for (const [assetId, asset] of Object.entries(this.manifest[assetType] || {})) { - const filteredDestinations = filterDict( - asset.destinations, - (_, destId) => selection.some(sel => sel.matches(new DestinationIdentifier(assetId, destId)))); - - if (Object.keys(filteredDestinations).length > 0) { - ret[assetType][assetId] = { - ...asset, - destinations: filteredDestinations, - }; - } - } - } - - return new AssetManifest(this.directory, ret); - } - - /** - * Describe the asset manifest as a list of strings - */ - public list() { - return [ - ...describeAssets('file', this.manifest.files || {}), - ...describeAssets('docker-image', this.manifest.dockerImages || {}), - ]; - - function describeAssets(type: string, assets: Record }>) { - const ret = new Array(); - for (const [assetId, asset] of Object.entries(assets || {})) { - ret.push(`${assetId} ${type} ${JSON.stringify(asset.source)}`); - - const destStrings = Object.entries(asset.destinations).map(([destId, dest]) => ` ${assetId}:${destId} ${JSON.stringify(dest)}`); - ret.push(...prefixTreeChars(destStrings, ' ')); - } - return ret; - } - } - - /** - * List of assets per destination - * - * Returns one asset for every publishable destination. Multiple asset - * destinations may share the same asset source. - */ - public get entries(): IManifestEntry[] { - return [ - ...makeEntries(this.manifest.files || {}, FileManifestEntry), - ...makeEntries(this.manifest.dockerImages || {}, DockerImageManifestEntry), - ]; - } - - /** - * List of file assets, splat out to destinations - */ - public get files(): FileManifestEntry[] { - return makeEntries(this.manifest.files || {}, FileManifestEntry); - } -} - -function makeEntries( - assets: Record }>, - ctor: new (id: DestinationIdentifier, source: A, destination: B) => C): C[] { - - const ret = new Array(); - for (const [assetId, asset] of Object.entries(assets)) { - for (const [destId, destination] of Object.entries(asset.destinations)) { - ret.push(new ctor(new DestinationIdentifier(assetId, destId), asset.source, destination)); - } - } - return ret; -} - -type AssetType = 'files' | 'dockerImages'; - -const ASSET_TYPES: AssetType[] = ['files', 'dockerImages']; - -/** - * A single asset from an asset manifest' - */ -export interface IManifestEntry { - /** - * The identifier of the asset and its destination - */ - readonly id: DestinationIdentifier; - - /** - * The type of asset - */ - readonly type: string; - - /** - * Type-dependent source data - */ - readonly genericSource: unknown; - - /** - * Type-dependent destination data - */ - readonly genericDestination: unknown; -} - -/** - * A manifest entry for a file asset - */ -export class FileManifestEntry implements IManifestEntry { - public readonly genericSource: unknown; - public readonly genericDestination: unknown; - public readonly type = 'file'; - - constructor( - /** Identifier for this asset */ - public readonly id: DestinationIdentifier, - /** Source of the file asset */ - public readonly source: FileSource, - /** Destination for the file asset */ - public readonly destination: FileDestination, - ) { - this.genericSource = source; - this.genericDestination = destination; - } -} - -/** - * A manifest entry for a docker image asset - */ -export class DockerImageManifestEntry implements IManifestEntry { - public readonly genericSource: unknown; - public readonly genericDestination: unknown; - public readonly type = 'docker-image'; - - constructor( - /** Identifier for this asset */ - public readonly id: DestinationIdentifier, - /** Source of the file asset */ - public readonly source: DockerImageSource, - /** Destination for the file asset */ - public readonly destination: DockerImageDestination, - ) { - this.genericSource = source; - this.genericDestination = destination; - } -} - -/** - * Identify an asset destination in an asset manifest - * - * When stringified, this will be a combination of the source - * and destination IDs. - */ -export class DestinationIdentifier { - /** - * Identifies the asset, by source. - * - * The assetId will be the same between assets that represent - * the same physical file or image. - */ - public readonly assetId: string; - - /** - * Identifies the destination where this asset will be published - */ - public readonly destinationId: string; - - constructor(assetId: string, destinationId: string) { - this.assetId = assetId; - this.destinationId = destinationId; - } - - /** - * Return a string representation for this asset identifier - */ - public toString() { - return this.destinationId ? `${this.assetId}:${this.destinationId}` : this.assetId; - } -} - -function filterDict(xs: Record, pred: (x: A, key: string) => boolean): Record { - const ret: Record = {}; - for (const [key, value] of Object.entries(xs)) { - if (pred(value, key)) { - ret[key] = value; - } - } - return ret; -} - -/** - * A filter pattern for an destination identifier - */ -export class DestinationPattern { - /** - * Parse a ':'-separated string into an asset/destination identifier - */ - public static parse(s: string) { - if (!s) { throw new Error('Empty string is not a valid destination identifier'); } - const parts = s.split(':').map(x => x !== '*' ? x : undefined); - if (parts.length === 1) { return new DestinationPattern(parts[0]); } - if (parts.length === 2) { return new DestinationPattern(parts[0] || undefined, parts[1] || undefined); } - throw new Error(`Asset identifier must contain at most 2 ':'-separated parts, got '${s}'`); - } - - /** - * Identifies the asset, by source. - */ - public readonly assetId?: string; - - /** - * Identifies the destination where this asset will be published - */ - public readonly destinationId?: string; - - constructor(assetId?: string, destinationId?: string) { - this.assetId = assetId; - this.destinationId = destinationId; - } - - /** - * Whether or not this pattern matches the given identifier - */ - public matches(id: DestinationIdentifier) { - return (this.assetId === undefined || this.assetId === id.assetId) - && (this.destinationId === undefined || this.destinationId === id.destinationId); - } - - /** - * Return a string representation for this asset identifier - */ - public toString() { - return `${this.assetId ?? '*'}:${this.destinationId ?? '*'}`; - } -} - -/** - * Prefix box-drawing characters to make lines look like a hanging tree - */ -function prefixTreeChars(xs: string[], prefix = '') { - const ret = new Array(); - for (let i = 0; i < xs.length; i++) { - const isLast = i === xs.length - 1; - const boxChar = isLast ? '└' : '├'; - ret.push(`${prefix}${boxChar}${xs[i]}`); - } - return ret; -} diff --git a/packages/cdk-assets/lib/aws.ts b/packages/cdk-assets/lib/aws.ts deleted file mode 100644 index 48143f24b0c1f..0000000000000 --- a/packages/cdk-assets/lib/aws.ts +++ /dev/null @@ -1,164 +0,0 @@ -import * as os from 'os'; - -/** - * AWS SDK operations required by Asset Publishing - */ -export interface IAws { - discoverPartition(): Promise; - discoverDefaultRegion(): Promise; - discoverCurrentAccount(): Promise; - - discoverTargetAccount(options: ClientOptions): Promise; - s3Client(options: ClientOptions): Promise; - ecrClient(options: ClientOptions): Promise; - secretsManagerClient(options: ClientOptions): Promise; -} - -export interface ClientOptions { - region?: string; - assumeRoleArn?: string; - assumeRoleExternalId?: string; - assumeRoleSessionTags?: { [key: string]: string }; - quiet?: boolean; -} - -/** - * An AWS account - * - * An AWS account always exists in only one partition. Usually we don't care about - * the partition, but when we need to form ARNs we do. - */ -export interface Account { - /** - * The account number - */ - readonly accountId: string; - - /** - * The partition ('aws' or 'aws-cn' or otherwise) - */ - readonly partition: string; -} - -/** - * AWS client using the AWS SDK for JS with no special configuration - */ -export class DefaultAwsClient implements IAws { - private readonly AWS: typeof import('aws-sdk'); - private account?: Account; - - constructor(profile?: string) { - // Force AWS SDK to look in ~/.aws/credentials and potentially use the configured profile. - process.env.AWS_SDK_LOAD_CONFIG = '1'; - process.env.AWS_STS_REGIONAL_ENDPOINTS = 'regional'; - process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = '1'; - if (profile) { - process.env.AWS_PROFILE = profile; - } - // Stop SDKv2 from displaying a warning for now. We are aware and will migrate at some point, - // our customer don't need to be bothered with this. - process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE = '1'; - - // We need to set the environment before we load this library for the first time. - // eslint-disable-next-line @typescript-eslint/no-require-imports - this.AWS = require('aws-sdk'); - } - - public async s3Client(options: ClientOptions) { - return new this.AWS.S3(await this.awsOptions(options)); - } - - public async ecrClient(options: ClientOptions) { - return new this.AWS.ECR(await this.awsOptions(options)); - } - - public async secretsManagerClient(options: ClientOptions) { - return new this.AWS.SecretsManager(await this.awsOptions(options)); - } - - public async discoverPartition(): Promise { - return (await this.discoverCurrentAccount()).partition; - } - - public async discoverDefaultRegion(): Promise { - return this.AWS.config.region || 'us-east-1'; - } - - public async discoverCurrentAccount(): Promise { - if (this.account === undefined) { - const sts = new this.AWS.STS(); - const response = await sts.getCallerIdentity().promise(); - if (!response.Account || !response.Arn) { - throw new Error(`Unrecognized response from STS: '${JSON.stringify(response)}'`); - } - this.account = { - accountId: response.Account!, - partition: response.Arn!.split(':')[1], - }; - } - - return this.account; - } - - public async discoverTargetAccount(options: ClientOptions): Promise { - const sts = new this.AWS.STS(await this.awsOptions(options)); - const response = await sts.getCallerIdentity().promise(); - if (!response.Account || !response.Arn) { - throw new Error(`Unrecognized response from STS: '${JSON.stringify(response)}'`); - } - return { - accountId: response.Account!, - partition: response.Arn!.split(':')[1], - }; - } - - private async awsOptions(options: ClientOptions) { - let credentials; - - if (options.assumeRoleArn) { - credentials = await this.assumeRole(options.region, options.assumeRoleArn, options.assumeRoleExternalId); - } - - return { - region: options.region, - customUserAgent: 'cdk-assets', - credentials, - }; - } - - /** - * Explicit manual AssumeRole call - * - * Necessary since I can't seem to get the built-in support for ChainableTemporaryCredentials to work. - * - * It needs an explicit configuration of `masterCredentials`, we need to put - * a `DefaultCredentialProverChain()` in there but that is not possible. - */ - private async assumeRole(region: string | undefined, roleArn: string, externalId?: string): Promise { - return new this.AWS.ChainableTemporaryCredentials({ - params: { - RoleArn: roleArn, - ExternalId: externalId, - RoleSessionName: `cdk-assets-${safeUsername()}`, - }, - stsConfig: { - region, - customUserAgent: 'cdk-assets', - }, - }); - } -} - -/** - * Return the username with characters invalid for a RoleSessionName removed - * - * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters - */ -function safeUsername() { - try { - return os.userInfo().username.replace(/[^\w+=,.@-]/g, '@'); - } catch { - return 'noname'; - } -} - diff --git a/packages/cdk-assets/lib/index.ts b/packages/cdk-assets/lib/index.ts deleted file mode 100644 index 26f81852f3601..0000000000000 --- a/packages/cdk-assets/lib/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './publishing'; -export * from './asset-manifest'; -export * from './aws'; -export * from './progress'; diff --git a/packages/cdk-assets/lib/private/archive.ts b/packages/cdk-assets/lib/private/archive.ts deleted file mode 100644 index 8e0d9a900b46e..0000000000000 --- a/packages/cdk-assets/lib/private/archive.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { createWriteStream, promises as fs } from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; - -// namespace object imports won't work in the bundle for function exports -// eslint-disable-next-line @typescript-eslint/no-require-imports -const archiver = require('archiver'); - -type Logger = (x: string) => void; - -export async function zipDirectory(directory: string, outputFile: string, logger: Logger): Promise { - // We write to a temporary file and rename at the last moment. This is so that if we are - // interrupted during this process, we don't leave a half-finished file in the target location. - const temporaryOutputFile = `${outputFile}.${randomString()}._tmp`; - await writeZipFile(directory, temporaryOutputFile); - await moveIntoPlace(temporaryOutputFile, outputFile, logger); -} - -function writeZipFile(directory: string, outputFile: string): Promise { - return new Promise(async (ok, fail) => { - // The below options are needed to support following symlinks when building zip files: - // - nodir: This will prevent symlinks themselves from being copied into the zip. - // - follow: This will follow symlinks and copy the files within. - const globOptions = { - dot: true, - nodir: true, - follow: true, - cwd: directory, - }; - const files = glob.sync('**', globOptions); // The output here is already sorted - - const output = createWriteStream(outputFile); - - const archive = archiver('zip'); - archive.on('warning', fail); - archive.on('error', fail); - - // archive has been finalized and the output file descriptor has closed, resolve promise - // this has to be done before calling `finalize` since the events may fire immediately after. - // see https://www.npmjs.com/package/archiver - output.once('close', ok); - - archive.pipe(output); - - // Append files serially to ensure file order - for (const file of files) { - const fullPath = path.resolve(directory, file); - const [data, stat] = await Promise.all([fs.readFile(fullPath), fs.stat(fullPath)]); - archive.append(data, { - name: file, - date: new Date('1980-01-01T00:00:00.000Z'), // reset dates to get the same hash for the same content - mode: stat.mode, - }); - } - - await archive.finalize(); - }); -} - -/** - * Rename the file to the target location, taking into account: - * - * - That we may see EPERM on Windows while an Antivirus scanner still has the - * file open, so retry a couple of times. - * - This same function may be called in parallel and be interrupted at any point. - */ -async function moveIntoPlace(source: string, target: string, logger: Logger) { - let delay = 100; - let attempts = 5; - while (true) { - try { - // 'rename' is guaranteed to overwrite an existing target, as long as it is a file (not a directory) - await fs.rename(source, target); - return; - } catch (e: any) { - if (e.code !== 'EPERM' || attempts-- <= 0) { - throw e; - } - logger(e.message); - await sleep(Math.floor(Math.random() * delay)); - delay *= 2; - } - } -} - -function sleep(ms: number) { - return new Promise(ok => setTimeout(ok, ms)); -} - -function randomString() { - return Math.random().toString(36).replace(/[^a-z0-9]+/g, ''); -} diff --git a/packages/cdk-assets/lib/private/asset-handler.ts b/packages/cdk-assets/lib/private/asset-handler.ts deleted file mode 100644 index baafb3cd0317e..0000000000000 --- a/packages/cdk-assets/lib/private/asset-handler.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DockerFactory } from './docker'; -import { IAws } from '../aws'; -import { EventType } from '../progress'; - -/** - * Handler for asset building and publishing. - */ -export interface IAssetHandler { - /** - * Build the asset. - */ - build(): Promise; - - /** - * Publish the asset. - */ - publish(): Promise; - - /** - * Return whether the asset already exists - */ - isPublished(): Promise; -} - -export interface IHandlerHost { - readonly aws: IAws; - readonly aborted: boolean; - readonly dockerFactory: DockerFactory; - - emitMessage(type: EventType, m: string): void; -} - -export interface IHandlerOptions { - /** - * Suppress all output - */ - readonly quiet?: boolean; -} diff --git a/packages/cdk-assets/lib/private/docker-credentials.ts b/packages/cdk-assets/lib/private/docker-credentials.ts deleted file mode 100644 index c46add8caeefb..0000000000000 --- a/packages/cdk-assets/lib/private/docker-credentials.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { Logger } from './shell'; -import { IAws } from '../aws'; - -export interface DockerCredentials { - readonly Username: string; - readonly Secret: string; -} - -export interface DockerCredentialsConfig { - readonly version: string; - readonly domainCredentials: Record; -} - -export interface DockerDomainCredentialSource { - readonly secretsManagerSecretId?: string; - readonly secretsUsernameField?: string; - readonly secretsPasswordField?: string; - readonly ecrRepository?: boolean; - readonly assumeRoleArn?: string; -} - -/** Returns the presumed location of the CDK Docker credentials config file */ -export function cdkCredentialsConfigFile(): string { - return process.env.CDK_DOCKER_CREDS_FILE ?? path.join((os.userInfo().homedir ?? os.homedir()).trim() || '/', '.cdk', 'cdk-docker-creds.json'); -} - -let _cdkCredentials: DockerCredentialsConfig | undefined; -/** Loads and parses the CDK Docker credentials configuration, if it exists. */ -export function cdkCredentialsConfig(): DockerCredentialsConfig | undefined { - if (!_cdkCredentials) { - try { - _cdkCredentials = JSON.parse(fs.readFileSync(cdkCredentialsConfigFile(), { encoding: 'utf-8' })) as DockerCredentialsConfig; - } catch { } - } - return _cdkCredentials; -} - -/** Fetches login credentials from the configured source (e.g., SecretsManager, ECR) */ -export async function fetchDockerLoginCredentials(aws: IAws, config: DockerCredentialsConfig, endpoint: string) { - // Paranoid handling to ensure new URL() doesn't throw if the schema is missing - // For official docker registry, docker will pass https://index.docker.io/v1/ - endpoint = endpoint.includes('://') ? endpoint : `https://${endpoint}`; - const domain = new URL(endpoint).hostname; - - if (!Object.keys(config.domainCredentials).includes(domain) && !Object.keys(config.domainCredentials).includes(endpoint)) { - throw new Error(`unknown domain ${domain}`); - } - - let domainConfig = config.domainCredentials[domain] ?? config.domainCredentials[endpoint]; - - if (domainConfig.secretsManagerSecretId) { - const sm = await aws.secretsManagerClient({ assumeRoleArn: domainConfig.assumeRoleArn }); - const secretValue = await sm.getSecretValue({ SecretId: domainConfig.secretsManagerSecretId }).promise(); - if (!secretValue.SecretString) { throw new Error(`unable to fetch SecretString from secret: ${domainConfig.secretsManagerSecretId}`); }; - - const secret = JSON.parse(secretValue.SecretString); - - const usernameField = domainConfig.secretsUsernameField ?? 'username'; - const secretField = domainConfig.secretsPasswordField ?? 'secret'; - if (!secret[usernameField] || !secret[secretField]) { - throw new Error(`malformed secret string ("${usernameField}" or "${secretField}" field missing)`); - } - - return { Username: secret[usernameField], Secret: secret[secretField] }; - } else if (domainConfig.ecrRepository) { - const ecr = await aws.ecrClient({ assumeRoleArn: domainConfig.assumeRoleArn }); - const ecrAuthData = await obtainEcrCredentials(ecr); - - return { Username: ecrAuthData.username, Secret: ecrAuthData.password }; - } else { - throw new Error('unknown credential type: no secret ID or ECR repo'); - } -} - -export async function obtainEcrCredentials(ecr: AWS.ECR, logger?: Logger) { - if (logger) { logger('Fetching ECR authorization token'); } - const authData = (await ecr.getAuthorizationToken({ }).promise()).authorizationData || []; - if (authData.length === 0) { - throw new Error('No authorization data received from ECR'); - } - const token = Buffer.from(authData[0].authorizationToken!, 'base64').toString('ascii'); - const [username, password] = token.split(':'); - if (!username || !password) { throw new Error('unexpected ECR authData format'); } - - return { - username, - password, - endpoint: authData[0].proxyEndpoint!, - }; -} diff --git a/packages/cdk-assets/lib/private/docker.ts b/packages/cdk-assets/lib/private/docker.ts deleted file mode 100644 index f321bac3c11b6..0000000000000 --- a/packages/cdk-assets/lib/private/docker.ts +++ /dev/null @@ -1,279 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { cdkCredentialsConfig, obtainEcrCredentials } from './docker-credentials'; -import { Logger, shell, ShellOptions, ProcessFailedError } from './shell'; -import { createCriticalSection } from './util'; - -interface BuildOptions { - readonly directory: string; - - /** - * Tag the image with a given repoName:tag combination - */ - readonly tag: string; - readonly target?: string; - readonly file?: string; - readonly buildArgs?: Record; - readonly buildSecrets?: Record; - readonly buildSsh?: string; - readonly networkMode?: string; - readonly platform?: string; - readonly outputs?: string[]; - readonly cacheFrom?: DockerCacheOption[]; - readonly cacheTo?: DockerCacheOption; - readonly cacheDisabled?: boolean; - readonly quiet?: boolean; -} - -interface PushOptions { - readonly tag: string; - readonly quiet?: boolean; -} - -export interface DockerCredentialsConfig { - readonly version: string; - readonly domainCredentials: Record; -} - -export interface DockerDomainCredentials { - readonly secretsManagerSecretId?: string; - readonly ecrRepository?: string; -} - -enum InspectImageErrorCode { - Docker = 1, - Podman = 125, -} - -export interface DockerCacheOption { - readonly type: string; - readonly params?: { [key: string]: string }; -} - -export class Docker { - - private configDir: string | undefined = undefined; - - constructor(private readonly logger?: Logger) { - } - - /** - * Whether an image with the given tag exists - */ - public async exists(tag: string) { - try { - await this.execute(['inspect', tag], { quiet: true }); - return true; - } catch (e: any) { - const error: ProcessFailedError = e; - - /** - * The only error we expect to be thrown will have this property and value. - * If it doesn't, it's unrecognized so re-throw it. - */ - if (error.code !== 'PROCESS_FAILED') { - throw error; - } - - /** - * If we know the shell command above returned an error, check to see - * if the exit code is one we know to actually mean that the image doesn't - * exist. - */ - switch (error.exitCode) { - case InspectImageErrorCode.Docker: - case InspectImageErrorCode.Podman: - // Docker and Podman will return this exit code when an image doesn't exist, return false - // context: https://github.com/aws/aws-cdk/issues/16209 - return false; - default: - // This is an error but it's not an exit code we recognize, throw. - throw error; - } - } - } - - public async build(options: BuildOptions) { - const buildCommand = [ - 'build', - ...flatten(Object.entries(options.buildArgs || {}).map(([k, v]) => ['--build-arg', `${k}=${v}`])), - ...flatten(Object.entries(options.buildSecrets || {}).map(([k, v]) => ['--secret', `id=${k},${v}`])), - ...options.buildSsh ? ['--ssh', options.buildSsh] : [], - '--tag', options.tag, - ...options.target ? ['--target', options.target] : [], - ...options.file ? ['--file', options.file] : [], - ...options.networkMode ? ['--network', options.networkMode] : [], - ...options.platform ? ['--platform', options.platform] : [], - ...options.outputs ? options.outputs.map(output => [`--output=${output}`]) : [], - ...options.cacheFrom ? [...options.cacheFrom.map(cacheFrom => ['--cache-from', this.cacheOptionToFlag(cacheFrom)]).flat()] : [], - ...options.cacheTo ? ['--cache-to', this.cacheOptionToFlag(options.cacheTo)] : [], - ...options.cacheDisabled ? ['--no-cache'] : [], - '.', - ]; - await this.execute(buildCommand, { - cwd: options.directory, - quiet: options.quiet, - }); - } - - /** - * Get credentials from ECR and run docker login - */ - public async login(ecr: AWS.ECR) { - const credentials = await obtainEcrCredentials(ecr); - - // Use --password-stdin otherwise docker will complain. Loudly. - await this.execute(['login', - '--username', credentials.username, - '--password-stdin', - credentials.endpoint], { - input: credentials.password, - - // Need to quiet otherwise Docker will complain - // 'WARNING! Your password will be stored unencrypted' - // doesn't really matter since it's a token. - quiet: true, - }); - } - - public async tag(sourceTag: string, targetTag: string) { - await this.execute(['tag', sourceTag, targetTag]); - } - - public async push(options: PushOptions) { - await this.execute(['push', options.tag], { quiet: options.quiet }); - } - - /** - * If a CDK Docker Credentials file exists, creates a new Docker config directory. - * Sets up `docker-credential-cdk-assets` to be the credential helper for each domain in the CDK config. - * All future commands (e.g., `build`, `push`) will use this config. - * - * See https://docs.docker.com/engine/reference/commandline/login/#credential-helpers for more details on cred helpers. - * - * @returns true if CDK config was found and configured, false otherwise - */ - public configureCdkCredentials(): boolean { - const config = cdkCredentialsConfig(); - if (!config) { return false; } - - this.configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdkDockerConfig')); - - const domains = Object.keys(config.domainCredentials); - const credHelpers = domains.reduce((map: Record, domain) => { - map[domain] = 'cdk-assets'; // Use docker-credential-cdk-assets for this domain - return map; - }, {}); - fs.writeFileSync(path.join(this.configDir, 'config.json'), JSON.stringify({ credHelpers }), { encoding: 'utf-8' }); - - return true; - } - - /** - * Removes any configured Docker config directory. - * All future commands (e.g., `build`, `push`) will use the default config. - * - * This is useful after calling `configureCdkCredentials` to reset to default credentials. - */ - public resetAuthPlugins() { - this.configDir = undefined; - } - - private async execute(args: string[], options: ShellOptions = {}) { - const configArgs = this.configDir ? ['--config', this.configDir] : []; - - const pathToCdkAssets = path.resolve(__dirname, '..', '..', 'bin'); - try { - await shell([getDockerCmd(), ...configArgs, ...args], { - logger: this.logger, - ...options, - env: { - ...process.env, - ...options.env, - PATH: `${pathToCdkAssets}${path.delimiter}${options.env?.PATH ?? process.env.PATH}`, - }, - }); - } catch (e: any) { - if (e.code === 'ENOENT') { - throw new Error('Unable to execute \'docker\' in order to build a container asset. Please install \'docker\' and try again.'); - } - throw e; - } - } - - private cacheOptionToFlag(option: DockerCacheOption): string { - let flag = `type=${option.type}`; - if (option.params) { - flag += ',' + Object.entries(option.params).map(([k, v]) => `${k}=${v}`).join(','); - } - return flag; - } -} - -export interface DockerFactoryOptions { - readonly repoUri: string; - readonly ecr: AWS.ECR; - readonly logger: (m: string) => void; -} - -/** - * Helps get appropriately configured Docker instances during the container - * image publishing process. - */ -export class DockerFactory { - private enterLoggedInDestinationsCriticalSection = createCriticalSection(); - private loggedInDestinations = new Set(); - - /** - * Gets a Docker instance for building images. - */ - public async forBuild(options: DockerFactoryOptions): Promise { - const docker = new Docker(options.logger); - - // Default behavior is to login before build so that the Dockerfile can reference images in the ECR repo - // However, if we're in a pipelines environment (for example), - // we may have alternative credentials to the default ones to use for the build itself. - // If the special config file is present, delay the login to the default credentials until the push. - // If the config file is present, we will configure and use those credentials for the build. - let cdkDockerCredentialsConfigured = docker.configureCdkCredentials(); - if (!cdkDockerCredentialsConfigured) { - await this.loginOncePerDestination(docker, options); - } - - return docker; - } - - /** - * Gets a Docker instance for pushing images to ECR. - */ - public async forEcrPush(options: DockerFactoryOptions) { - const docker = new Docker(options.logger); - await this.loginOncePerDestination(docker, options); - return docker; - } - - private async loginOncePerDestination(docker: Docker, options: DockerFactoryOptions) { - // Changes: 012345678910.dkr.ecr.us-west-2.amazonaws.com/tagging-test - // To this: 012345678910.dkr.ecr.us-west-2.amazonaws.com - const repositoryDomain = options.repoUri.split('/')[0]; - - // Ensure one-at-a-time access to loggedInDestinations. - await this.enterLoggedInDestinationsCriticalSection(async () => { - if (this.loggedInDestinations.has(repositoryDomain)) { - return; - } - - await docker.login(options.ecr); - this.loggedInDestinations.add(repositoryDomain); - }); - } -} - -function getDockerCmd(): string { - return process.env.CDK_DOCKER ?? 'docker'; -} - -function flatten(x: string[][]) { - return Array.prototype.concat([], ...x); -} diff --git a/packages/cdk-assets/lib/private/fs-extra.ts b/packages/cdk-assets/lib/private/fs-extra.ts deleted file mode 100644 index ac865789eb269..0000000000000 --- a/packages/cdk-assets/lib/private/fs-extra.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -const pfs = fs.promises; - -export async function pathExists(pathName: string) { - try { - await pfs.stat(pathName); - return true; - } catch (e: any) { - if (e.code !== 'ENOENT') { throw e; } - return false; - } -} - -export function emptyDirSync(dir: string) { - fs.readdirSync(dir, { withFileTypes: true }).forEach(dirent => { - const fullPath = path.join(dir, dirent.name); - if (dirent.isDirectory()) { - emptyDirSync(fullPath); - fs.rmdirSync(fullPath); - } else { - fs.unlinkSync(fullPath); - } - }); -} - -export function rmRfSync(dir: string) { - emptyDirSync(dir); - fs.rmdirSync(dir); -} diff --git a/packages/cdk-assets/lib/private/handlers/container-images.ts b/packages/cdk-assets/lib/private/handlers/container-images.ts deleted file mode 100644 index 8764b1e9c41b3..0000000000000 --- a/packages/cdk-assets/lib/private/handlers/container-images.ts +++ /dev/null @@ -1,238 +0,0 @@ -import * as path from 'path'; -import { DockerImageDestination } from '@aws-cdk/cloud-assembly-schema'; -import type * as AWS from 'aws-sdk'; -import { DockerImageManifestEntry } from '../../asset-manifest'; -import { EventType } from '../../progress'; -import { IAssetHandler, IHandlerHost, IHandlerOptions } from '../asset-handler'; -import { Docker } from '../docker'; -import { replaceAwsPlaceholders } from '../placeholders'; -import { shell } from '../shell'; - -interface ContainerImageAssetHandlerInit { - readonly ecr: AWS.ECR; - readonly repoUri: string; - readonly imageUri: string; - readonly destinationAlreadyExists: boolean; -} - -export class ContainerImageAssetHandler implements IAssetHandler { - private init?: ContainerImageAssetHandlerInit; - - constructor( - private readonly workDir: string, - private readonly asset: DockerImageManifestEntry, - private readonly host: IHandlerHost, - private readonly options: IHandlerOptions) { - } - - public async build(): Promise { - const initOnce = await this.initOnce(); - - if (initOnce.destinationAlreadyExists) { return; } - if (this.host.aborted) { return; } - - const dockerForBuilding = await this.host.dockerFactory.forBuild({ - repoUri: initOnce.repoUri, - logger: (m: string) => this.host.emitMessage(EventType.DEBUG, m), - ecr: initOnce.ecr, - }); - - const builder = new ContainerImageBuilder(dockerForBuilding, this.workDir, this.asset, this.host, { - quiet: this.options.quiet, - }); - const localTagName = await builder.build(); - - if (localTagName === undefined || this.host.aborted) { return; } - if (this.host.aborted) { return; } - - await dockerForBuilding.tag(localTagName, initOnce.imageUri); - } - - public async isPublished(): Promise { - try { - const initOnce = await this.initOnce({ quiet: true }); - return initOnce.destinationAlreadyExists; - } catch (e: any) { - this.host.emitMessage(EventType.DEBUG, `${e.message}`); - } - return false; - } - - public async publish(): Promise { - const initOnce = await this.initOnce(); - - if (initOnce.destinationAlreadyExists) { return; } - if (this.host.aborted) { return; } - - const dockerForPushing = await this.host.dockerFactory.forEcrPush({ - repoUri: initOnce.repoUri, - logger: (m: string) => this.host.emitMessage(EventType.DEBUG, m), - ecr: initOnce.ecr, - }); - - if (this.host.aborted) { return; } - - this.host.emitMessage(EventType.UPLOAD, `Push ${initOnce.imageUri}`); - await dockerForPushing.push({ tag: initOnce.imageUri, quiet: this.options.quiet }); - } - - private async initOnce(options: { quiet?: boolean } = {}): Promise { - if (this.init) { - return this.init; - } - - const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); - const ecr = await this.host.aws.ecrClient({ - ...destination, - quiet: options.quiet, - }); - const account = async () => (await this.host.aws.discoverCurrentAccount())?.accountId; - - const repoUri = await repositoryUri(ecr, destination.repositoryName); - if (!repoUri) { - throw new Error(`No ECR repository named '${destination.repositoryName}' in account ${await account()}. Is this account bootstrapped?`); - } - - const imageUri = `${repoUri}:${destination.imageTag}`; - - this.init = { - imageUri, - ecr, - repoUri, - destinationAlreadyExists: await this.destinationAlreadyExists(ecr, destination, imageUri), - }; - - return this.init; - } - - /** - * Check whether the image already exists in the ECR repo - * - * Use the fields from the destination to do the actual check. The imageUri - * should correspond to that, but is only used to print Docker image location - * for user benefit (the format is slightly different). - */ - private async destinationAlreadyExists(ecr: AWS.ECR, destination: DockerImageDestination, imageUri: string): Promise { - this.host.emitMessage(EventType.CHECK, `Check ${imageUri}`); - if (await imageExists(ecr, destination.repositoryName, destination.imageTag)) { - this.host.emitMessage(EventType.FOUND, `Found ${imageUri}`); - return true; - } - - return false; - } -} - -interface ContainerImageBuilderOptions { - readonly quiet?: boolean; -} - -class ContainerImageBuilder { - constructor( - private readonly docker: Docker, - private readonly workDir: string, - private readonly asset: DockerImageManifestEntry, - private readonly host: IHandlerHost, - private readonly options: ContainerImageBuilderOptions) { - } - - async build(): Promise { - return this.asset.source.executable - ? this.buildExternalAsset(this.asset.source.executable) - : this.buildDirectoryAsset(); - } - - /** - * Build a (local) Docker asset from a directory with a Dockerfile - * - * Tags under a deterministic, unique, local identifier wich will skip - * the build if it already exists. - */ - private async buildDirectoryAsset(): Promise { - const localTagName = `cdkasset-${this.asset.id.assetId.toLowerCase()}`; - - if (!(await this.isImageCached(localTagName))) { - if (this.host.aborted) { return undefined; } - - await this.buildImage(localTagName); - } - - return localTagName; - } - - /** - * Build a (local) Docker asset by running an external command - * - * External command is responsible for deduplicating the build if possible, - * and is expected to return the generated image identifier on stdout. - */ - private async buildExternalAsset(executable: string[], cwd?: string): Promise { - const assetPath = cwd ?? this.workDir; - - this.host.emitMessage(EventType.BUILD, `Building Docker image using command '${executable}'`); - if (this.host.aborted) { return undefined; } - - return (await shell(executable, { cwd: assetPath, quiet: true })).trim(); - } - - private async buildImage(localTagName: string): Promise { - const source = this.asset.source; - if (!source.directory) { - throw new Error(`'directory' is expected in the DockerImage asset source, got: ${JSON.stringify(source)}`); - } - - const fullPath = path.resolve(this.workDir, source.directory); - this.host.emitMessage(EventType.BUILD, `Building Docker image at ${fullPath}`); - - await this.docker.build({ - directory: fullPath, - tag: localTagName, - buildArgs: source.dockerBuildArgs, - buildSecrets: source.dockerBuildSecrets, - buildSsh: source.dockerBuildSsh, - target: source.dockerBuildTarget, - file: source.dockerFile, - networkMode: source.networkMode, - platform: source.platform, - outputs: source.dockerOutputs, - cacheFrom: source.cacheFrom, - cacheTo: source.cacheTo, - cacheDisabled: source.cacheDisabled, - quiet: this.options.quiet, - }); - } - - private async isImageCached(localTagName: string): Promise { - if (await this.docker.exists(localTagName)) { - this.host.emitMessage(EventType.CACHED, `Cached ${localTagName}`); - return true; - } - - return false; - } -} - -async function imageExists(ecr: AWS.ECR, repositoryName: string, imageTag: string) { - try { - await ecr.describeImages({ repositoryName, imageIds: [{ imageTag }] }).promise(); - return true; - } catch (e: any) { - if (e.code !== 'ImageNotFoundException') { throw e; } - return false; - } -} - -/** - * Return the URI for the repository with the given name - * - * Returns undefined if the repository does not exist. - */ -async function repositoryUri(ecr: AWS.ECR, repositoryName: string): Promise { - try { - const response = await ecr.describeRepositories({ repositoryNames: [repositoryName] }).promise(); - return (response.repositories || [])[0]?.repositoryUri; - } catch (e: any) { - if (e.code !== 'RepositoryNotFoundException') { throw e; } - return undefined; - } -} diff --git a/packages/cdk-assets/lib/private/handlers/files.ts b/packages/cdk-assets/lib/private/handlers/files.ts deleted file mode 100644 index 12008fd220323..0000000000000 --- a/packages/cdk-assets/lib/private/handlers/files.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { createReadStream, promises as fs } from 'fs'; -import * as path from 'path'; -import { FileAssetPackaging, FileSource } from '@aws-cdk/cloud-assembly-schema'; -import * as mime from 'mime'; -import { FileManifestEntry } from '../../asset-manifest'; -import { EventType } from '../../progress'; -import { zipDirectory } from '../archive'; -import { IAssetHandler, IHandlerHost } from '../asset-handler'; -import { pathExists } from '../fs-extra'; -import { replaceAwsPlaceholders } from '../placeholders'; -import { shell } from '../shell'; - -/** - * The size of an empty zip file is 22 bytes - * - * Ref: https://en.wikipedia.org/wiki/ZIP_(file_format) - */ -const EMPTY_ZIP_FILE_SIZE = 22; - -export class FileAssetHandler implements IAssetHandler { - private readonly fileCacheRoot: string; - - constructor( - private readonly workDir: string, - private readonly asset: FileManifestEntry, - private readonly host: IHandlerHost) { - this.fileCacheRoot = path.join(workDir, '.cache'); - } - - public async build(): Promise {} - - public async isPublished(): Promise { - const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); - const s3Url = `s3://${destination.bucketName}/${destination.objectKey}`; - try { - const s3 = await this.host.aws.s3Client({ - ...destination, - quiet: true, - }); - this.host.emitMessage(EventType.CHECK, `Check ${s3Url}`); - - if (await objectExists(s3, destination.bucketName, destination.objectKey)) { - this.host.emitMessage(EventType.FOUND, `Found ${s3Url}`); - return true; - } - } catch (e: any) { - this.host.emitMessage(EventType.DEBUG, `${e.message}`); - } - return false; - } - - public async publish(): Promise { - const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws); - const s3Url = `s3://${destination.bucketName}/${destination.objectKey}`; - const s3 = await this.host.aws.s3Client(destination); - this.host.emitMessage(EventType.CHECK, `Check ${s3Url}`); - - const bucketInfo = BucketInformation.for(this.host); - - // A thunk for describing the current account. Used when we need to format an error - // message, not in the success case. - const account = async () => (await this.host.aws.discoverTargetAccount(destination))?.accountId; - switch (await bucketInfo.bucketOwnership(s3, destination.bucketName)) { - case BucketOwnership.MINE: - break; - case BucketOwnership.DOES_NOT_EXIST: - throw new Error(`No bucket named '${destination.bucketName}'. Is account ${await account()} bootstrapped?`); - case BucketOwnership.SOMEONE_ELSES_OR_NO_ACCESS: - throw new Error(`Bucket named '${destination.bucketName}' exists, but not in account ${await account()}. Wrong account?`); - } - - if (await objectExists(s3, destination.bucketName, destination.objectKey)) { - this.host.emitMessage(EventType.FOUND, `Found ${s3Url}`); - return; - } - - // Identify the the bucket encryption type to set the header on upload - // required for SCP rules denying uploads without encryption header - let paramsEncryption: {[index: string]:any}= {}; - const encryption2 = await bucketInfo.bucketEncryption(s3, destination.bucketName); - switch (encryption2.type) { - case 'no_encryption': - break; - case 'aes256': - paramsEncryption = { ServerSideEncryption: 'AES256' }; - break; - case 'kms': - // We must include the key ID otherwise S3 will encrypt with the default key - paramsEncryption = { - ServerSideEncryption: 'aws:kms', - SSEKMSKeyId: encryption2.kmsKeyId, - }; - break; - case 'does_not_exist': - this.host.emitMessage(EventType.DEBUG, `No bucket named '${destination.bucketName}'. Is account ${await account()} bootstrapped?`); - break; - case 'access_denied': - this.host.emitMessage(EventType.DEBUG, `Could not read encryption settings of bucket '${destination.bucketName}': uploading with default settings ("cdk bootstrap" to version 9 if your organization's policies prevent a successful upload or to get rid of this message).`); - break; - } - - if (this.host.aborted) { return; } - const publishFile = this.asset.source.executable ? - await this.externalPackageFile(this.asset.source.executable) : await this.packageFile(this.asset.source); - - this.host.emitMessage(EventType.UPLOAD, `Upload ${s3Url}`); - - const params = Object.assign({}, { - Bucket: destination.bucketName, - Key: destination.objectKey, - Body: createReadStream(publishFile.packagedPath), - ContentType: publishFile.contentType, - }, - paramsEncryption); - - await s3.upload(params).promise(); - } - - private async packageFile(source: FileSource): Promise { - if (!source.path) { - throw new Error(`'path' is expected in the File asset source, got: ${JSON.stringify(source)}`); - } - - const fullPath = path.resolve(this.workDir, source.path); - - if (source.packaging === FileAssetPackaging.ZIP_DIRECTORY) { - const contentType = 'application/zip'; - - await fs.mkdir(this.fileCacheRoot, { recursive: true }); - const packagedPath = path.join(this.fileCacheRoot, `${this.asset.id.assetId}.zip`); - - if (await pathExists(packagedPath)) { - this.host.emitMessage(EventType.CACHED, `From cache ${packagedPath}`); - return { packagedPath, contentType }; - } - - this.host.emitMessage(EventType.BUILD, `Zip ${fullPath} -> ${packagedPath}`); - await zipDirectory(fullPath, packagedPath, (m) => this.host.emitMessage(EventType.DEBUG, m)); - return { packagedPath, contentType }; - } else { - const contentType = mime.getType(fullPath) ?? 'application/octet-stream'; - return { packagedPath: fullPath, contentType }; - } - } - - private async externalPackageFile(executable: string[]): Promise { - this.host.emitMessage(EventType.BUILD, `Building asset source using command: '${executable}'`); - - return { - packagedPath: (await shell(executable, { quiet: true })).trim(), - contentType: 'application/zip', - }; - } -} - -enum BucketOwnership { - DOES_NOT_EXIST, - MINE, - SOMEONE_ELSES_OR_NO_ACCESS, -} - -type BucketEncryption = - | { readonly type: 'no_encryption' } - | { readonly type: 'aes256' } - | { readonly type: 'kms'; readonly kmsKeyId?: string } - | { readonly type: 'access_denied' } - | { readonly type: 'does_not_exist' } - ; - -async function objectExists(s3: AWS.S3, bucket: string, key: string) { - /* - * The object existence check here refrains from using the `headObject` operation because this - * would create a negative cache entry, making GET-after-PUT eventually consistent. This has been - * observed to result in CloudFormation issuing "ValidationError: S3 error: Access Denied", for - * example in https://github.com/aws/aws-cdk/issues/6430. - * - * To prevent this, we are instead using the listObjectsV2 call, using the looked up key as the - * prefix, and limiting results to 1. Since the list operation returns keys ordered by binary - * UTF-8 representation, the key we are looking for is guaranteed to always be the first match - * returned if it exists. - * - * If the file is too small, we discount it as a cache hit. There is an issue - * somewhere that sometimes produces empty zip files, and we would otherwise - * never retry building those assets without users having to manually clear - * their bucket, which is a bad experience. - */ - const response = await s3.listObjectsV2({ Bucket: bucket, Prefix: key, MaxKeys: 1 }).promise(); - return ( - response.Contents != null && - response.Contents.some( - (object) => object.Key === key && (object.Size == null || object.Size > EMPTY_ZIP_FILE_SIZE), - ) - ); -} - -/** - * A packaged asset which can be uploaded (either a single file or directory) - */ -interface PackagedFileAsset { - /** - * Path of the file or directory - */ - readonly packagedPath: string; - - /** - * Content type to be added in the S3 upload action - * - * @default - No content type - */ - readonly contentType?: string; -} - -/** - * Cache for bucket information, so we don't have to keep doing the same calls again and again - * - * We scope the lifetime of the cache to the lifetime of the host, so that we don't have to do - * anything special for tests and yet the cache will live for the entire lifetime of the asset - * upload session when used by the CLI. - */ -class BucketInformation { - public static for(host: IHandlerHost) { - const existing = BucketInformation.caches.get(host); - if (existing) { return existing; } - - const fresh = new BucketInformation(); - BucketInformation.caches.set(host, fresh); - return fresh; - } - - private static readonly caches = new WeakMap(); - - private readonly ownerships = new Map(); - private readonly encryptions = new Map(); - - private constructor() { - } - - public async bucketOwnership(s3: AWS.S3, bucket: string): Promise { - return cached(this.ownerships, bucket, () => this._bucketOwnership(s3, bucket)); - } - - public async bucketEncryption(s3: AWS.S3, bucket: string): Promise { - return cached(this.encryptions, bucket, () => this._bucketEncryption(s3, bucket)); - } - - private async _bucketOwnership(s3: AWS.S3, bucket: string): Promise { - try { - await s3.getBucketLocation({ Bucket: bucket }).promise(); - return BucketOwnership.MINE; - } catch (e: any) { - if (e.code === 'NoSuchBucket') { return BucketOwnership.DOES_NOT_EXIST; } - if (['AccessDenied', 'AllAccessDisabled'].includes(e.code)) { return BucketOwnership.SOMEONE_ELSES_OR_NO_ACCESS; } - throw e; - } - } - - private async _bucketEncryption(s3: AWS.S3, bucket: string): Promise { - try { - const encryption = await s3.getBucketEncryption({ Bucket: bucket }).promise(); - const l = encryption?.ServerSideEncryptionConfiguration?.Rules?.length ?? 0; - if (l > 0) { - const apply = encryption?.ServerSideEncryptionConfiguration?.Rules[0]?.ApplyServerSideEncryptionByDefault; - let ssealgo = apply?.SSEAlgorithm; - if (ssealgo === 'AES256') return { type: 'aes256' }; - if (ssealgo === 'aws:kms') return { type: 'kms', kmsKeyId: apply?.KMSMasterKeyID }; - } - return { type: 'no_encryption' }; - } catch (e: any) { - if (e.code === 'NoSuchBucket') { - return { type: 'does_not_exist' }; - } - if (e.code === 'ServerSideEncryptionConfigurationNotFoundError') { - return { type: 'no_encryption' }; - } - - if (['AccessDenied', 'AllAccessDisabled'].includes(e.code)) { - return { type: 'access_denied' }; - } - return { type: 'no_encryption' }; - } - } -} - -async function cached(cache: Map, key: A, factory: (x: A) => Promise): Promise { - if (cache.has(key)) { - return cache.get(key)!; - } - - const fresh = await factory(key); - cache.set(key, fresh); - return fresh; -} diff --git a/packages/cdk-assets/lib/private/handlers/index.ts b/packages/cdk-assets/lib/private/handlers/index.ts deleted file mode 100644 index 2b3c767eb4963..0000000000000 --- a/packages/cdk-assets/lib/private/handlers/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ContainerImageAssetHandler } from './container-images'; -import { FileAssetHandler } from './files'; -import { AssetManifest, DockerImageManifestEntry, FileManifestEntry, IManifestEntry } from '../../asset-manifest'; -import { IAssetHandler, IHandlerHost, IHandlerOptions } from '../asset-handler'; - -export function makeAssetHandler(manifest: AssetManifest, asset: IManifestEntry, host: IHandlerHost, options: IHandlerOptions): IAssetHandler { - if (asset instanceof FileManifestEntry) { - return new FileAssetHandler(manifest.directory, asset, host); - } - if (asset instanceof DockerImageManifestEntry) { - return new ContainerImageAssetHandler(manifest.directory, asset, host, options); - } - - throw new Error(`Unrecognized asset type: '${asset}'`); -} diff --git a/packages/cdk-assets/lib/private/placeholders.ts b/packages/cdk-assets/lib/private/placeholders.ts deleted file mode 100644 index 50f76dfd3a7a6..0000000000000 --- a/packages/cdk-assets/lib/private/placeholders.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { EnvironmentPlaceholders } from '@aws-cdk/cx-api'; -import { IAws } from '../aws'; - -/** - * Replace the {ACCOUNT} and {REGION} placeholders in all strings found in a complex object. - * - * Duplicated between cdk-assets and aws-cdk CLI because we don't have a good single place to put it - * (they're nominally independent tools). - */ -export async function replaceAwsPlaceholders(object: A, aws: IAws): Promise { - let partition = async () => { - const p = await aws.discoverPartition(); - partition = () => Promise.resolve(p); - return p; - }; - - let account = async () => { - const a = await aws.discoverCurrentAccount(); - account = () => Promise.resolve(a); - return a; - }; - - return EnvironmentPlaceholders.replaceAsync(object, { - async region() { - return object.region ?? aws.discoverDefaultRegion(); - }, - async accountId() { - return (await account()).accountId; - }, - async partition() { - return partition(); - }, - }); -} \ No newline at end of file diff --git a/packages/cdk-assets/lib/private/shell.ts b/packages/cdk-assets/lib/private/shell.ts deleted file mode 100644 index 5e27452f98bab..0000000000000 --- a/packages/cdk-assets/lib/private/shell.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as child_process from 'child_process'; - -export type Logger = (x: string) => void; - -export interface ShellOptions extends child_process.SpawnOptions { - readonly quiet?: boolean; - readonly logger?: Logger; - readonly input?: string; -} - -/** - * OS helpers - * - * Shell function which both prints to stdout and collects the output into a - * string. - */ -export async function shell(command: string[], options: ShellOptions = {}): Promise { - if (options.logger) { - options.logger(renderCommandLine(command)); - } - const child = child_process.spawn(command[0], command.slice(1), { - ...options, - stdio: [options.input ? 'pipe' : 'ignore', 'pipe', 'pipe'], - }); - - return new Promise((resolve, reject) => { - if (options.input) { - child.stdin!.write(options.input); - child.stdin!.end(); - } - - const stdout = new Array(); - const stderr = new Array(); - - // Both write to stdout and collect - child.stdout!.on('data', chunk => { - if (!options.quiet) { - process.stdout.write(chunk); - } - stdout.push(chunk); - }); - - child.stderr!.on('data', chunk => { - if (!options.quiet) { - process.stderr.write(chunk); - } - - stderr.push(chunk); - }); - - child.once('error', reject); - - child.once('close', (code, signal) => { - if (code === 0) { - resolve(Buffer.concat(stdout).toString('utf-8')); - } else { - const out = Buffer.concat(stderr).toString('utf-8').trim(); - reject(new ProcessFailed(code, signal, `${renderCommandLine(command)} exited with ${code != null ? 'error code' : 'signal'} ${code ?? signal}: ${out}`)); - } - }); - }); -} - -export type ProcessFailedError = ProcessFailed - -class ProcessFailed extends Error { - public readonly code = 'PROCESS_FAILED'; - - constructor(public readonly exitCode: number | null, public readonly signal: NodeJS.Signals | null, message: string) { - super(message); - } -} - -/** - * Render the given command line as a string - * - * Probably missing some cases but giving it a good effort. - */ -function renderCommandLine(cmd: string[]) { - if (process.platform !== 'win32') { - return doRender(cmd, hasAnyChars(' ', '\\', '!', '"', "'", '&', '$'), posixEscape); - } else { - return doRender(cmd, hasAnyChars(' ', '"', '&', '^', '%'), windowsEscape); - } -} - -/** - * Render a UNIX command line - */ -function doRender(cmd: string[], needsEscaping: (x: string) => boolean, doEscape: (x: string) => string): string { - return cmd.map(x => needsEscaping(x) ? doEscape(x) : x).join(' '); -} - -/** - * Return a predicate that checks if a string has any of the indicated chars in it - */ -function hasAnyChars(...chars: string[]): (x: string) => boolean { - return (str: string) => { - return chars.some(c => str.indexOf(c) !== -1); - }; -} - -/** - * Escape a shell argument for POSIX shells - * - * Wrapping in single quotes and escaping single quotes inside will do it for us. - */ -function posixEscape(x: string) { - // Turn ' -> '"'"' - x = x.replace(/'/g, "'\"'\"'"); - return `'${x}'`; -} - -/** - * Escape a shell argument for cmd.exe - * - * This is how to do it right, but I'm not following everything: - * - * https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ - */ -function windowsEscape(x: string): string { - // First surround by double quotes, ignore the part about backslashes - x = `"${x}"`; - // Now escape all special characters - const shellMeta = new Set(['"', '&', '^', '%']); - return x.split('').map(c => shellMeta.has(x) ? '^' + c : c).join(''); -} diff --git a/packages/cdk-assets/lib/private/util.ts b/packages/cdk-assets/lib/private/util.ts deleted file mode 100644 index 88a87a18e6ba9..0000000000000 --- a/packages/cdk-assets/lib/private/util.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Creates a critical section, ensuring that at most one function can - * enter the critical section at a time. - */ -export function createCriticalSection() { - let lock = Promise.resolve(); - return async (criticalFunction: () => Promise) => { - const res = lock.then(() => criticalFunction()); - lock = res.catch(e => e); - return res; - }; -}; \ No newline at end of file diff --git a/packages/cdk-assets/lib/progress.ts b/packages/cdk-assets/lib/progress.ts deleted file mode 100644 index b2c8e77ddad78..0000000000000 --- a/packages/cdk-assets/lib/progress.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { IManifestEntry } from './asset-manifest'; - -/** - * A listener for progress events from the publisher - */ -export interface IPublishProgressListener { - /** - * Asset build event - */ - onPublishEvent(type: EventType, event: IPublishProgress): void; -} - -/** - * A single event for an asset - */ -export enum EventType { - /** - * Just starting on an asset - */ - START = 'start', - - /** - * When an asset is successfully finished - */ - SUCCESS = 'success', - - /** - * When an asset failed - */ - FAIL = 'fail', - - /** - * Checking whether an asset has already been published - */ - CHECK = 'check', - - /** - * The asset was already published - */ - FOUND = 'found', - - /** - * The asset was reused locally from a cached version - */ - CACHED = 'cached', - - /** - * The asset will be built - */ - BUILD = 'build', - - /** - * The asset will be uploaded - */ - UPLOAD = 'upload', - - /** - * Another type of detail message - */ - DEBUG = 'debug', -} - -/** - * Context object for publishing progress - */ -export interface IPublishProgress { - /** - * Current event message - */ - readonly message: string; - - /** - * Asset currently being packaged (if any) - */ - readonly currentAsset?: IManifestEntry; - - /** - * How far along are we? - */ - readonly percentComplete: number; - - /** - * Abort the current publishing operation - */ - abort(): void; -} diff --git a/packages/cdk-assets/lib/publishing.ts b/packages/cdk-assets/lib/publishing.ts deleted file mode 100644 index 03cf2683f3293..0000000000000 --- a/packages/cdk-assets/lib/publishing.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { AssetManifest, IManifestEntry } from './asset-manifest'; -import { IAws } from './aws'; -import { IAssetHandler, IHandlerHost } from './private/asset-handler'; -import { DockerFactory } from './private/docker'; -import { makeAssetHandler } from './private/handlers'; -import { EventType, IPublishProgress, IPublishProgressListener } from './progress'; - -export interface AssetPublishingOptions { - /** - * Entry point for AWS client - */ - readonly aws: IAws; - - /** - * Listener for progress events - * - * @default No listener - */ - readonly progressListener?: IPublishProgressListener; - - /** - * Whether to throw at the end if there were errors - * - * @default true - */ - readonly throwOnError?: boolean; - - /** - * Whether to publish in parallel, when 'publish()' is called - * - * @default false - */ - readonly publishInParallel?: boolean; - - /** - * Whether to build assets, when 'publish()' is called - * - * @default true - */ - readonly buildAssets?: boolean; - - /** - * Whether to publish assets, when 'publish()' is called - * - * @default true - */ - readonly publishAssets?: boolean; - - /** - * Whether to print publishing logs - * - * @default true - */ - readonly quiet?: boolean; -} - -/** - * A failure to publish an asset - */ -export interface FailedAsset { - /** - * The asset that failed to publish - */ - readonly asset: IManifestEntry; - - /** - * The failure that occurred - */ - readonly error: Error; -} - -export class AssetPublishing implements IPublishProgress { - /** - * The message for the IPublishProgress interface - */ - public message: string = 'Starting'; - - /** - * The current asset for the IPublishProgress interface - */ - public currentAsset?: IManifestEntry; - public readonly failures = new Array(); - private readonly assets: IManifestEntry[]; - - private readonly totalOperations: number; - private completedOperations: number = 0; - private aborted = false; - private readonly handlerHost: IHandlerHost; - private readonly publishInParallel: boolean; - private readonly buildAssets: boolean; - private readonly publishAssets: boolean; - private readonly handlerCache = new Map(); - - constructor(private readonly manifest: AssetManifest, private readonly options: AssetPublishingOptions) { - this.assets = manifest.entries; - this.totalOperations = this.assets.length; - this.publishInParallel = options.publishInParallel ?? false; - this.buildAssets = options.buildAssets ?? true; - this.publishAssets = options.publishAssets ?? true; - - const self = this; - this.handlerHost = { - aws: this.options.aws, - get aborted() { return self.aborted; }, - emitMessage(t, m) { self.progressEvent(t, m); }, - dockerFactory: new DockerFactory(), - }; - } - - /** - * Publish all assets from the manifest - */ - public async publish(): Promise { - if (this.publishInParallel) { - await Promise.all(this.assets.map(async (asset) => this.publishAsset(asset))); - } else { - for (const asset of this.assets) { - if (!await this.publishAsset(asset)) { - break; - } - } - } - - if ((this.options.throwOnError ?? true) && this.failures.length > 0) { - throw new Error(`Error publishing: ${this.failures.map(e => e.error.message)}`); - } - } - - /** - * Build a single asset from the manifest - */ - public async buildEntry(asset: IManifestEntry) { - try { - if (this.progressEvent(EventType.START, `Building ${asset.id}`)) { return false; } - - const handler = this.assetHandler(asset); - await handler.build(); - - if (this.aborted) { - throw new Error('Aborted'); - } - - this.completedOperations++; - if (this.progressEvent(EventType.SUCCESS, `Built ${asset.id}`)) { return false; } - } catch (e: any) { - this.failures.push({ asset, error: e }); - this.completedOperations++; - if (this.progressEvent(EventType.FAIL, e.message)) { return false; } - } - - return true; - } - - /** - * Publish a single asset from the manifest - */ - public async publishEntry(asset: IManifestEntry) { - try { - if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) { return false; } - - const handler = this.assetHandler(asset); - await handler.publish(); - - if (this.aborted) { - throw new Error('Aborted'); - } - - this.completedOperations++; - if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) { return false; } - } catch (e: any) { - this.failures.push({ asset, error: e }); - this.completedOperations++; - if (this.progressEvent(EventType.FAIL, e.message)) { return false; } - } - - return true; - } - - /** - * Return whether a single asset is published - */ - public isEntryPublished(asset: IManifestEntry) { - const handler = this.assetHandler(asset); - return handler.isPublished(); - } - - /** - * publish an asset (used by 'publish()') - * @param asset The asset to publish - * @returns false when publishing should stop - */ - private async publishAsset(asset: IManifestEntry) { - try { - if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) { return false; } - - const handler = this.assetHandler(asset); - - if (this.buildAssets) { - await handler.build(); - } - - if (this.publishAssets) { - await handler.publish(); - } - - if (this.aborted) { - throw new Error('Aborted'); - } - - this.completedOperations++; - if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) { return false; } - } catch (e: any) { - this.failures.push({ asset, error: e }); - this.completedOperations++; - if (this.progressEvent(EventType.FAIL, e.message)) { return false; } - } - - return true; - } - - public get percentComplete() { - if (this.totalOperations === 0) { return 100; } - return Math.floor((this.completedOperations / this.totalOperations) * 100); - } - - public abort(): void { - this.aborted = true; - } - - public get hasFailures() { - return this.failures.length > 0; - } - - /** - * Publish a progress event to the listener, if present. - * - * Returns whether an abort is requested. Helper to get rid of repetitive code in publish(). - */ - private progressEvent(event: EventType, message: string): boolean { - this.message = message; - if (this.options.progressListener) { this.options.progressListener.onPublishEvent(event, this); } - return this.aborted; - } - - private assetHandler(asset: IManifestEntry) { - const existing = this.handlerCache.get(asset); - if (existing) { - return existing; - } - const ret = makeAssetHandler(this.manifest, asset, this.handlerHost, { - quiet: this.options.quiet, - }); - this.handlerCache.set(asset, ret); - return ret; - } -} diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json deleted file mode 100644 index e07450f12ec3a..0000000000000 --- a/packages/cdk-assets/package.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "cdk-assets", - "description": "CDK Asset Publishing Tool", - "version": "0.0.0", - "main": "lib/index.js", - "types": "lib/index.d.ts", - "bin": { - "cdk-assets": "bin/cdk-assets", - "docker-credential-cdk-assets": "bin/docker-credential-cdk-assets" - }, - "scripts": { - "build": "cdk-build", - "integ": "integ-runner --language javascript", - "lint": "cdk-lint", - "package": "cdk-package", - "awslint": "cdk-awslint", - "pkglint": "pkglint -f", - "test": "cdk-test", - "watch": "cdk-watch", - "build+test": "yarn build && yarn test", - "build+test+package": "yarn build+test && yarn package", - "compat": "cdk-compat", - "build+extract": "yarn build", - "build+test+extract": "yarn build+test" - }, - "author": { - "name": "Amazon Web Services", - "url": "https://aws.amazon.com", - "organization": true - }, - "license": "Apache-2.0", - "devDependencies": { - "@types/archiver": "^5.3.4", - "@types/glob": "^7.2.0", - "@types/jest": "^29.5.12", - "@types/mime": "^2.0.3", - "@types/mock-fs": "^4.13.4", - "@types/yargs": "^15.0.19", - "@aws-cdk/cdk-build-tools": "0.0.0", - "jest": "^29.7.0", - "jszip": "^3.10.1", - "mock-fs": "^4.14.0", - "@aws-cdk/pkglint": "0.0.0" - }, - "dependencies": { - "@aws-cdk/cloud-assembly-schema": "0.0.0", - "@aws-cdk/cx-api": "0.0.0", - "archiver": "^5.3.2", - "aws-sdk": "^2.1653.0", - "glob": "^7.2.3", - "mime": "^2.6.0", - "yargs": "^16.2.0" - }, - "repository": { - "url": "https://github.com/aws/aws-cdk.git", - "type": "git", - "directory": "packages/cdk-assets" - }, - "keywords": [ - "aws", - "cdk" - ], - "homepage": "https://github.com/aws/aws-cdk", - "engines": { - "node": ">= 14.15.0" - }, - "cdk-package": { - "shrinkWrap": true - }, - "nozem": { - "ostools": [ - "unzip", - "diff", - "rm" - ] - }, - "stability": "stable", - "maturity": "stable", - "publishConfig": { - "tag": "latest" - } -} diff --git a/packages/cdk-assets/test/archive.test.ts b/packages/cdk-assets/test/archive.test.ts deleted file mode 100644 index d0fe1e2b3dbe7..0000000000000 --- a/packages/cdk-assets/test/archive.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { exec as _exec } from 'child_process'; -import * as crypto from 'crypto'; -import { constants, exists, promises as fs } from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { promisify } from 'util'; -import * as jszip from 'jszip'; -import { zipDirectory } from '../lib/private/archive'; -import { rmRfSync } from '../lib/private/fs-extra'; -const exec = promisify(_exec); -const pathExists = promisify(exists); - -function logger(x: string) { - // eslint-disable-next-line no-console - console.log(x); -} - -test('zipDirectory can take a directory and produce a zip from it', async () => { - const stagingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test.archive')); - const extractDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test.archive.extract')); - try { - const zipFile = path.join(stagingDir, 'output.zip'); - const originalDir = path.join(__dirname, 'test-archive'); - await zipDirectory(originalDir, zipFile, logger); - - // unzip and verify that the resulting tree is the same - await exec(`unzip ${zipFile}`, { cwd: extractDir }); - - await expect(exec(`diff -bur ${originalDir} ${extractDir}`)).resolves.toBeTruthy(); - - // inspect the zip file to check that dates are reset - const zip = await fs.readFile(zipFile); - const zipData = await jszip.loadAsync(zip); - const dates = Object.values(zipData.files).map(file => file.date.toISOString()); - expect(dates[0]).toBe('1980-01-01T00:00:00.000Z'); - expect(new Set(dates).size).toBe(1); - - // check that mode is preserved - const stat = await fs.stat(path.join(extractDir, 'executable.txt')); - // eslint-disable-next-line no-bitwise - const isExec = (stat.mode & constants.S_IXUSR) || (stat.mode & constants.S_IXGRP) || (stat.mode & constants.S_IXOTH); - expect(isExec).toBeTruthy(); - } finally { - rmRfSync(stagingDir); - rmRfSync(extractDir); - } -}); - -test('md5 hash of a zip stays consistent across invocations', async () => { - const stagingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test.archive')); - const zipFile1 = path.join(stagingDir, 'output.zip'); - const zipFile2 = path.join(stagingDir, 'output.zip'); - const originalDir = path.join(__dirname, 'test-archive'); - await zipDirectory(originalDir, zipFile1, logger); - await new Promise(ok => setTimeout(ok, 2000)); // wait 2s - await zipDirectory(originalDir, zipFile2, logger); - - const hash1 = contentHash(await fs.readFile(zipFile1)); - const hash2 = contentHash(await fs.readFile(zipFile2)); - - expect(hash1).toEqual(hash2); -}); - -test('zipDirectory follows symlinks', async () => { - const stagingDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test.archive')); - const extractDir = await fs.mkdtemp(path.join(os.tmpdir(), 'test.archive.follow')); - try { - // First MAKE the symlink we're going to follow. We can't check it into git, because - // CodeBuild/CodePipeline (I forget which) is going to replace symlinks with a textual - // representation of its target upon checkout, for security reasons. So, to make sure - // the symlink exists, we need to create it at build time. - const symlinkPath = path.join(__dirname, 'test-archive-follow', 'data', 'linked'); - const symlinkTarget = '../linked'; - - if (await pathExists(symlinkPath)) { - await fs.unlink(symlinkPath); - } - await fs.symlink(symlinkTarget, symlinkPath, 'dir'); - - const originalDir = path.join(__dirname, 'test-archive-follow', 'data'); - const zipFile = path.join(stagingDir, 'output.zip'); - - await expect(zipDirectory(originalDir, zipFile, logger)).resolves.toBeUndefined(); - await expect(exec(`unzip ${zipFile}`, { cwd: extractDir })).resolves.toBeDefined(); - await expect(exec(`diff -bur ${originalDir} ${extractDir}`)).resolves.toBeDefined(); - } finally { - rmRfSync(stagingDir); - rmRfSync(extractDir); - } -}); - -function contentHash(data: string | Buffer | DataView) { - return crypto.createHash('sha256').update(data).digest('hex'); -} diff --git a/packages/cdk-assets/test/docker-images.test.ts b/packages/cdk-assets/test/docker-images.test.ts deleted file mode 100644 index c396853576c3e..0000000000000 --- a/packages/cdk-assets/test/docker-images.test.ts +++ /dev/null @@ -1,706 +0,0 @@ -jest.mock('child_process'); - -import * as fs from 'fs'; -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import * as mockfs from 'mock-fs'; -import { mockAws, mockedApiFailure, mockedApiResult } from './mock-aws'; -import { mockSpawn } from './mock-child_process'; -import { AssetManifest, AssetPublishing } from '../lib'; -import * as dockercreds from '../lib/private/docker-credentials'; - -let aws: ReturnType; -const absoluteDockerPath = '/simple/cdk.out/dockerdir'; -beforeEach(() => { - jest.resetAllMocks(); - delete(process.env.CDK_DOCKER); - - // By default, assume no externally-configured credentials. - jest.spyOn(dockercreds, 'cdkCredentialsConfig').mockReturnValue(undefined); - - mockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'abcdef', - }, - }, - }, - }, - }), - '/multi/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset1: { - source: { - directory: 'dockerdir', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'theAsset1', - }, - }, - }, - theAsset2: { - source: { - directory: 'dockerdir', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'theAsset2', - }, - }, - }, - }, - }), - '/external/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theExternalAsset: { - source: { - executable: ['sometool'], - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'ghijkl', - }, - }, - }, - }, - }), - '/simple/cdk.out/dockerdir/Dockerfile': 'FROM scratch', - '/abs/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: absoluteDockerPath, - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'abcdef', - }, - }, - }, - }, - }), - '/default-network/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - networkMode: 'default', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/default-network/cdk.out/dockerdir/Dockerfile': 'FROM scratch', - '/platform-arm64/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - platform: 'linux/arm64', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/cache/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - cacheFrom: [{ type: 'registry', params: { ref: 'abcdef' } }], - cacheTo: { type: 'inline' }, - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/cache-from-multiple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - cacheFrom: [ - { type: 'registry', params: { ref: 'cache:ref' } }, - { type: 'registry', params: { ref: 'cache:main' } }, - { type: 'gha' }, - ], - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/cache-to-complex/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - cacheTo: { type: 'registry', params: { ref: 'cache:main', mode: 'max', compression: 'zstd' } }, - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/nocache/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - dockerImages: { - theAsset: { - source: { - directory: 'dockerdir', - cacheDisabled: true, - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - repositoryName: 'repo', - imageTag: 'nopqr', - }, - }, - }, - }, - }), - '/platform-arm64/cdk.out/dockerdir/Dockerfile': 'FROM scratch', - }); - - aws = mockAws(); -}); - -afterEach(() => { - mockfs.restore(); -}); - -test('pass destination properties to AWS client', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, throwOnError: false }); - - await pub.publish(); - - expect(aws.ecrClient).toHaveBeenCalledWith(expect.objectContaining({ - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - })); -}); - -describe('with a complete manifest', () => { - let pub: AssetPublishing; - beforeEach(() => { - pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - }); - - test('Do nothing if docker image already exists', async () => { - aws.mockEcr.describeImages = mockedApiResult({ /* No error == image exists */ }); - - await pub.publish(); - - expect(aws.mockEcr.describeImages).toHaveBeenCalledWith(expect.objectContaining({ - imageIds: [{ imageTag: 'abcdef' }], - repositoryName: 'repo', - })); - }); - - test('Displays an error if the ECR repository cannot be found', async () => { - aws.mockEcr.describeImages = mockedApiFailure('RepositoryNotFoundException', 'Repository not Found'); - - await expect(pub.publish()).rejects.toThrow('Error publishing: Repository not Found'); - }); - - test('successful run does not need to query account ID', async () => { - aws.mockEcr.describeImages = mockedApiResult({ /* No error == image exists */ }); - await pub.publish(); - expect(aws.discoverCurrentAccount).not.toHaveBeenCalled(); - }); - - test('upload docker image if not uploaded yet but exists locally', async () => { - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'] }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:abcdef'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:abcdef'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build and upload docker image if not exists anywhere', async () => { - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '.'], cwd: absoluteDockerPath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:abcdef'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:abcdef'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with networkMode option', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/default-network/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/default-network/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '--network', 'default', '.'], cwd: defaultNetworkDockerpath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with platform option', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/platform-arm64/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/platform-arm64/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '--platform', 'linux/arm64', '.'], cwd: defaultNetworkDockerpath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with cache option', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/cache/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/cache/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '--cache-from', 'type=registry,ref=abcdef', '--cache-to', 'type=inline', '.'], cwd: defaultNetworkDockerpath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with cache disabled', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/nocache/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/nocache/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '--no-cache', '.'], cwd: defaultNetworkDockerpath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with multiple cache from option', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/cache-from-multiple/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/cache-from-multiple/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { - commandLine: [ - 'docker', 'build', '--tag', 'cdkasset-theasset', '--cache-from', 'type=registry,ref=cache:ref', '--cache-from', 'type=registry,ref=cache:main', '--cache-from', 'type=gha', '.', - ], - cwd: defaultNetworkDockerpath, - }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); - - test('build with cache to complex option', async () => { - pub = new AssetPublishing(AssetManifest.fromPath('/cache-to-complex/cdk.out'), { aws }); - const defaultNetworkDockerpath = '/cache-to-complex/cdk.out/dockerdir'; - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '--cache-to', 'type=registry,ref=cache:main,mode=max,compression=zstd', '.'], cwd: defaultNetworkDockerpath }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:nopqr'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:nopqr'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - }); -}); - -describe('external assets', () => { - let pub: AssetPublishing; - const externalTag = 'external:tag'; - beforeEach(() => { - pub = new AssetPublishing(AssetManifest.fromPath('/external/cdk.out'), { aws }); - }); - - test('upload externally generated Docker image', async () => { - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['sometool'], stdout: externalTag, cwd: '/external/cdk.out' }, - { commandLine: ['docker', 'tag', externalTag, '12345.amazonaws.com/repo:ghijkl'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:ghijkl'] }, - ); - - await pub.publish(); - - expect(aws.ecrClient).toHaveBeenCalledWith(expect.objectContaining({ - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - })); - expectAllSpawns(); - }); -}); - -test('correctly identify Docker directory if path is absolute', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/abs/cdk.out'), { aws }); - - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - // Only care about the 'build' command line - { commandLine: ['docker', 'login'], prefix: true }, - { commandLine: ['docker', 'inspect'], exitCode: 1, prefix: true }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset', '.'], cwd: absoluteDockerPath }, - { commandLine: ['docker', 'tag'], prefix: true }, - { commandLine: ['docker', 'push'], prefix: true }, - ); - - await pub.publish(); - - expect(true).toBeTruthy(); // Expect no exception, satisfy linter - expectAllSpawns(); -}); - -test('when external credentials are present, explicit Docker config directories are used', async () => { - // Setup -- Mock that we have CDK credentials, and mock fs operations. - jest.spyOn(dockercreds, 'cdkCredentialsConfig').mockReturnValue({ version: '0.1', domainCredentials: {} }); - jest.spyOn(fs, 'mkdtempSync').mockImplementationOnce(() => '/tmp/mockedTempDir'); - jest.spyOn(fs, 'writeFileSync').mockImplementation(jest.fn()); - - let pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - // Initally use the first created directory with the CDK credentials - { commandLine: ['docker', '--config', '/tmp/mockedTempDir', 'inspect', 'cdkasset-theasset'], exitCode: 1 }, - { commandLine: ['docker', '--config', '/tmp/mockedTempDir', 'build', '--tag', 'cdkasset-theasset', '.'], cwd: absoluteDockerPath }, - { commandLine: ['docker', '--config', '/tmp/mockedTempDir', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:abcdef'] }, - // Prior to push, revert to the default config directory - { commandLine: ['docker', 'login'], prefix: true }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:abcdef'] }, - ); - - await pub.publish(); - - expectAllSpawns(); -}); - -test('logging in only once for two assets', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/multi/cdk.out'), { aws, throwOnError: false }); - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset1'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset1', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset1', '12345.amazonaws.com/repo:theAsset1'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:theAsset1'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset2'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset2', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset2', '12345.amazonaws.com/repo:theAsset2'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/repo:theAsset2'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter -}); - -test('logging in twice for two repository domains (containing account id & region)', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/multi/cdk.out'), { aws, throwOnError: false }); - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - - let repoIdx = 12345; - aws.mockEcr.describeRepositories = jest.fn().mockReturnValue({ - promise: jest.fn().mockImplementation(() => Promise.resolve({ - repositories: [ - // Usually looks like: 012345678910.dkr.ecr.us-west-2.amazonaws.com/aws-cdk/assets - { repositoryUri: `${repoIdx++}.amazonaws.com/aws-cdk/assets` }, - ], - })), - }); - - let proxyIdx = 12345; - aws.mockEcr.getAuthorizationToken = jest.fn().mockReturnValue({ - promise: jest.fn().mockImplementation(() => Promise.resolve({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: `https://${proxyIdx++}.proxy.com/` }, - ], - })), - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://12345.proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset1'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset1', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset1', '12345.amazonaws.com/aws-cdk/assets:theAsset1'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/aws-cdk/assets:theAsset1'] }, - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://12346.proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset2'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset2', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset2', '12346.amazonaws.com/aws-cdk/assets:theAsset2'] }, - { commandLine: ['docker', 'push', '12346.amazonaws.com/aws-cdk/assets:theAsset2'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter -}); - -test('building only', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/multi/cdk.out'), { - aws, - throwOnError: false, - buildAssets: true, - publishAssets: false, - }); - - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset1'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset1', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset1', '12345.amazonaws.com/repo:theAsset1'] }, - { commandLine: ['docker', 'inspect', 'cdkasset-theasset2'], exitCode: 1 }, - { commandLine: ['docker', 'build', '--tag', 'cdkasset-theasset2', '.'], cwd: '/multi/cdk.out/dockerdir' }, - { commandLine: ['docker', 'tag', 'cdkasset-theasset2', '12345.amazonaws.com/repo:theAsset2'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter -}); - -test('publishing only', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/multi/cdk.out'), { - aws, - throwOnError: false, - buildAssets: false, - publishAssets: true, - }); - - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['docker', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/aws-cdk/assets:theAsset1'] }, - { commandLine: ['docker', 'push', '12345.amazonaws.com/aws-cdk/assets:theAsset2'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter -}); - -test('overriding the docker command', async () => { - process.env.CDK_DOCKER = 'custom'; - - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, throwOnError: false }); - - aws.mockEcr.describeImages = mockedApiFailure('ImageNotFoundException', 'File does not exist'); - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { authorizationToken: 'dXNlcjpwYXNz', proxyEndpoint: 'https://proxy.com/' }, - ], - }); - - const expectAllSpawns = mockSpawn( - { commandLine: ['custom', 'login', '--username', 'user', '--password-stdin', 'https://proxy.com/'] }, - { commandLine: ['custom', 'inspect', 'cdkasset-theasset'] }, - { commandLine: ['custom', 'tag', 'cdkasset-theasset', '12345.amazonaws.com/repo:abcdef'] }, - { commandLine: ['custom', 'push', '12345.amazonaws.com/repo:abcdef'] }, - ); - - await pub.publish(); - - expectAllSpawns(); - expect(true).toBeTruthy(); // Expect no exception, satisfy linter -}); diff --git a/packages/cdk-assets/test/fake-listener.ts b/packages/cdk-assets/test/fake-listener.ts deleted file mode 100644 index 7aef7fbe9d9f6..0000000000000 --- a/packages/cdk-assets/test/fake-listener.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IPublishProgressListener, EventType, IPublishProgress } from '../lib/progress'; - -export class FakeListener implements IPublishProgressListener { - public readonly types = new Array(); - public readonly messages = new Array(); - - constructor(private readonly doAbort = false) { - } - - public onPublishEvent(_type: EventType, event: IPublishProgress): void { - this.messages.push(event.message); - - if (this.doAbort) { - event.abort(); - } - } -} \ No newline at end of file diff --git a/packages/cdk-assets/test/files.test.ts b/packages/cdk-assets/test/files.test.ts deleted file mode 100644 index 83af51717bea6..0000000000000 --- a/packages/cdk-assets/test/files.test.ts +++ /dev/null @@ -1,344 +0,0 @@ -jest.mock('child_process'); - -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import * as mockfs from 'mock-fs'; -import { FakeListener } from './fake-listener'; -import { mockAws, mockedApiFailure, mockedApiResult, mockUpload } from './mock-aws'; -import { mockSpawn } from './mock-child_process'; -import { AssetPublishing, AssetManifest } from '../lib'; - -const ABS_PATH = '/simple/cdk.out/some_external_file'; - -const DEFAULT_DESTINATION = { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key', -}; - -let aws: ReturnType; -beforeEach(() => { - jest.resetAllMocks(); - - mockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theAsset: { - source: { - path: 'some_file', - }, - destinations: { theDestination: DEFAULT_DESTINATION }, - }, - }, - }), - '/simple/cdk.out/some_file': 'FILE_CONTENTS', - [ABS_PATH]: 'ZIP_FILE_THAT_IS_DEFINITELY_NOT_EMPTY', - '/abs/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theAsset: { - source: { - path: '/simple/cdk.out/some_file', - }, - destinations: { theDestination: { ...DEFAULT_DESTINATION, bucketName: 'some_other_bucket' } }, - }, - }, - }), - '/external/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - externalAsset: { - source: { - executable: ['sometool'], - }, - destinations: { theDestination: { ...DEFAULT_DESTINATION, bucketName: 'some_external_bucket' } }, - }, - }, - }), - '/types/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theTextAsset: { - source: { - path: 'plain_text.txt', - }, - destinations: { theDestination: { ...DEFAULT_DESTINATION, objectKey: 'some_key.txt' } }, - }, - theImageAsset: { - source: { - path: 'image.png', - }, - destinations: { theDestination: { ...DEFAULT_DESTINATION, objectKey: 'some_key.png' } }, - }, - }, - }), - '/types/cdk.out/plain_text.txt': 'FILE_CONTENTS', - '/types/cdk.out/image.png': 'FILE_CONTENTS', - '/emptyzip/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theTextAsset: { - source: { - path: 'empty_dir', - packaging: 'zip', - }, - destinations: { theDestination: DEFAULT_DESTINATION }, - }, - }, - }), - '/emptyzip/cdk.out/empty_dir': { }, // Empty directory - }); - - aws = mockAws(); -}); - -afterEach(() => { - mockfs.restore(); -}); - -test('pass destination properties to AWS client', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, throwOnError: false }); - aws.mockS3.listObjectsV2 = mockedApiResult({}); - - await pub.publish(); - - expect(aws.s3Client).toHaveBeenCalledWith(expect.objectContaining({ - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - })); -}); - -test('Do nothing if file already exists', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key' }] }); - aws.mockS3.upload = mockUpload(); - await pub.publish(); - - expect(aws.mockS3.listObjectsV2).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Prefix: 'some_key', - MaxKeys: 1, - })); - expect(aws.mockS3.upload).not.toHaveBeenCalled(); -}); - -test('tiny file does not count as cache hit', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key', Size: 5 }] }); - aws.mockS3.upload = mockUpload(); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalled(); -}); - -test('upload file if new (list returns other key)', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key', - ContentType: 'application/octet-stream', - })); - - // We'll just have to assume the contents are correct -}); - -test('upload with server side encryption AES256 header', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.getBucketEncryption = mockedApiResult({ - ServerSideEncryptionConfiguration: { - Rules: [ - { - ApplyServerSideEncryptionByDefault: { - SSEAlgorithm: 'AES256', - }, - BucketKeyEnabled: false, - }, - ], - }, - }); - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key', - ContentType: 'application/octet-stream', - ServerSideEncryption: 'AES256', - })); - - // We'll just have to assume the contents are correct -}); - -test('upload with server side encryption aws:kms header and key id', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.getBucketEncryption = mockedApiResult({ - ServerSideEncryptionConfiguration: { - Rules: [ - { - ApplyServerSideEncryptionByDefault: { - SSEAlgorithm: 'aws:kms', - KMSMasterKeyID: 'the-key-id', - }, - BucketKeyEnabled: false, - }, - ], - }, - }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key', - ContentType: 'application/octet-stream', - ServerSideEncryption: 'aws:kms', - SSEKMSKeyId: 'the-key-id', - })); - - // We'll just have to assume the contents are correct -}); - -test('will only read bucketEncryption once even for multiple assets', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/types/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledTimes(2); - expect(aws.mockS3.getBucketEncryption).toHaveBeenCalledTimes(1); -}); - -test('no server side encryption header if access denied for bucket encryption', async () => { - const progressListener = new FakeListener(); - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, progressListener }); - - aws.mockS3.getBucketEncryption = mockedApiFailure('AccessDenied', 'Access Denied'); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.not.objectContaining({ - ServerSideEncryption: 'aws:kms', - })); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.not.objectContaining({ - ServerSideEncryption: 'AES256', - })); -}); - -test('correctly looks up content type', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/types/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key.but_not_the_one' }] }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key.txt', - ContentType: 'text/plain', - })); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key.png', - ContentType: 'image/png', - })); - - // We'll just have to assume the contents are correct -}); - -test('upload file if new (list returns no key)', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key', - })); - - // We'll just have to assume the contents are correct -}); - -test('successful run does not need to query account ID', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(aws.discoverCurrentAccount).not.toHaveBeenCalled(); - expect(aws.discoverTargetAccount).not.toHaveBeenCalled(); -}); - -test('correctly identify asset path if path is absolute', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/abs/cdk.out'), { aws }); - - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload('FILE_CONTENTS'); - - await pub.publish(); - - expect(true).toBeTruthy(); // No exception, satisfy linter -}); - -describe('external assets', () => { - let pub: AssetPublishing; - beforeEach(() => { - pub = new AssetPublishing(AssetManifest.fromPath('/external/cdk.out'), { aws }); - }); - - test('do nothing if file exists already', async () => { - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key' }] }); - - await pub.publish(); - - expect(aws.mockS3.listObjectsV2).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_external_bucket', - Prefix: 'some_key', - MaxKeys: 1, - })); - }); - - test('upload external asset correctly', async () => { - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload('ZIP_FILE_THAT_IS_DEFINITELY_NOT_EMPTY'); - const expectAllSpawns = mockSpawn({ commandLine: ['sometool'], stdout: ABS_PATH }); - - await pub.publish(); - - expect(aws.s3Client).toHaveBeenCalledWith(expect.objectContaining({ - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - })); - - expectAllSpawns(); - }); -}); diff --git a/packages/cdk-assets/test/manifest.test.ts b/packages/cdk-assets/test/manifest.test.ts deleted file mode 100644 index 605d6922b5e08..0000000000000 --- a/packages/cdk-assets/test/manifest.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import * as mockfs from 'mock-fs'; -import { AssetManifest, DestinationIdentifier, DestinationPattern, DockerImageManifestEntry, FileManifestEntry } from '../lib'; - -beforeEach(() => { - mockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - asset1: { - type: 'file', - source: { path: 'S1' }, - destinations: { - dest1: { bucketName: 'D1', objectKey: 'X' }, - dest2: { bucketName: 'D2', objectKey: 'X' }, - }, - }, - }, - dockerImages: { - asset2: { - type: 'thing', - source: { directory: 'S2' }, - destinations: { - dest1: { repositoryName: 'D3', imageTag: 'X' }, - dest2: { repositoryName: 'D4', imageTag: 'X' }, - }, - }, - }, - }), - }); -}); - -afterEach(() => { - mockfs.restore(); -}); - -test('Can list manifest', () => { - const manifest = AssetManifest.fromPath('/simple/cdk.out'); - expect(manifest.list().join('\n')).toEqual(` -asset1 file {\"path\":\"S1\"} - ├ asset1:dest1 {\"bucketName\":\"D1\",\"objectKey\":\"X\"} - └ asset1:dest2 {\"bucketName\":\"D2\",\"objectKey\":\"X\"} -asset2 docker-image {\"directory\":\"S2\"} - ├ asset2:dest1 {\"repositoryName\":\"D3\",\"imageTag\":\"X\"} - └ asset2:dest2 {\"repositoryName\":\"D4\",\"imageTag\":\"X\"} -`.trim()); -}); - -test('.entries() iterates over all destinations', () => { - const manifest = AssetManifest.fromPath('/simple/cdk.out'); - - expect(manifest.entries).toEqual([ - new FileManifestEntry(new DestinationIdentifier('asset1', 'dest1'), { path: 'S1' }, { bucketName: 'D1', objectKey: 'X' }), - new FileManifestEntry(new DestinationIdentifier('asset1', 'dest2'), { path: 'S1' }, { bucketName: 'D2', objectKey: 'X' }), - new DockerImageManifestEntry(new DestinationIdentifier('asset2', 'dest1'), { directory: 'S2' }, { repositoryName: 'D3', imageTag: 'X' }), - new DockerImageManifestEntry(new DestinationIdentifier('asset2', 'dest2'), { directory: 'S2' }, { repositoryName: 'D4', imageTag: 'X' }), - ]); -}); - -test('can select by asset ID', () => { - const manifest = AssetManifest.fromPath('/simple/cdk.out'); - - const subset = manifest.select([DestinationPattern.parse('asset2')]); - - expect(subset.entries.map(e => f(e.genericDestination, 'repositoryName'))).toEqual(['D3', 'D4']); -}); - -test('can select by asset ID + destination ID', () => { - const manifest = AssetManifest.fromPath('/simple/cdk.out'); - - const subset = manifest.select([ - DestinationPattern.parse('asset1:dest1'), - DestinationPattern.parse('asset2:dest2'), - ]); - - expect(subset.entries.map(e => f(e.genericDestination, 'repositoryName', 'bucketName'))).toEqual(['D1', 'D4']); -}); - -test('can select by destination ID', () => { - const manifest = AssetManifest.fromPath('/simple/cdk.out'); - - const subset = manifest.select([ - DestinationPattern.parse(':dest1'), - ]); - - expect(subset.entries.map(e => f(e.genericDestination, 'repositoryName', 'bucketName'))).toEqual(['D1', 'D3']); -}); - -test('empty string is not a valid pattern', () => { - expect(() => { - DestinationPattern.parse(''); - }).toThrow(/Empty string is not a valid destination identifier/); -}); - -test('pattern must have two components', () => { - expect(() => { - DestinationPattern.parse('a:b:c'); - }).toThrow(/Asset identifier must contain at most 2/); -}); - -test('parse ASSET:* the same as ASSET and ASSET:', () => { - expect(DestinationPattern.parse('a:*')).toEqual(DestinationPattern.parse('a')); - expect(DestinationPattern.parse('a:*')).toEqual(DestinationPattern.parse('a:')); -}); - -test('parse *:DEST the same as :DEST', () => { - expect(DestinationPattern.parse('*:a')).toEqual(DestinationPattern.parse(':a')); -}); - -function f(obj: unknown, ...keys: string[]): any { - for (const k of keys) { - if (typeof obj === 'object' && obj !== null && k in obj) { - return (obj as any)[k]; - } - } - return undefined; -} diff --git a/packages/cdk-assets/test/mock-aws.ts b/packages/cdk-assets/test/mock-aws.ts deleted file mode 100644 index 10cb26da99727..0000000000000 --- a/packages/cdk-assets/test/mock-aws.ts +++ /dev/null @@ -1,74 +0,0 @@ -jest.mock('aws-sdk'); -import * as AWS from 'aws-sdk'; - -export function mockAws() { - const mockEcr = new AWS.ECR(); - const mockS3 = new AWS.S3(); - const mockSecretsManager = new AWS.SecretsManager(); - - // Sane defaults which can be overridden - mockS3.getBucketLocation = mockedApiResult({}); - mockS3.getBucketEncryption = mockedApiResult({}); - mockEcr.describeRepositories = mockedApiResult({ - repositories: [ - { - repositoryUri: '12345.amazonaws.com/repo', - }, - ], - }); - mockSecretsManager.getSecretValue = mockedApiFailure('NotImplemented', 'You need to supply an implementation for getSecretValue'); - - return { - mockEcr, - mockS3, - mockSecretsManager, - discoverPartition: jest.fn(() => Promise.resolve('swa')), - discoverCurrentAccount: jest.fn(() => Promise.resolve({ accountId: 'current_account', partition: 'swa' })), - discoverDefaultRegion: jest.fn(() => Promise.resolve('current_region')), - discoverTargetAccount: jest.fn(() => Promise.resolve({ accountId: 'target_account', partition: 'swa' })), - ecrClient: jest.fn(() => Promise.resolve(mockEcr)), - s3Client: jest.fn(() => Promise.resolve(mockS3)), - secretsManagerClient: jest.fn(() => Promise.resolve(mockSecretsManager)), - }; -} - -export function errorWithCode(code: string, message: string) { - const ret = new Error(message); - (ret as any).code = code; - return ret; -} - -export function mockedApiResult(returnValue: any) { - return jest.fn().mockReturnValue({ - promise: jest.fn().mockResolvedValue(returnValue), - }); -} - -export function mockedApiFailure(code: string, message: string) { - return jest.fn().mockReturnValue({ - promise: jest.fn().mockRejectedValue(errorWithCode(code, message)), - }); -} - -/** - * Mock upload, draining the stream that we get before returning - * so no race conditions happen with the uninstallation of mock-fs. - */ -export function mockUpload(expectContent?: string) { - return jest.fn().mockImplementation(request => ({ - promise: () => new Promise((ok, ko) => { - const didRead = new Array(); - - const bodyStream: NodeJS.ReadableStream = request.Body; - bodyStream.on('data', (chunk) => { didRead.push(chunk.toString()); }); // This listener must exist - bodyStream.on('error', ko); - bodyStream.on('close', () => { - const actualContent = didRead.join(''); - if (expectContent !== undefined && expectContent !== actualContent) { - throw new Error(`Expected to read '${expectContent}' but read: '${actualContent}'`); - } - ok(); - }); - }), - })); -} diff --git a/packages/cdk-assets/test/mock-child_process.ts b/packages/cdk-assets/test/mock-child_process.ts deleted file mode 100644 index 2cb513e24fff7..0000000000000 --- a/packages/cdk-assets/test/mock-child_process.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as child_process from 'child_process'; -import * as events from 'events'; - -if (!(child_process as any).spawn.mockImplementationOnce) { - throw new Error('Call "jest.mock(\'child_process\');" at the top of the test file!'); -} - -export interface Invocation { - commandLine: string[]; - cwd?: string; - exitCode?: number; - stdout?: string; - - /** - * Only match a prefix of the command (don't care about the details of the arguments) - */ - prefix?: boolean; -} - -export function mockSpawn(...invocations: Invocation[]): () => void { - let mock = (child_process.spawn as any); - for (const _invocation of invocations) { - const invocation = _invocation; // Mirror into variable for closure - mock = mock.mockImplementationOnce((binary: string, args: string[], options: child_process.SpawnOptions) => { - if (invocation.prefix) { - // Match command line prefix - expect([binary, ...args].slice(0, invocation.commandLine.length)).toEqual(invocation.commandLine); - } else { - // Match full command line - expect([binary, ...args]).toEqual(invocation.commandLine); - } - - if (invocation.cwd != null) { - expect(options.cwd).toBe(invocation.cwd); - } - - const child: any = new events.EventEmitter(); - child.stdin = new events.EventEmitter(); - child.stdin.write = jest.fn(); - child.stdin.end = jest.fn(); - child.stdout = new events.EventEmitter(); - child.stderr = new events.EventEmitter(); - - if (invocation.stdout) { - mockEmit(child.stdout, 'data', Buffer.from(invocation.stdout)); - } - mockEmit(child, 'close', invocation.exitCode ?? 0); - - return child; - }); - } - - mock.mockImplementation((binary: string, args: string[], _options: any) => { - throw new Error(`Did not expect call of ${JSON.stringify([binary, ...args])}`); - }); - - return () => { - expect(mock).toHaveBeenCalledTimes(invocations.length); - }; -} - -/** - * Must do this on the next tick, as emitter.emit() expects all listeners to have been attached already - */ -function mockEmit(emitter: events.EventEmitter, event: string, data: any) { - setImmediate(() => { - emitter.emit(event, data); - }); -} diff --git a/packages/cdk-assets/test/placeholders.test.ts b/packages/cdk-assets/test/placeholders.test.ts deleted file mode 100644 index 87944ca673c32..0000000000000 --- a/packages/cdk-assets/test/placeholders.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import * as mockfs from 'mock-fs'; -import { mockAws, mockedApiResult } from './mock-aws'; -import { AssetManifest, AssetPublishing } from '../lib'; - -let aws: ReturnType; -beforeEach(() => { - mockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - fileAsset: { - type: 'file', - source: { - path: 'some_file', - }, - destinations: { - theDestination: { - // Absence of region - assumeRoleArn: 'arn:aws:role-${AWS::AccountId}', - bucketName: 'some_bucket-${AWS::AccountId}-${AWS::Region}', - objectKey: 'some_key-${AWS::AccountId}-${AWS::Region}', - }, - }, - }, - }, - dockerImages: { - dockerAsset: { - type: 'docker-image', - source: { - directory: 'dockerdir', - }, - destinations: { - theDestination: { - // Explicit region - region: 'explicit_region', - assumeRoleArn: 'arn:aws:role-${AWS::AccountId}', - repositoryName: 'repo-${AWS::AccountId}-${AWS::Region}', - imageTag: 'abcdef', - }, - }, - }, - }, - }), - '/simple/cdk.out/some_file': 'FILE_CONTENTS', - }); - - aws = mockAws(); -}); - -afterEach(() => { - mockfs.restore(); -}); - -test('check that placeholders are replaced', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws }); - aws.mockS3.getBucketLocation = mockedApiResult({}); - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: [{ Key: 'some_key-current_account-current_region' }] }); - aws.mockEcr.describeImages = mockedApiResult({ /* No error == image exists */ }); - - await pub.publish(); - - expect(aws.s3Client).toHaveBeenCalledWith(expect.objectContaining({ - assumeRoleArn: 'arn:aws:role-current_account', - })); - - expect(aws.ecrClient).toHaveBeenCalledWith(expect.objectContaining({ - region: 'explicit_region', - assumeRoleArn: 'arn:aws:role-current_account', - })); - - expect(aws.mockS3.listObjectsV2).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket-current_account-current_region', - Prefix: 'some_key-current_account-current_region', - MaxKeys: 1, - })); - - expect(aws.mockEcr.describeImages).toHaveBeenCalledWith(expect.objectContaining({ - imageIds: [{ imageTag: 'abcdef' }], - repositoryName: 'repo-current_account-explicit_region', - })); -}); diff --git a/packages/cdk-assets/test/private/docker-credentials.test.ts b/packages/cdk-assets/test/private/docker-credentials.test.ts deleted file mode 100644 index c7e6957f9bc66..0000000000000 --- a/packages/cdk-assets/test/private/docker-credentials.test.ts +++ /dev/null @@ -1,220 +0,0 @@ -import * as os from 'os'; -import * as path from 'path'; -import * as mockfs from 'mock-fs'; -import { cdkCredentialsConfig, cdkCredentialsConfigFile, DockerCredentialsConfig, fetchDockerLoginCredentials } from '../../lib/private/docker-credentials'; -import { mockAws, mockedApiFailure, mockedApiResult } from '../mock-aws'; - -const _ENV = process.env; - -let aws: ReturnType; -beforeEach(() => { - jest.resetModules(); - jest.resetAllMocks(); - - aws = mockAws(); - - process.env = { ..._ENV }; -}); - -afterEach(() => { - mockfs.restore(); - process.env = _ENV; -}); - -describe('cdkCredentialsConfigFile', () => { - test('Can be overridden by CDK_DOCKER_CREDS_FILE', () => { - const credsFile = '/tmp/insertfilenamehere_cdk_config.json'; - process.env.CDK_DOCKER_CREDS_FILE = credsFile; - - expect(cdkCredentialsConfigFile()).toEqual(credsFile); - }); - - test('Uses homedir if no process env is set', () => { - expect(cdkCredentialsConfigFile()).toEqual(path.join(os.userInfo().homedir, '.cdk', 'cdk-docker-creds.json')); - }); -}); - -describe('cdkCredentialsConfig', () => { - const credsFile = '/tmp/foo/bar/does/not/exist/config.json'; - beforeEach(() => { process.env.CDK_DOCKER_CREDS_FILE = credsFile; }); - - test('returns undefined if no config exists', () => { - expect(cdkCredentialsConfig()).toBeUndefined(); - }); - - test('returns parsed config if it exists', () => { - mockfs({ - [credsFile]: JSON.stringify({ - version: '0.1', - domainCredentials: { - 'test1.example.com': { secretsManagerSecretId: 'mySecret' }, - 'test2.example.com': { ecrRepository: 'arn:aws:ecr:bar' }, - }, - }), - }); - - const config = cdkCredentialsConfig(); - expect(config).toBeDefined(); - expect(config?.version).toEqual('0.1'); - expect(config?.domainCredentials['test1.example.com']?.secretsManagerSecretId).toEqual('mySecret'); - expect(config?.domainCredentials['test2.example.com']?.ecrRepository).toEqual('arn:aws:ecr:bar'); - }); -}); - -describe('fetchDockerLoginCredentials', () => { - let config: DockerCredentialsConfig; - - beforeEach(() => { - config = { - version: '0.1', - domainCredentials: { - 'misconfigured.example.com': {}, - 'secret.example.com': { secretsManagerSecretId: 'mySecret' }, - 'secretwithrole.example.com': { - secretsManagerSecretId: 'mySecret', - assumeRoleArn: 'arn:aws:iam::0123456789012:role/my-role', - }, - 'secretwithcustomfields.example.com': { - secretsManagerSecretId: 'mySecret', - secretsUsernameField: 'name', - secretsPasswordField: 'apiKey', - assumeRoleArn: 'arn:aws:iam::0123456789012:role/my-role', - }, - 'ecr.example.com': { ecrRepository: true }, - 'ecrwithrole.example.com': { - ecrRepository: true, - assumeRoleArn: 'arn:aws:iam::0123456789012:role/my-role', - }, - }, - }; - }); - - test('throws on unknown domain', async () => { - await expect(fetchDockerLoginCredentials(aws, config, 'unknowndomain.example.com')).rejects.toThrow(/unknown domain/); - }); - - test('throws on misconfigured domain (no ECR or SM)', async () => { - await expect(fetchDockerLoginCredentials(aws, config, 'misconfigured.example.com')).rejects.toThrow(/unknown credential type/); - }); - - test('does not throw on correctly configured raw domain', async () => { - mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' }); - - await expect(fetchDockerLoginCredentials(aws, config, 'https://secret.example.com/v1/')).resolves.toBeTruthy(); - }); - - describe('SecretsManager', () => { - test('returns the credentials successfully if configured correctly - domain', async () => { - mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' }); - - const creds = await fetchDockerLoginCredentials(aws, config, 'secret.example.com'); - - expect(creds).toEqual({ Username: 'secretUser', Secret: 'secretPass' }); - }); - - test('returns the credentials successfully if configured correctly - raw domain', async () => { - mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' }); - - const creds = await fetchDockerLoginCredentials(aws, config, 'https://secret.example.com'); - - expect(creds).toEqual({ Username: 'secretUser', Secret: 'secretPass' }); - }); - - test('throws when SecretsManager returns an error', async () => { - const errMessage = "Secrets Manager can't find the specified secret."; - aws.mockSecretsManager.getSecretValue = mockedApiFailure('ResourceNotFoundException', errMessage); - - await expect(fetchDockerLoginCredentials(aws, config, 'secret.example.com')).rejects.toThrow(errMessage); - }); - - test('supports assuming a role', async () => { - mockSecretWithSecretString({ username: 'secretUser', secret: 'secretPass' }); - - const creds = await fetchDockerLoginCredentials(aws, config, 'secretwithrole.example.com'); - - expect(creds).toEqual({ Username: 'secretUser', Secret: 'secretPass' }); - expect(aws.secretsManagerClient).toHaveBeenCalledWith({ assumeRoleArn: 'arn:aws:iam::0123456789012:role/my-role' }); - }); - - test('supports configuring the secret fields', async () => { - mockSecretWithSecretString({ name: 'secretUser', apiKey: '01234567' }); - - const creds = await fetchDockerLoginCredentials(aws, config, 'secretwithcustomfields.example.com'); - - expect(creds).toEqual({ Username: 'secretUser', Secret: '01234567' }); - }); - - test('throws when secret does not have the correct fields - key/value', async () => { - mockSecretWithSecretString({ principal: 'foo', credential: 'bar' }); - - await expect(fetchDockerLoginCredentials(aws, config, 'secret.example.com')).rejects.toThrow(/malformed secret string/); - }); - - test('throws when secret does not have the correct fields - plaintext', async () => { - mockSecretWithSecretString('myAPIKey'); - - await expect(fetchDockerLoginCredentials(aws, config, 'secret.example.com')).rejects.toThrow(/malformed secret string/); - }); - }); - - describe('ECR getAuthorizationToken', () => { - test('returns the credentials successfully', async () => { - mockEcrAuthorizationData(Buffer.from('myFoo:myBar', 'utf-8').toString('base64')); - - const creds = await fetchDockerLoginCredentials(aws, config, 'ecr.example.com'); - - expect(creds).toEqual({ Username: 'myFoo', Secret: 'myBar' }); - }); - - test('throws if ECR errors', async () => { - aws.mockEcr.getAuthorizationToken = mockedApiFailure('ServerException', 'uhoh'); - - await expect(fetchDockerLoginCredentials(aws, config, 'ecr.example.com')).rejects.toThrow(/uhoh/); - }); - - test('supports assuming a role', async () => { - mockEcrAuthorizationData(Buffer.from('myFoo:myBar', 'utf-8').toString('base64')); - - const creds = await fetchDockerLoginCredentials(aws, config, 'ecrwithrole.example.com'); - - expect(creds).toEqual({ Username: 'myFoo', Secret: 'myBar' }); - expect(aws.ecrClient).toHaveBeenCalledWith({ assumeRoleArn: 'arn:aws:iam::0123456789012:role/my-role' }); - }); - - test('throws if ECR returns no authData', async () => { - aws.mockEcr.getAuthorizationToken = mockedApiResult({ authorizationData: [] }); - - await expect(fetchDockerLoginCredentials(aws, config, 'ecr.example.com')).rejects.toThrow(/No authorization data received from ECR/); - }); - - test('throws if ECR authData is in an incorrect format', async () => { - mockEcrAuthorizationData('notabase64encodedstring'); - - await expect(fetchDockerLoginCredentials(aws, config, 'ecr.example.com')).rejects.toThrow(/unexpected ECR authData format/); - }); - }); - -}); - -function mockSecretWithSecretString(secretString: any) { - aws.mockSecretsManager.getSecretValue = mockedApiResult({ - ARN: 'arn:aws:secretsmanager:eu-west-1:0123456789012:secret:mySecret', - Name: 'mySecret', - VersionId: 'fa81fe61-c167-4aca-969e-4d8df74d4814', - SecretString: JSON.stringify(secretString), - VersionStages: [ - 'AWSCURRENT', - ], - }); -} - -function mockEcrAuthorizationData(authorizationToken: string) { - aws.mockEcr.getAuthorizationToken = mockedApiResult({ - authorizationData: [ - { - authorizationToken, - proxyEndpoint: 'https://0123456789012.dkr.ecr.eu-west-1.amazonaws.com', - }, - ], - }); -} diff --git a/packages/cdk-assets/test/private/docker.test.ts b/packages/cdk-assets/test/private/docker.test.ts deleted file mode 100644 index 40c37ca35f271..0000000000000 --- a/packages/cdk-assets/test/private/docker.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Docker } from '../../lib/private/docker'; -import { ShellOptions, ProcessFailedError } from '../../lib/private/shell'; - -type ShellExecuteMock = jest.SpyInstance, Parameters>; - -describe('Docker', () => { - describe('exists', () => { - let docker: Docker; - - const makeShellExecuteMock = ( - fn: (params: string[]) => void, - ): ShellExecuteMock => - jest.spyOn<{ execute: Docker['execute'] }, 'execute'>(Docker.prototype as any, 'execute').mockImplementation( - async (params: string[], _options?: ShellOptions) => fn(params), - ); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - beforeEach(() => { - docker = new Docker(); - }); - - test('returns true when image inspect command does not throw', async () => { - const spy = makeShellExecuteMock(() => undefined); - - const imageExists = await docker.exists('foo'); - - expect(imageExists).toBe(true); - expect(spy.mock.calls[0][0]).toEqual(['inspect', 'foo']); - }); - - test('throws when an arbitrary error is caught', async () => { - makeShellExecuteMock(() => { - throw new Error(); - }); - - await expect(docker.exists('foo')).rejects.toThrow(); - }); - - test('throws when the error is a shell failure but the exit code is unrecognized', async () => { - makeShellExecuteMock(() => { - throw new (class extends Error implements ProcessFailedError { - public readonly code = 'PROCESS_FAILED' - public readonly exitCode = 47 - public readonly signal = null - - constructor() { - super('foo'); - } - }); - }); - - await expect(docker.exists('foo')).rejects.toThrow(); - }); - - test('returns false when the error is a shell failure and the exit code is 1 (Docker)', async () => { - makeShellExecuteMock(() => { - throw new (class extends Error implements ProcessFailedError { - public readonly code = 'PROCESS_FAILED' - public readonly exitCode = 1 - public readonly signal = null - - constructor() { - super('foo'); - } - }); - }); - - const imageExists = await docker.exists('foo'); - - expect(imageExists).toBe(false); - }); - - test('returns false when the error is a shell failure and the exit code is 125 (Podman)', async () => { - makeShellExecuteMock(() => { - throw new (class extends Error implements ProcessFailedError { - public readonly code = 'PROCESS_FAILED' - public readonly exitCode = 125 - public readonly signal = null - - constructor() { - super('foo'); - } - }); - }); - - const imageExists = await docker.exists('foo'); - - expect(imageExists).toBe(false); - }); - }); -}); diff --git a/packages/cdk-assets/test/progress.test.ts b/packages/cdk-assets/test/progress.test.ts deleted file mode 100644 index 2cca80311942a..0000000000000 --- a/packages/cdk-assets/test/progress.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import * as mockfs from 'mock-fs'; -import { FakeListener } from './fake-listener'; -import { mockAws, mockedApiResult, mockUpload } from './mock-aws'; -import { AssetManifest, AssetPublishing } from '../lib'; - -let aws: ReturnType; -beforeEach(() => { - mockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theAsset: { - source: { - path: 'some_file', - }, - destinations: { - theDestination1: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key', - }, - theDestination2: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key2', - }, - }, - }, - }, - }), - '/simple/cdk.out/some_file': 'FILE_CONTENTS', - }); - - aws = mockAws(); - - // Accept all S3 uploads as new - aws.mockS3.getBucketLocation = mockedApiResult({}); - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload(); -}); - -afterEach(() => { - mockfs.restore(); -}); - -test('test listener', async () => { - const progressListener = new FakeListener(); - - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, progressListener }); - await pub.publish(); - - const allMessages = progressListener.messages.join('\n'); - - // Log mentions asset/destination ids - expect(allMessages).toContain('theAsset:theDestination1'); - expect(allMessages).toContain('theAsset:theDestination2'); -}); - -test('test publishing in parallel', async () => { - const progressListener = new FakeListener(); - - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, progressListener, publishInParallel: true }); - await pub.publish(); - - const allMessages = progressListener.messages.join('\n'); - - // Log mentions asset/destination ids - expect(allMessages).toContain('theAsset:theDestination1'); - expect(allMessages).toContain('theAsset:theDestination2'); -}); - -test('test abort', async () => { - const progressListener = new FakeListener(true); - - const pub = new AssetPublishing(AssetManifest.fromPath('/simple/cdk.out'), { aws, progressListener }); - await pub.publish(); - - const allMessages = progressListener.messages.join('\n'); - - // We never get to asset 2 - expect(allMessages).not.toContain('theAsset:theDestination2'); -}); \ No newline at end of file diff --git a/packages/cdk-assets/test/test-archive-follow/data/one.txt b/packages/cdk-assets/test/test-archive-follow/data/one.txt deleted file mode 100644 index 56a6051ca2b02..0000000000000 --- a/packages/cdk-assets/test/test-archive-follow/data/one.txt +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/packages/cdk-assets/test/test-archive-follow/linked/two.txt b/packages/cdk-assets/test/test-archive-follow/linked/two.txt deleted file mode 100644 index d8263ee986059..0000000000000 --- a/packages/cdk-assets/test/test-archive-follow/linked/two.txt +++ /dev/null @@ -1 +0,0 @@ -2 \ No newline at end of file diff --git a/packages/cdk-assets/test/test-archive/executable.txt b/packages/cdk-assets/test/test-archive/executable.txt deleted file mode 100755 index e69de29bb2d1d..0000000000000 diff --git a/packages/cdk-assets/test/test-archive/file1.txt b/packages/cdk-assets/test/test-archive/file1.txt deleted file mode 100644 index 7bb7edbd4b634..0000000000000 --- a/packages/cdk-assets/test/test-archive/file1.txt +++ /dev/null @@ -1 +0,0 @@ -I am file1 \ No newline at end of file diff --git a/packages/cdk-assets/test/test-archive/file2.txt b/packages/cdk-assets/test/test-archive/file2.txt deleted file mode 100644 index ccb69856e7157..0000000000000 --- a/packages/cdk-assets/test/test-archive/file2.txt +++ /dev/null @@ -1,2 +0,0 @@ -I am file2 -BLA! \ No newline at end of file diff --git a/packages/cdk-assets/test/test-archive/subdir/file3.txt b/packages/cdk-assets/test/test-archive/subdir/file3.txt deleted file mode 100644 index 976606ef5a8ac..0000000000000 --- a/packages/cdk-assets/test/test-archive/subdir/file3.txt +++ /dev/null @@ -1 +0,0 @@ -I am in a subdirectory diff --git a/packages/cdk-assets/test/util.test.ts b/packages/cdk-assets/test/util.test.ts deleted file mode 100644 index 8e498076913f2..0000000000000 --- a/packages/cdk-assets/test/util.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createCriticalSection } from '../lib/private/util'; - -test('critical section', async () => { - // GIVEN - const criticalSection = createCriticalSection(); - - // WHEN - const arr = new Array(); - void criticalSection(async () => { - await new Promise(res => setTimeout(res, 500)); - arr.push('first'); - }); - await criticalSection(async () => { - arr.push('second'); - }); - - // THEN - expect(arr).toEqual([ - 'first', - 'second', - ]); -}); - -test('exceptions in critical sections', async () => { - // GIVEN - const criticalSection = createCriticalSection(); - - // WHEN/THEN - await expect(() => criticalSection(async () => { - throw new Error('Thrown'); - })).rejects.toThrow('Thrown'); -}); \ No newline at end of file diff --git a/packages/cdk-assets/test/zipping.test.ts b/packages/cdk-assets/test/zipping.test.ts deleted file mode 100644 index 71651289070b8..0000000000000 --- a/packages/cdk-assets/test/zipping.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Separate test file since the archiving module doesn't work well with 'mock-fs' -import { bockfs } from '@aws-cdk/cdk-build-tools'; -import { Manifest } from '@aws-cdk/cloud-assembly-schema'; -import { mockAws, mockedApiResult, mockUpload } from './mock-aws'; -import { AssetManifest, AssetPublishing } from '../lib'; - -let aws: ReturnType; -beforeEach(() => { - bockfs({ - '/simple/cdk.out/assets.json': JSON.stringify({ - version: Manifest.version(), - files: { - theAsset: { - source: { - path: 'some_dir', - packaging: 'zip', - }, - destinations: { - theDestination: { - region: 'us-north-50', - assumeRoleArn: 'arn:aws:role', - bucketName: 'some_bucket', - objectKey: 'some_key', - }, - }, - }, - }, - }), - '/simple/cdk.out/some_dir/some_file': 'FILE_CONTENTS', - }); - - aws = mockAws(); - - // Accept all S3 uploads as new - aws.mockS3.listObjectsV2 = mockedApiResult({ Contents: undefined }); - aws.mockS3.upload = mockUpload(); -}); - -afterEach(() => { - bockfs.restore(); -}); - -test('Take a zipped upload', async () => { - const pub = new AssetPublishing(AssetManifest.fromPath(bockfs.path('/simple/cdk.out')), { aws }); - - await pub.publish(); - - expect(aws.mockS3.upload).toHaveBeenCalledWith(expect.objectContaining({ - Bucket: 'some_bucket', - Key: 'some_key', - ContentType: 'application/zip', - })); -}); diff --git a/packages/cdk-assets/tsconfig.json b/packages/cdk-assets/tsconfig.json deleted file mode 100644 index b238d46998d26..0000000000000 --- a/packages/cdk-assets/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "lib": ["es2020", "dom"], - "strict": true, - "alwaysStrict": true, - "declaration": true, - "inlineSourceMap": true, - "inlineSources": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true, - "composite": true, - "incremental": true - }, - "include": [ - "**/*.ts", - "**/*.d.ts", - "lib/init-templates/*/*/add-project.hook.ts" - ], - "exclude": [ - "lib/init-templates/*/typescript/**/*.ts" - ] -} - From 4bc8830667372c54b469acf974d09e834ba07b3e Mon Sep 17 00:00:00 2001 From: Sumu Date: Sat, 24 Aug 2024 18:03:39 -0400 Subject: [PATCH 39/78] add back cloud-assembly-schema dir with README and index.ts Signed-off-by: Sumu --- .../cloud-assembly-schema/README.md | 55 +++++++++++++++++++ .../cloud-assembly-schema/index.ts | 1 + 2 files changed, 56 insertions(+) create mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/README.md create mode 100644 packages/aws-cdk-lib/cloud-assembly-schema/index.ts diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/README.md b/packages/aws-cdk-lib/cloud-assembly-schema/README.md new file mode 100644 index 0000000000000..72a319264f003 --- /dev/null +++ b/packages/aws-cdk-lib/cloud-assembly-schema/README.md @@ -0,0 +1,55 @@ +# Cloud Assembly Schema + +This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. + +## Cloud Assembly + +The _Cloud Assembly_ is the output of the synthesis operation. It is produced as part of the +[`cdk synth`](https://github.com/aws/aws-cdk/tree/main/packages/aws-cdk#cdk-synthesize) +command, or the [`app.synth()`](https://github.com/aws/aws-cdk/blob/main/packages/@aws-cdk/core/lib/app.ts#L135) method invocation. + +Its essentially a set of files and directories, one of which is the `manifest.json` file. It defines the set of instructions that are +needed in order to deploy the assembly directory. + +> For example, when `cdk deploy` is executed, the CLI reads this file and performs its instructions: +> +> - Build container images. +> - Upload assets. +> - Deploy CloudFormation templates. + +Therefore, the assembly is how the CDK class library and CDK CLI (or any other consumer) communicate. To ensure compatibility +between the assembly and its consumers, we treat the manifest file as a well defined, versioned schema. + +## Schema + +This module contains the typescript structs that comprise the `manifest.json` file, as well as the +generated [_json-schema_](./schema/cloud-assembly.schema.json). + +## Versioning + +The schema version is specified my the major version of the package release. It follows semantic versioning, but with a small twist. + +When we add instructions to the assembly, they are reflected in the manifest file and the _json-schema_ accordingly. +Every such instruction, is crucial for ensuring the correct deployment behavior. This means that to properly deploy a cloud assembly, +consumers must be aware of every such instruction modification. + +For this reason, every change to the schema, even though it might not strictly break validation of the _json-schema_ format, +is considered `major` version bump. All changes that do not impact the schema are considered a `minor` version bump. + +## How to consume + +If you'd like to consume the [schema file](./schema/cloud-assembly.schema.json) in order to do validations on `manifest.json` files, +simply download it from this repo and run it against standard _json-schema_ validators, such as [jsonschema](https://www.npmjs.com/package/jsonschema). + +Consumers must take into account the `major` version of the schema they are consuming. They should reject cloud assemblies +with a `major` version that is higher than what they expect. While schema validation might pass on such assemblies, the deployment integrity +cannot be guaranteed because some instructions will be ignored. + +> For example, if your consumer was built when the schema version was 2.0.0, you should reject deploying cloud assemblies with a +> manifest version of 3.0.0. + +## Contributing + +The source code for this file has been moved to CDKLabs. + +See [Contribution Guide](https://github.com/cdklabs/cloud-assembly-schema/blob/main/CONTRIBUTING.md) \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/index.ts b/packages/aws-cdk-lib/cloud-assembly-schema/index.ts new file mode 100644 index 0000000000000..ee838a8eb9f53 --- /dev/null +++ b/packages/aws-cdk-lib/cloud-assembly-schema/index.ts @@ -0,0 +1 @@ +export * from '@aws-cdk/cloud-assembly-schema'; \ No newline at end of file From e8195d3f7198f22d0363bee8b4aaac31914f51fb Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:39:00 -0400 Subject: [PATCH 40/78] Update packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts Co-authored-by: Eli Polonsky --- packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts | 2 ++ 1 file changed, 2 insertions(+) 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 758c7c5039913..dfccff32826cb 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 @@ -232,6 +232,8 @@ 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; From da4328164a7eb97142aa76e5d7c8118981ecd5a1 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 11:20:59 -0400 Subject: [PATCH 41/78] add explanation comments inside custom bootstrap templates on TagSession trust policies and PrincipalTag conditionals Signed-off-by: Sumu --- ...p-with-deploy-role-labmda-permissions.yaml | 15 ++++++++++++++ .../custom-bootstrap-with-session-tags.yaml | 20 ++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml index aa1eba01f84da..265341e7221b5 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml @@ -277,6 +277,8 @@ Resources: 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: @@ -305,6 +307,8 @@ Resources: 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: @@ -338,6 +342,8 @@ Resources: 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: @@ -451,6 +457,8 @@ Resources: 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: @@ -515,10 +523,17 @@ Resources: 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" diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml index 0f7e3913e97f6..955f060424967 100644 --- a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml +++ b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml @@ -282,6 +282,8 @@ Resources: 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: @@ -310,6 +312,8 @@ Resources: 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: @@ -338,6 +342,8 @@ Resources: 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: @@ -392,6 +398,9 @@ Resources: 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: @@ -433,6 +442,9 @@ Resources: 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" @@ -455,6 +467,8 @@ Resources: 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: @@ -482,6 +496,9 @@ Resources: - 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" @@ -569,9 +586,6 @@ Resources: Effect: Allow Principal: Service: cloudformation.amazonaws.com - # Condition: - # StringEquals: - # aws:RequestTag/Department: "Engineering" Version: '2012-10-17' ManagedPolicyArns: Fn::If: From 37a8ad561cc17351860fa199f395e694a2cf5505 Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:23:55 -0400 Subject: [PATCH 42/78] Update packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js Co-authored-by: Eli Polonsky --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 46455132f2b69..60a5b6bbe4240 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 @@ -462,7 +462,7 @@ class SessionTagsStack extends cdk.Stack { MIN_BOOTSTRAP_STACK_VERSION = 6; MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 6; -class CustomSynthesizer extends cdk.DefaultStackSynthesizer { +class NoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { // This CustomSynthesizer is the same as the Default synthesizer, except that it passes // undefined for the CloudFormationExecutionRole, since we want it to use the Deploy role instead. From 95d990b73a4a81037cb1640943d83446782f4c43 Mon Sep 17 00:00:00 2001 From: Sumu Pitchayan <35242245+sumupitchayan@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:51:37 -0400 Subject: [PATCH 43/78] Update packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts Co-authored-by: Eli Polonsky --- .../cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a0772532d1055..0f9219c3a557c 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 @@ -101,7 +101,7 @@ integTest('can deploy with session tags on the deploy, file asset, and image ass })); // Custom Bootstrap test without CloudFormationExecutionRole and Session Tags on the DeployRole -integTest('can deploy with session tags using custom synthesizer', withoutBootstrap(async (fixture) => { +integTest('can deploy without execution role and with session tags on deploy role', withoutBootstrap(async (fixture) => { const bootstrapStackName = fixture.bootstrapStackName; await fixture.cdkBootstrapModern({ From 94dc868b4d2505122ee3947c8ff6987b48a59745 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 11:52:17 -0400 Subject: [PATCH 44/78] cleanup app.js Signed-off-by: Sumu --- .../cli-integ/resources/cdk-apps/app/app.js | 7 ------- 1 file changed, 7 deletions(-) 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 60a5b6bbe4240..5b46120ac10fd 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 @@ -30,9 +30,6 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') { aws_ecr_assets: docker, Stack } = require('aws-cdk-lib'); - var { - StringSpecializer - } = require('aws-cdk-lib/core/lib/helpers-internal'); } const { Annotations } = cdk; @@ -448,15 +445,11 @@ class SessionTagsStack extends cdk.Stack { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler' }); - new cdk.CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); // DockerImageAsset to test ImageAssetPublishingRole new docker.DockerImageAsset(this, 'image', { directory: path.join(__dirname, 'docker') }); - new cdk.CfnResource(this, 'Handle', { - type: 'AWS::CloudFormation::WaitConditionHandle' - }); } } From d114fc77e05228c140dac2a2b3c9ccc1486a7e84 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 11:54:10 -0400 Subject: [PATCH 45/78] rename CustomSynthesizer to NoExecutionRoleCustomSynthesizer in app.js Signed-off-by: Sumu --- .../cli-integ/resources/cdk-apps/app/app.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 5b46120ac10fd..235e5a3f4291d 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 @@ -455,9 +455,9 @@ class SessionTagsStack extends cdk.Stack { MIN_BOOTSTRAP_STACK_VERSION = 6; MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 6; -class NoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { +class NoExecutionRoleNoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { - // This CustomSynthesizer is the same as the Default synthesizer, except that it passes + // This Custom Synthesizer is the same as the Default synthesizer, except that it passes // undefined for the CloudFormationExecutionRole, since we want it to use the Deploy role instead. synthesize(session) { if (this.props.generateBootstrapVersionRule ?? true) { @@ -493,11 +493,11 @@ class NoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { } } -class SessionTagsWithCustomSynthesizerStack extends cdk.Stack { +class SessionTagsWithNoExecutionRoleCustomSynthesizerStack extends cdk.Stack { constructor(parent, id, props) { super(parent, id, { ...props, - synthesizer: new CustomSynthesizer({ + synthesizer: new NoExecutionRoleCustomSynthesizer({ deployRoleSessionTags: { 'Department' : 'Engineering', }, @@ -758,7 +758,7 @@ switch (stackSet) { new LambdaStack(app, `${stackPrefix}-lambda`); new SessionTagsStack(app, `${stackPrefix}-session-tags`); - new SessionTagsWithCustomSynthesizerStack(app, `${stackPrefix}-session-tags-with-custom-synthesizer`); + new SessionTagsWithNoExecutionRoleCustomSynthesizerStack(app, `${stackPrefix}-session-tags-with-custom-synthesizer`); new LambdaHotswapStack(app, `${stackPrefix}-lambda-hotswap`); new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`); new DockerStack(app, `${stackPrefix}-docker`); From 59c10984428cf05061b506dfa0c9d7d8202b0ca8 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 12:06:37 -0400 Subject: [PATCH 46/78] WIP: core README Signed-off-by: Sumu --- packages/aws-cdk-lib/core/README.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 323ca7ae1f972..16c740febe126 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -157,9 +157,9 @@ see [Bootstrapping in the CDK Developer Guide](https://docs.aws.amazon.com/cdk/l ## Session Tags -Stack Synthesizers also support passing Session Tags to the different [IAM roles created by CDK during bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles): +CDK also supports attribute-based access control (ABAC) by allowing you to pass session tags on the different [IAM roles created by CDK during bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles). For more information on Session Tags see [Define permissions based on attributes with ABAC authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html). -Here is an example of passing a session tag to the `DeploymentActionRole`: +Here is an example of passing session tags using the `DefaultStackSynthesizer`: ```ts class MyStack extends cdk.Stack { @@ -170,15 +170,22 @@ class MyStack extends cdk.Stack { deployRoleSessionTags: { 'Department' : 'Engineering', }, + fileAssetPublishingRoleSessionTags: { + Department: 'Engineering-FileAssetTag', + }, + imageAssetPublishingRoleSessionTags: { + Department: 'Engineering-ImageAssetTag', + }, + lookupRoleSessionTags: { + Department: 'Engineering-LookupRoleTag', + }, }) }); } } ``` -You can also do the same with a custom stack synthesizer. - -For more information on Session Tags see [Define permissions based on attributes with ABAC authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html). +You can also do the same with a custom stack synthesizer by extending the `DefaultStackSynthesizer` or `StackSynthesizer` base class. ## Nested Stacks From 3a7a948476bbc4ed0c5ab4a3d46896d72774c8f8 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 12:11:52 -0400 Subject: [PATCH 47/78] add comment in sdk-provider.ts explaining why we use transitive tag keys Signed-off-by: Sumu --- packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts | 3 +++ 1 file changed, 3 insertions(+) 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 92f950bfba26f..dac83c3c6eee2 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -373,6 +373,9 @@ export class SdkProvider { Value: value, })), } : {}, + // We pass in the all Session Tags keys as TransitiveTagKeys so that + // they persist during role chaining. For more information, see: + // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining ...sessionTags ? { TransitiveTagKeys: Object.keys(sessionTags) } : {}, RoleSessionName: `aws-cdk-${safeUsername()}`, }, From 2bb83815f12a1506ec4f39f0fcbe87e47de1e4e2 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 12:18:45 -0400 Subject: [PATCH 48/78] remove extra line in cloud-assembly-schema/README.md Signed-off-by: Sumu --- packages/aws-cdk-lib/cloud-assembly-schema/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/aws-cdk-lib/cloud-assembly-schema/README.md b/packages/aws-cdk-lib/cloud-assembly-schema/README.md index ad96ca16b6f8a..40796ca98ace0 100644 --- a/packages/aws-cdk-lib/cloud-assembly-schema/README.md +++ b/packages/aws-cdk-lib/cloud-assembly-schema/README.md @@ -52,5 +52,4 @@ cannot be guaranteed because some instructions will be ignored. The source code for this file has been moved to CDKLabs. - See [Contribution Guide](https://github.com/cdklabs/cloud-assembly-schema/blob/main/CONTRIBUTING.md) From 06de35ad95a40bf5cb84932ff568a527608191c3 Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 12:57:47 -0400 Subject: [PATCH 49/78] delete custom-synthesizer file, write it inline core unit test instead; make DefaultStackSynthesizer props protected readonly in constructor intead of private readonly Signed-off-by: Sumu --- .../stack-synthesizers/default-synthesizer.ts | 2 +- .../stack-synthesis/custom-synthesizer.ts | 260 ------------------ .../new-style-synthesis.test.ts | 35 ++- 3 files changed, 35 insertions(+), 262 deletions(-) delete mode 100644 packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts 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 d01893b61359e..5cb8023579a0f 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 @@ -330,7 +330,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab private bootstrapStackVersionSsmParameter?: string; private assetManifest = new AssetManifestBuilder(); - constructor(private readonly props: DefaultStackSynthesizerProps = {}) { + constructor(protected readonly props: DefaultStackSynthesizerProps = {}) { super(); this.useLookupRoleForStackOperations = props.useLookupRoleForStackOperations ?? true; diff --git a/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts b/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts deleted file mode 100644 index cea6ddcb9857b..0000000000000 --- a/packages/aws-cdk-lib/core/test/stack-synthesis/custom-synthesizer.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { Stack, FileAssetSource, FileAssetLocation, DockerImageAssetSource, DockerImageAssetLocation } from '../../lib'; -import { StringSpecializer } from '../../lib/helpers-internal'; -import { AssetManifestBuilder, DefaultStackSynthesizerProps, IBoundStackSynthesizer, IReusableStackSynthesizer, ISynthesisSession, StackSynthesizer } from '../../lib/stack-synthesizers'; -import { assertBound } from '../../lib/stack-synthesizers/_shared'; - -export const BOOTSTRAP_QUALIFIER_CONTEXT = '@aws-cdk/core:bootstrapQualifier'; - -/** - * The minimum bootstrap stack version required by this app. - */ -const MIN_BOOTSTRAP_STACK_VERSION = 6; - -/** - * The minimum bootstrap stack version required - * to use the lookup role. - */ -const MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 8; - -/** - * Uses conventionally named roles and asset storage locations - * - * This synthesizer: - * - * - Is a Custom Stack Synthesizer with pretty much the same implementation as the - * `DefaultStackSynthesizer` - it also takes in the `DefaultStackSynthesizer` props - * in the constructor, but the only difference is that we pass in undefined for the - * CloudFormationExecutionRoleArn inside emitArtifact() of the synthesize() function; - * the purpose of this to use the DeployRole for CloudFormation Execution instead. - * - * Requires the environment to have been bootstrapped with Bootstrap Stack V2 - * (also known as "modern bootstrap stack"). The synthesizer adds a version - * check to the template, to make sure the bootstrap stack is recent enough - * to support all features expected by this synthesizer. - */ -export class CustomSynthesizer extends StackSynthesizer implements IReusableStackSynthesizer, IBoundStackSynthesizer { - /** - * Default ARN qualifier - */ - public static readonly DEFAULT_QUALIFIER = 'hnb659fds'; - - /** - * Default CloudFormation role ARN. - */ - public static readonly DEFAULT_CLOUDFORMATION_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default deploy role ARN. - */ - public static readonly DEFAULT_DEPLOY_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default asset publishing role ARN for file (S3) assets. - */ - public static readonly DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default asset publishing role ARN for image (ECR) assets. - */ - public static readonly DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default lookup role ARN for missing values. - */ - public static readonly DEFAULT_LOOKUP_ROLE_ARN = 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default image assets repository name - */ - public static readonly DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME = 'cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}'; - - /** - * Default file assets bucket name - */ - public static readonly DEFAULT_FILE_ASSETS_BUCKET_NAME = 'cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}'; - - /** - * Name of the CloudFormation Export with the asset key name - */ - public static readonly DEFAULT_FILE_ASSET_KEY_ARN_EXPORT_NAME = 'CdkBootstrap-${Qualifier}-FileAssetKeyArn'; - - /** - * Default file asset prefix - */ - public static readonly DEFAULT_FILE_ASSET_PREFIX = ''; - /** - * Default Docker asset prefix - */ - public static readonly DEFAULT_DOCKER_ASSET_PREFIX = ''; - - /** - * Default bootstrap stack version SSM parameter. - */ - public static readonly DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER = '/cdk-bootstrap/${Qualifier}/version'; - - private bucketName?: string; - private repositoryName?: string; - private _deployRoleArn?: string; - private _cloudFormationExecutionRoleArn?: string; - private fileAssetPublishingRoleArn?: string; - private imageAssetPublishingRoleArn?: string; - private lookupRoleArn?: string; - private useLookupRoleForStackOperations: boolean; - private qualifier?: string; - private bucketPrefix?: string; - private dockerTagPrefix?: string; - private bootstrapStackVersionSsmParameter?: string; - private assetManifest = new AssetManifestBuilder(); - - constructor(private readonly props: DefaultStackSynthesizerProps = {}) { - super(); - this.useLookupRoleForStackOperations = props.useLookupRoleForStackOperations ?? true; - } - - /** - * Produce a bound Stack Synthesizer for the given stack. - * - * This method may be called more than once on the same object. - */ - public reusableBind(stack: Stack): IBoundStackSynthesizer { - // Create a copy of the current object and bind that - const copy = Object.create(this); - copy.bind(stack); - return copy; - } - - /** - * The qualifier used to bootstrap this stack - */ - public get bootstrapQualifier(): string | undefined { - return this.qualifier; - } - - /** - * The role used to lookup for this stack - */ - public get lookupRole(): string | undefined { - return this.lookupRoleArn; - } - - public bind(stack: Stack): void { - super.bind(stack); - - const qualifier = this.props.qualifier ?? stack.node.tryGetContext(BOOTSTRAP_QUALIFIER_CONTEXT) ?? CustomSynthesizer.DEFAULT_QUALIFIER; - this.qualifier = qualifier; - - const spec = new StringSpecializer(stack, qualifier); - - /* eslint-disable max-len */ - this.bucketName = spec.specialize(this.props.fileAssetsBucketName ?? CustomSynthesizer.DEFAULT_FILE_ASSETS_BUCKET_NAME); - this.repositoryName = spec.specialize(this.props.imageAssetsRepositoryName ?? CustomSynthesizer.DEFAULT_IMAGE_ASSETS_REPOSITORY_NAME); - this._deployRoleArn = spec.specialize(this.props.deployRoleArn ?? CustomSynthesizer.DEFAULT_DEPLOY_ROLE_ARN); - this._cloudFormationExecutionRoleArn = spec.specialize(this.props.cloudFormationExecutionRole ?? CustomSynthesizer.DEFAULT_CLOUDFORMATION_ROLE_ARN); - this.fileAssetPublishingRoleArn = spec.specialize(this.props.fileAssetPublishingRoleArn ?? CustomSynthesizer.DEFAULT_FILE_ASSET_PUBLISHING_ROLE_ARN); - this.imageAssetPublishingRoleArn = spec.specialize(this.props.imageAssetPublishingRoleArn ?? CustomSynthesizer.DEFAULT_IMAGE_ASSET_PUBLISHING_ROLE_ARN); - this.lookupRoleArn = spec.specialize(this.props.lookupRoleArn ?? CustomSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - this.bucketPrefix = spec.specialize(this.props.bucketPrefix ?? CustomSynthesizer.DEFAULT_FILE_ASSET_PREFIX); - this.dockerTagPrefix = spec.specialize(this.props.dockerTagPrefix ?? CustomSynthesizer.DEFAULT_DOCKER_ASSET_PREFIX); - this.bootstrapStackVersionSsmParameter = spec.qualifierOnly(this.props.bootstrapStackVersionSsmParameter ?? CustomSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER); - /* eslint-enable max-len */ - } - - public addFileAsset(asset: FileAssetSource): FileAssetLocation { - assertBound(this.bucketName); - - const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, { - bucketName: this.bucketName, - bucketPrefix: this.bucketPrefix, - role: this.fileAssetPublishingRoleArn ? { - assumeRoleArn: this.fileAssetPublishingRoleArn, - assumeRoleExternalId: this.props.fileAssetPublishingExternalId, - assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, - } : undefined, - }); - return this.cloudFormationLocationFromFileAsset(location); - } - - public addDockerImageAsset(asset: DockerImageAssetSource): DockerImageAssetLocation { - assertBound(this.repositoryName); - - const location = this.assetManifest.defaultAddDockerImageAsset(this.boundStack, asset, { - repositoryName: this.repositoryName, - dockerTagPrefix: this.dockerTagPrefix, - role: this.imageAssetPublishingRoleArn ? { - assumeRoleArn: this.imageAssetPublishingRoleArn, - assumeRoleExternalId: this.props.imageAssetPublishingExternalId, - assumeRoleSessionTags: this.props.imageAssetPublishingRoleSessionTags, - } : undefined, - }); - return this.cloudFormationLocationFromDockerImageAsset(location); - } - - /** - * 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); - } - - /** - * Return the currently bound stack - * - * @deprecated Use `boundStack` instead. - */ - protected get stack(): Stack | undefined { - return this.boundStack; - } - - /** - * Synthesize the associated stack to the session - */ - public synthesize(session: ISynthesisSession): void { - assertBound(this.qualifier); - - // Must be done here -- if it's done in bind() (called in the Stack's constructor) - // then it will become impossible to set context after that. - // - // 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!); - } - - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); - const templateAsset = this.addFileAsset(templateAssetSource); - - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - }); - - this.emitArtifact(session, { - assumeRoleExternalId: this.props.deployRoleExternalId, - assumeRoleArn: this._deployRoleArn, - assumeRoleSessionTags: this.props.deployRoleSessionTags, - // Sets CFN Execution Role Arn to undefined: - cloudFormationExecutionRoleArn: undefined, - stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - additionalDependencies: [assetManifestId], - lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { - arn: this.lookupRoleArn, - assumeRoleExternalId: this.props.lookupRoleExternalId, - assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - } : undefined, - }); - } - - /** - * Returns the ARN of the deploy Role. - */ - public get deployRoleArn(): string { - if (!this._deployRoleArn) { - throw new Error('deployRoleArn getter can only be called after the synthesizer has been bound to a Stack'); - } - return this._deployRoleArn; - } -} 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 e17fda04fa402..ae61ee84bed36 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 @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import { CustomSynthesizer } from './custom-synthesizer.ts'; import * as cxschema from '../../../cloud-assembly-schema'; import { ArtifactType } from '../../../cloud-assembly-schema'; import * as cxapi from '../../../cx-api'; @@ -152,6 +151,40 @@ describe('new style synthesis', () => { }); test('can set session tags on a custom stack synthesizer', () => { + class CustomSynthesizer extends DefaultStackSynthesizer { + public static readonly MIN_BOOTSTRAP_STACK_VERSION = 6; + private myAssetManifest = new AssetManifestBuilder(); + + public synthesize(session) { + const templateAssetSource = this.synthesizeTemplate(session, DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); + const templateAsset = this.addFileAsset(templateAssetSource); + + const assetManifestId = this.myAssetManifest.emitManifest(this.boundStack, session, { + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + }); + + this.emitArtifact(session, { + assumeRoleExternalId: this.props.deployRoleExternalId, + assumeRoleArn: DefaultStackSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, + assumeRoleSessionTags: this.props.deployRoleSessionTags, + // Pass in UNDEFINED for the CFN Execution Role Arn: + cloudFormationExecutionRoleArn: undefined, + stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + additionalDependencies: [assetManifestId], + lookupRole: { + arn: DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN, + assumeRoleExternalId: this.props.lookupRoleExternalId, + assumeRoleSessionTags: this.props.lookupRoleSessionTags, + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + }, + }); + } + } + // GIVEN stack = new Stack(app, 'SessionTagsCustomSynthesizerStack', { synthesizer: new CustomSynthesizer({ From ec4b07df31366823f20c82cd3e081cab0f86b88d Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 13:13:46 -0400 Subject: [PATCH 50/78] add example using CustomSynthesizer to core README Signed-off-by: Sumu --- packages/aws-cdk-lib/core/README.md | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 16c740febe126..8201c82e574f4 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -187,6 +187,59 @@ class MyStack extends cdk.Stack { You can also do the same with a custom stack synthesizer by extending the `DefaultStackSynthesizer` or `StackSynthesizer` base class. +Here is an example of a custom synthesizer that extends the `DefaultStackSynthesizer` class, and sets the the `CloudFormationExecutionRoleArn` to `undefined` within the synthesize method; doing this will mean that CDK uses the `DeploymentActionRole` for CFN execution instead of the `CloudFormationExecutionRole`. + +```ts +class CustomSynthesizer extends DefaultStackSynthesizer { + public static readonly MIN_BOOTSTRAP_STACK_VERSION = 6; + private myAssetManifest = new AssetManifestBuilder(); + + public synthesize(session) { + const templateAssetSource = this.synthesizeTemplate(session, DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); + const templateAsset = this.addFileAsset(templateAssetSource); + + const assetManifestId = this.myAssetManifest.emitManifest(this.boundStack, session, { + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + }); + + this.emitArtifact(session, { + assumeRoleExternalId: this.props.deployRoleExternalId, + assumeRoleArn: DefaultStackSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, + assumeRoleSessionTags: this.props.deployRoleSessionTags, + // Pass in UNDEFINED for the CFN Execution Role Arn: + cloudFormationExecutionRoleArn: undefined, + stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + additionalDependencies: [assetManifestId], + lookupRole: { + arn: DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN, + assumeRoleExternalId: this.props.lookupRoleExternalId, + assumeRoleSessionTags: this.props.lookupRoleSessionTags, + requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, + bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, + }, + }); + } +} + +// Then, passing session tags to the CustomSynthesizer is the same as in the +// DefaultStackSynthesizer above. +class MyOtherStack extends cdk.Stack { + constructor(parent, id, props) { + super(parent, id, { + ...props, + synthesizer: new CustomSynthesizer({ + deployRoleSessionTags: { + 'Department' : 'Engineering', + }, + }) + }); + } +} +``` + ## 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. From 278dc4e55eb31c725d61ec17008291729c55063f Mon Sep 17 00:00:00 2001 From: Sumu Date: Mon, 26 Aug 2024 16:14:05 -0400 Subject: [PATCH 51/78] add sdk-provider unit test, including adding sessionTags and transitiveKeys to FakeSTS request Signed-off-by: Sumu --- packages/aws-cdk/test/api/fake-sts.ts | 32 +++++++++++++++++ .../aws-cdk/test/api/sdk-provider.test.ts | 35 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/packages/aws-cdk/test/api/fake-sts.ts b/packages/aws-cdk/test/api/fake-sts.ts index 026caecb2810f..f42c59740c662 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 sessionTags?: { [key: string]: string }; + readonly transitiveTagKeys?: string[]; } /** @@ -158,14 +160,43 @@ export class FakeSts { }; } + /** + * This function parses session tags from the STS mock request into a dictionary. This is necessary because + * the STS request body writes these dictionary values in the format Tags.member.X.Key and Tags.member.X.Value. + * + * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + * @param body - the parsed body of the mockRequest + * @returns session tags dictionary + */ + private parseSessionTagsFromRequestBody(body: Record): { [key: string]: string } { + const tags: { [key: string]: string } = {}; + + for (const key in body) { + if (key.startsWith('Tags.member.') && key.endsWith('.Key')) { + const tagIndex = key.split('.')[2]; + const tagKey = body[key]; + const tagValueKey = `Tags.member.${tagIndex}.Value`; + if (tagValueKey in body) { + tags[tagKey] = body[tagValueKey]; + } + } + } + + return tags; + } + private handleAssumeRole(identity: RegisteredIdentity, mockRequest: MockRequest): Record { this.checkForFailure(mockRequest.parsedBody.RoleArn); + const parsedSessionTags = this.parseSessionTagsFromRequestBody(mockRequest.parsedBody); + this.assumedRoles.push({ roleArn: mockRequest.parsedBody.RoleArn, roleSessionName: mockRequest.parsedBody.RoleSessionName, serialNumber: mockRequest.parsedBody.SerialNumber, tokenCode: mockRequest.parsedBody.TokenCode, + sessionTags: parsedSessionTags, + transitiveTagKeys: Object.keys(parsedSessionTags), }); const roleArn = mockRequest.parsedBody.RoleArn; @@ -255,6 +286,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..6376e0f885465 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -342,6 +342,41 @@ 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', + assumeRoleSessionTags: { + Department: 'Engineering', + }, + })).sdk as SDK; + await sdk.currentAccount(); + + // THEN + expect(fakeSts.assumedRoles[0]).toEqual(expect.objectContaining({ + sessionTags: { + Department: 'Engineering', + }, + transitiveTagKeys: ['Department'], + })); + }); + }); + test('assuming a role does not fail when OS username cannot be read', async () => { // GIVEN prepareCreds({ From a8fc420679fe5230c5b9a476e45d71903c8b5d98 Mon Sep 17 00:00:00 2001 From: epolon Date: Wed, 28 Aug 2024 10:19:25 +0300 Subject: [PATCH 52/78] sts:TagSession to bootstrap template and adjust bootstrap versions requirement according to session tags usage --- packages/aws-cdk-lib/core/README.md | 73 +++---------- .../asset-manifest-builder.ts | 16 ++- .../stack-synthesizers/default-synthesizer.ts | 78 +++++++++++++- .../new-style-synthesis.test.ts | 81 +------------- .../aws-cdk-lib/core/test/synthesis.test.ts | 102 ++++++++++++++++++ .../lib/api/bootstrap/bootstrap-template.yaml | 30 +++++- 6 files changed, 234 insertions(+), 146 deletions(-) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 8201c82e574f4..7bc5d1cbb79e9 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -155,11 +155,13 @@ 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). -## Session Tags +### STS Session Tags -CDK also supports attribute-based access control (ABAC) by allowing you to pass session tags on the different [IAM roles created by CDK during bootstrapping](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping-env.html#bootstrapping-env-roles). For more information on Session Tags see [Define permissions based on attributes with ABAC authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html). +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). -Here is an example of passing session tags using the `DefaultStackSynthesizer`: +> 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 `DefaultStackSynthesizer`: ```ts class MyStack extends cdk.Stack { @@ -168,16 +170,16 @@ class MyStack extends cdk.Stack { ...props, synthesizer: new DefaultStackSynthesizer({ deployRoleSessionTags: { - 'Department' : 'Engineering', + Department : 'Engineering', }, fileAssetPublishingRoleSessionTags: { - Department: 'Engineering-FileAssetTag', + Department: 'Engineering', }, imageAssetPublishingRoleSessionTags: { - Department: 'Engineering-ImageAssetTag', + Department: 'Engineering', }, lookupRoleSessionTags: { - Department: 'Engineering-LookupRoleTag', + Department: 'Engineering', }, }) }); @@ -185,60 +187,13 @@ class MyStack extends cdk.Stack { } ``` -You can also do the same with a custom stack synthesizer by extending the `DefaultStackSynthesizer` or `StackSynthesizer` base class. - -Here is an example of a custom synthesizer that extends the `DefaultStackSynthesizer` class, and sets the the `CloudFormationExecutionRoleArn` to `undefined` within the synthesize method; doing this will mean that CDK uses the `DeploymentActionRole` for CFN execution instead of the `CloudFormationExecutionRole`. - -```ts -class CustomSynthesizer extends DefaultStackSynthesizer { - public static readonly MIN_BOOTSTRAP_STACK_VERSION = 6; - private myAssetManifest = new AssetManifestBuilder(); - - public synthesize(session) { - const templateAssetSource = this.synthesizeTemplate(session, DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - const templateAsset = this.addFileAsset(templateAssetSource); +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. - const assetManifestId = this.myAssetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - }); - - this.emitArtifact(session, { - assumeRoleExternalId: this.props.deployRoleExternalId, - assumeRoleArn: DefaultStackSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, - assumeRoleSessionTags: this.props.deployRoleSessionTags, - // Pass in UNDEFINED for the CFN Execution Role Arn: - cloudFormationExecutionRoleArn: undefined, - stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - additionalDependencies: [assetManifestId], - lookupRole: { - arn: DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN, - assumeRoleExternalId: this.props.lookupRoleExternalId, - assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - }, - }); - } -} +> See https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required -// Then, passing session tags to the CustomSynthesizer is the same as in the -// DefaultStackSynthesizer above. -class MyOtherStack extends cdk.Stack { - constructor(parent, id, props) { - super(parent, id, { - ...props, - synthesizer: new CustomSynthesizer({ - deployRoleSessionTags: { - 'Department' : 'Engineering', - }, - }) - }); - } -} -``` +- If you are using a custom bootstrap template, make sure the template includes these permissions. +- If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). ## Nested Stacks 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 2dc4678f73d74..92033b83da5c1 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 @@ -123,7 +123,21 @@ export class AssetManifestBuilder { * Whether there are any assets registered in the manifest */ public get hasAssets() { - return Object.keys(this.files).length + Object.keys(this.dockerImages).length > 0; + return this.hasFileAssets || this.hasImageAssets; + } + + /** + * Whether there are any file assets registered in the manifest + */ + public get hasFileAssets() { + return Object.keys(this.files).length > 0; + } + + /** + * Whether there are any image assets registered in the manifest + */ + public get hasImageAssets() { + return Object.keys(this.dockerImages).length > 0; } /** 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 5cb8023579a0f..fd1e5a0b3899c 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 */ @@ -213,29 +221,57 @@ export interface DefaultStackSynthesizerProps { readonly bootstrapStackVersionSsmParameter?: string; /** - * Session tags to be used for the deploy role + * Session tags to be used when assuming the deploy role. + * This will cause the CDK CLI to include session tags when assuming this role during deployment. * + * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * + * - If you are using a custom bootstrap template, make sure the template includes these permissions. + * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). + * + * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required * @default - No Session Tags */ readonly deployRoleSessionTags?: { [key: string]: string }; /** * Session tags to be used for the lookup role + * This will cause the CDK CLI to include session tags when assuming this role during deployment. + * + * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. * + * - If you are using a custom bootstrap template, make sure the template includes these permissions. + * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). + * + * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required * @default - No Session Tags */ readonly lookupRoleSessionTags?: { [key: string]: string }; /** * Session tags to be used for the file asset publishing role + * This will cause the CDK CLI to include session tags when assuming this role during deployment. + * + * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * + * - If you are using a custom bootstrap template, make sure the template includes these permissions. + * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). * + * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required * @default - No Session Tags */ readonly fileAssetPublishingRoleSessionTags?: { [key: string]: string }; /** * Session tags to be used for the image asset publishing role + * This will cause the CDK CLI to include session tags when assuming this role during deployment. * + * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * + * - If you are using a custom bootstrap template, make sure the template includes these permissions. + * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). + * + * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required * @default - No Session Tags */ readonly imageAssetPublishingRoleSessionTags?: { [key: string]: string }; @@ -459,14 +495,14 @@ 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 templateAsset = this.addFileAsset(templateAssetSource); const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, + requiresBootstrapStackVersion: this._requiredBoostrapVersionForAssets, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, }); @@ -476,19 +512,51 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab assumeRoleSessionTags: this.props.deployRoleSessionTags, 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, assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, + requiresBootstrapStackVersion: this._requiredBootstrapVersionForLookup, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, } : undefined, }); } + private get _requiredBoostrapVersionForAssets() { + return Math.max(this._requiredBootstrapVersionForFileAssets, this._requiredBootstrapVersionForImageAssets); + } + + private get _requiredBootstrapVersionForDeployment() { + if (this.props.deployRoleSessionTags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; + } + + private get _requiredBootstrapVersionForLookup() { + if (this.props.lookupRoleSessionTags && this.props.lookupRoleSessionTags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION; + } + + private get _requiredBootstrapVersionForFileAssets() { + if (this.assetManifest.hasFileAssets && this.props.fileAssetPublishingRoleSessionTags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; + } + + private get _requiredBootstrapVersionForImageAssets() { + if (this.assetManifest.hasImageAssets && this.props.imageAssetPublishingRoleSessionTags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; + } + /** * Returns the ARN of the deploy Role. */ 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 ae61ee84bed36..d482e1ceee4ed 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, DefaultStackSynthesizerProps, DockerImageAssetLocation, DockerImageAssetSource, FileAssetLocation, FileAssetSource, AssetManifestBuilder, CfnParameter } 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'; @@ -150,85 +150,6 @@ describe('new style synthesis', () => { expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); }); - test('can set session tags on a custom stack synthesizer', () => { - class CustomSynthesizer extends DefaultStackSynthesizer { - public static readonly MIN_BOOTSTRAP_STACK_VERSION = 6; - private myAssetManifest = new AssetManifestBuilder(); - - public synthesize(session) { - const templateAssetSource = this.synthesizeTemplate(session, DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN); - const templateAsset = this.addFileAsset(templateAssetSource); - - const assetManifestId = this.myAssetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - }); - - this.emitArtifact(session, { - assumeRoleExternalId: this.props.deployRoleExternalId, - assumeRoleArn: DefaultStackSynthesizer.DEFAULT_DEPLOY_ROLE_ARN, - assumeRoleSessionTags: this.props.deployRoleSessionTags, - // Pass in UNDEFINED for the CFN Execution Role Arn: - cloudFormationExecutionRoleArn: undefined, - stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - additionalDependencies: [assetManifestId], - lookupRole: { - arn: DefaultStackSynthesizer.DEFAULT_LOOKUP_ROLE_ARN, - assumeRoleExternalId: this.props.lookupRoleExternalId, - assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: CustomSynthesizer.MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: DefaultStackSynthesizer.DEFAULT_BOOTSTRAP_STACK_VERSION_SSM_PARAMETER, - }, - }); - } - } - - // GIVEN - stack = new Stack(app, 'SessionTagsCustomSynthesizerStack', { - synthesizer: new CustomSynthesizer({ - deployRoleSessionTags: { - Department: 'Engineering-DeployRoleTag', - }, - fileAssetPublishingRoleSessionTags: { - Department: 'Engineering-FileAssetTag', - }, - imageAssetPublishingRoleSessionTags: { - Department: 'Engineering-ImageAssetTag', - }, - lookupRoleSessionTags: { - Department: 'Engineering-LookupRoleTag', - }, - }), - }); - - stack.synthesizer.addFileAsset({ - fileName: __filename, - packaging: FileAssetPackaging.FILE, - sourceHash: 'fileHash', - }); - - stack.synthesizer.addDockerImageAsset({ - directoryName: '.', - sourceHash: 'dockerHash', - }); - - // THEN - const asm = app.synth(); - const manifest = app.synth().getStackByName('SessionTagsCustomSynthesizerStack').manifest; - // Validates that the deploy and lookup role session tags were set in the Manifest: - expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleSessionTags).toEqual({ Department: 'Engineering-DeployRoleTag' }); - expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).lookupRole?.assumeRoleSessionTags).toEqual({ Department: '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['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-ImageAssetTag' }); - expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); - }); - test('customize version parameter', () => { // 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..88702d0404e71 100644 --- a/packages/aws-cdk-lib/core/test/synthesis.test.ts +++ b/packages/aws-cdk-lib/core/test/synthesis.test.ts @@ -283,6 +283,108 @@ 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({ + deployRoleSessionTags: { + Departement: '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({ + lookupRoleSessionTags: { + Departement: '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({ + fileAssetPublishingRoleSessionTags: { + Departement: '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 without a docker image asset, required assets bootstrap version is 6', () => { + + const app = new cdk.App(); + new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + imageAssetPublishingRoleSessionTags: { + Departement: 'Engineering', + }, + }), + }); + + const assembly = app.synth(); + const assetsArtifact = assembly.tryGetArtifact('stack.assets') as cxapi.AssetManifestArtifact; + + expect(assetsArtifact.requiresBootstrapStackVersion).toEqual(6); + + }); + + test('when image asset role session tags are configured with a docker image asset, required assets bootstrap version is 22', () => { + + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack', { + synthesizer: new cdk.DefaultStackSynthesizer({ + imageAssetPublishingRoleSessionTags: { + Departement: 'Engineering', + }, + }), + }); + + stack.synthesizer.addDockerImageAsset({ + sourceHash: 'hash', + executable: ['echo', 'hello mock image'], + }); + + 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/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 From b3c62c9eef1ff7de8f9733e61743e2385444c22b Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 29 Aug 2024 18:54:55 +0300 Subject: [PATCH 53/78] mid work --- .../asset-manifest-builder.ts | 16 +----- .../stack-synthesizers/default-synthesizer.ts | 19 ++----- .../aws-cdk-lib/core/test/synthesis.test.ts | 25 +--------- .../lib/blueprint/stage-deployment.ts | 6 +++ .../test/codepipeline/codepipeline.test.ts | 50 ++++++++++++++++++- 5 files changed, 60 insertions(+), 56 deletions(-) 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 92033b83da5c1..2dc4678f73d74 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 @@ -123,21 +123,7 @@ export class AssetManifestBuilder { * Whether there are any assets registered in the manifest */ public get hasAssets() { - return this.hasFileAssets || this.hasImageAssets; - } - - /** - * Whether there are any file assets registered in the manifest - */ - public get hasFileAssets() { - return Object.keys(this.files).length > 0; - } - - /** - * Whether there are any image assets registered in the manifest - */ - public get hasImageAssets() { - return Object.keys(this.dockerImages).length > 0; + return Object.keys(this.files).length + Object.keys(this.dockerImages).length > 0; } /** 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 fd1e5a0b3899c..4e4e268cc2a49 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 @@ -526,7 +526,10 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab } private get _requiredBoostrapVersionForAssets() { - return Math.max(this._requiredBootstrapVersionForFileAssets, this._requiredBootstrapVersionForImageAssets); + if (this.props.fileAssetPublishingRoleSessionTags || this.props.imageAssetPublishingRoleSessionTags) { + return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; + } + return MIN_BOOTSTRAP_STACK_VERSION; } private get _requiredBootstrapVersionForDeployment() { @@ -543,20 +546,6 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab return MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION; } - private get _requiredBootstrapVersionForFileAssets() { - if (this.assetManifest.hasFileAssets && this.props.fileAssetPublishingRoleSessionTags) { - return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; - } - return MIN_BOOTSTRAP_STACK_VERSION; - } - - private get _requiredBootstrapVersionForImageAssets() { - if (this.assetManifest.hasImageAssets && this.props.imageAssetPublishingRoleSessionTags) { - return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; - } - return MIN_BOOTSTRAP_STACK_VERSION; - } - /** * Returns the ARN of the deploy Role. */ diff --git a/packages/aws-cdk-lib/core/test/synthesis.test.ts b/packages/aws-cdk-lib/core/test/synthesis.test.ts index 88702d0404e71..b9758523bc047 100644 --- a/packages/aws-cdk-lib/core/test/synthesis.test.ts +++ b/packages/aws-cdk-lib/core/test/synthesis.test.ts @@ -344,7 +344,7 @@ describe('synthesis', () => { }); - test('when image asset role session tags are configured without a docker image asset, required assets bootstrap version is 6', () => { + 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', { @@ -358,29 +358,6 @@ describe('synthesis', () => { const assembly = app.synth(); const assetsArtifact = assembly.tryGetArtifact('stack.assets') as cxapi.AssetManifestArtifact; - expect(assetsArtifact.requiresBootstrapStackVersion).toEqual(6); - - }); - - test('when image asset role session tags are configured with a docker image asset, required assets bootstrap version is 22', () => { - - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'stack', { - synthesizer: new cdk.DefaultStackSynthesizer({ - imageAssetPublishingRoleSessionTags: { - Departement: 'Engineering', - }, - }), - }); - - stack.synthesizer.addDockerImageAsset({ - sourceHash: 'hash', - executable: ['echo', 'hello mock image'], - }); - - const assembly = app.synth(); - const assetsArtifact = assembly.tryGetArtifact('stack.assets') as cxapi.AssetManifestArtifact; - expect(assetsArtifact.requiresBootstrapStackVersion).toEqual(22); }); 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..96385a375ed1d 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,12 @@ export class StageDeployment { const stepFromArtifact = new Map(); for (const artifact of assembly.stacks) { + if (artifact.assumeRoleSessionTags && artifact.assumeRoleArn) { + throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with session tags ${JSON.stringify(artifact.assumeRoleSessionTags)}, but assuming roles with session tags is not supported by CodePipeline.`); + } + if (artifact.assumeRoleExternalId && artifact.assumeRoleArn) { + throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with external id ${artifact.assumeRoleExternalId}, but assuming roles with an external id 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..488e23b955116 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,49 @@ test('artifactBucket can be overridden', () => { }); }); +test('throws when deploy role session tags are used', () => { + + const synthesizer = new cdk.DefaultStackSynthesizer({ + deployRoleSessionTags: { + Departement: 'Engineering', + }, + }); + + 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 {"Departement":"Engineering"}, but assuming roles with session tags is not supported by CodePipeline.'); + +}); + +test('throws when deploy role external id is used', () => { + + const synthesizer = new cdk.DefaultStackSynthesizer({ + deployRoleExternalId: 'external-id', + }); + + 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 external id external-id, but assuming roles with an external id 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 +600,7 @@ class ReuseCodePipelineStack extends cdk.Stack { `SampleStage-${testStageEnv.account}-${testStageEnv.region}`, { env: testStageEnv, + ...(props.reuseStageProps ?? {}), }, ); pipeline.addStage(stage); @@ -566,10 +608,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 ?? {}); } } From e5a369e79a4901d6906432c364762ce0274018b0 Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 1 Sep 2024 11:41:40 +0300 Subject: [PATCH 54/78] mid work --- .../cli-integ/resources/cdk-apps/app/app.js | 52 +++---------- .../asset-manifest-builder.ts | 16 ++-- .../stack-synthesizers/default-synthesizer.ts | 78 +++++++++---------- .../stack-synthesizers/stack-synthesizer.ts | 11 ++- .../lib/artifacts/cloudformation-artifact.ts | 14 ++-- .../lib/blueprint/stage-deployment.ts | 7 +- .../test/codepipeline/codepipeline.test.ts | 23 +----- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 35 +++++---- packages/aws-cdk/lib/api/deployments.ts | 15 ++-- packages/aws-cdk/lib/util/asset-publishing.ts | 13 +++- packages/aws-cdk/test/api/fake-sts.ts | 40 ++++++++-- .../aws-cdk/test/api/sdk-provider.test.ts | 4 +- 12 files changed, 152 insertions(+), 156 deletions(-) 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 235e5a3f4291d..d4791f06cb740 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 @@ -427,14 +427,14 @@ class SessionTagsStack extends cdk.Stack { super(parent, id, { ...props, synthesizer: new DefaultStackSynthesizer({ - deployRoleSessionTags: { - 'Department' : 'Engineering', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }] }, - fileAssetPublishingRoleSessionTags: { - 'Department' : 'Engineering', + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }] }, - imageAssetPublishingRoleSessionTags: { - 'Department' : 'Engineering', + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }] }, }) }); @@ -453,43 +453,13 @@ class SessionTagsStack extends cdk.Stack { } } -MIN_BOOTSTRAP_STACK_VERSION = 6; -MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION = 6; -class NoExecutionRoleNoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { - - // This Custom Synthesizer is the same as the Default synthesizer, except that it passes - // undefined for the CloudFormationExecutionRole, since we want it to use the Deploy role instead. - synthesize(session) { - if (this.props.generateBootstrapVersionRule ?? true) { - this.addBootstrapVersionRule(MIN_BOOTSTRAP_STACK_VERSION, this.bootstrapStackVersionSsmParameter); - } +class NoExecutionRoleCustomSynthesizer extends cdk.DefaultStackSynthesizer { - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); - const templateAsset = this.addFileAsset(templateAssetSource); - - const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - }); - - this.emitArtifact(session, { - assumeRoleExternalId: this.props.deployRoleExternalId, - assumeRoleArn: this._deployRoleArn, - assumeRoleSessionTags: this.props.deployRoleSessionTags, - // Pass in UNDEFINED for the CFN Execution Role Arn: + emitArtifact(session, options) { + super.emitArtifact(session, { + ...options, cloudFormationExecutionRoleArn: undefined, - stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, - requiresBootstrapStackVersion: MIN_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - additionalDependencies: [assetManifestId], - lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { - arn: this.lookupRoleArn, - assumeRoleExternalId: this.props.lookupRoleExternalId, - assumeRoleSessionTags: this.props.lookupRoleSessionTags, - requiresBootstrapStackVersion: MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION, - bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, - } : undefined, - }); + }) } } 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 2dc4678f73d74..69ffd44cfe1e1 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,7 +44,7 @@ export class AssetManifestBuilder { region: resolvedOr(stack.region, undefined), assumeRoleArn: target.role?.assumeRoleArn, assumeRoleExternalId: target.role?.assumeRoleExternalId, - assumeRoleSessionTags: target.role?.assumeRoleSessionTags, + assumeRoleAdditionalOptions: target.role?.assumeRoleAdditionalOptions, }); } @@ -83,7 +83,7 @@ export class AssetManifestBuilder { region: resolvedOr(stack.region, undefined), assumeRoleArn: target.role?.assumeRoleArn, assumeRoleExternalId: target.role?.assumeRoleExternalId, - assumeRoleSessionTags: target.role?.assumeRoleSessionTags, + assumeRoleAdditionalOptions: target.role?.assumeRoleAdditionalOptions, }); } @@ -235,11 +235,17 @@ export interface RoleOptions { readonly assumeRoleExternalId?: string; /** - * The session tags to use for the assume role + * Additional options to pass to STS when assuming the role for cloudformation deployments. * - * @default - No Session Tags + * - `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 transtive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly assumeRoleSessionTags?: { [key: string]: string}; + 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 4e4e268cc2a49..922f61de4db83 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 @@ -221,60 +221,52 @@ export interface DefaultStackSynthesizerProps { readonly bootstrapStackVersionSsmParameter?: string; /** - * Session tags to be used when assuming the deploy role. - * This will cause the CDK CLI to include session tags when assuming this role during deployment. + * Additional options to pass to STS when assuming the deploy role. * - * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * - `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 transtive by default. * - * - If you are using a custom bootstrap template, make sure the template includes these permissions. - * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). - * - * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - * @default - No Session Tags + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly deployRoleSessionTags?: { [key: string]: string }; + readonly deployRoleAdditionalOptions?: { [key: string]: any }; /** - * Session tags to be used for the lookup role - * This will cause the CDK CLI to include session tags when assuming this role during deployment. - * - * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * Additional options to pass to STS when assuming the lookup role. * - * - If you are using a custom bootstrap template, make sure the template includes these permissions. - * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). + * - `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 transtive by default. * - * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - * @default - No Session Tags + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly lookupRoleSessionTags?: { [key: string]: string }; + readonly lookupRoleAdditionalOptions?: { [key: string]: any }; /** - * Session tags to be used for the file asset publishing role - * This will cause the CDK CLI to include session tags when assuming this role during deployment. + * Additional options to pass to STS when assuming the file asset publishing. * - * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * - `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 transtive by default. * - * - If you are using a custom bootstrap template, make sure the template includes these permissions. - * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). - * - * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - * @default - No Session Tags + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly fileAssetPublishingRoleSessionTags?: { [key: string]: string }; + readonly fileAssetPublishingRoleAdditionalOptions?: { [key: string]: any }; /** - * Session tags to be used for the image asset publishing role - * This will cause the CDK CLI to include session tags when assuming this role during deployment. - * - * Note that the trust policy of the role must contain permissions for the `sts:TagSession` action. + * Additional options to pass to STS when assuming the image asset publishing. * - * - If you are using a custom bootstrap template, make sure the template includes these permissions. - * - If you are using the default bootstrap template, you will need to rebootstrap your enviroment (once). + * - `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 transtive by default. * - * @see https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_permissions-required - * @default - No Session Tags + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly imageAssetPublishingRoleSessionTags?: { [key: string]: string }; + readonly imageAssetPublishingRoleAdditionalOptions?: { [key: string]: any }; } /** @@ -446,7 +438,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.fileAssetPublishingRoleArn ? { assumeRoleArn: this.fileAssetPublishingRoleArn, assumeRoleExternalId: this.props.fileAssetPublishingExternalId, - assumeRoleSessionTags: this.props.fileAssetPublishingRoleSessionTags, + assumeRoleAdditionalOptions: this.props.fileAssetPublishingRoleAdditionalOptions, } : undefined, }); return this.cloudFormationLocationFromFileAsset(location); @@ -461,7 +453,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab role: this.imageAssetPublishingRoleArn ? { assumeRoleArn: this.imageAssetPublishingRoleArn, assumeRoleExternalId: this.props.imageAssetPublishingExternalId, - assumeRoleSessionTags: this.props.imageAssetPublishingRoleSessionTags, + assumeRoleAdditionalOptions: this.props.imageAssetPublishingRoleAdditionalOptions, } : undefined, }); return this.cloudFormationLocationFromDockerImageAsset(location); @@ -509,7 +501,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab this.emitArtifact(session, { assumeRoleExternalId: this.props.deployRoleExternalId, assumeRoleArn: this._deployRoleArn, - assumeRoleSessionTags: this.props.deployRoleSessionTags, + assumeRoleAdditionalOptions: this.props.deployRoleAdditionalOptions, cloudFormationExecutionRoleArn: this._cloudFormationExecutionRoleArn, stackTemplateAssetObjectUrl: templateAsset.s3ObjectUrlWithPlaceholders, requiresBootstrapStackVersion: this._requiredBootstrapVersionForDeployment, @@ -518,7 +510,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab lookupRole: this.useLookupRoleForStackOperations && this.lookupRoleArn ? { arn: this.lookupRoleArn, assumeRoleExternalId: this.props.lookupRoleExternalId, - assumeRoleSessionTags: this.props.lookupRoleSessionTags, + assumeRoleAdditionalOptions: this.props.lookupRoleAdditionalOptions, requiresBootstrapStackVersion: this._requiredBootstrapVersionForLookup, bootstrapStackVersionSsmParameter: this.bootstrapStackVersionSsmParameter, } : undefined, @@ -526,21 +518,21 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab } private get _requiredBoostrapVersionForAssets() { - if (this.props.fileAssetPublishingRoleSessionTags || this.props.imageAssetPublishingRoleSessionTags) { + 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.deployRoleSessionTags) { + if (this.props.deployRoleAdditionalOptions?.Tags) { return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; } return MIN_BOOTSTRAP_STACK_VERSION; } private get _requiredBootstrapVersionForLookup() { - if (this.props.lookupRoleSessionTags && this.props.lookupRoleSessionTags) { + if (this.props.lookupRoleAdditionalOptions?.Tags) { return MIN_SESSION_TAGS_BOOTSTRAP_STACK_VERSION; } return MIN_LOOKUP_ROLE_BOOTSTRAP_STACK_VERSION; 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 4130afdedc2ec..f89ee468aa9fa 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 @@ -241,11 +241,16 @@ export interface SynthesizeStackArtifactOptions { readonly assumeRoleExternalId?: string; /** - * Session tags to be used on the assume role + * Additional options to pass to STS when assuming the role for cloudformation deployments. * - * @default - No session tags + * - `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 transtive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly assumeRoleSessionTags?: { [key: string]: string}; + readonly assumeRoleAdditionalOptions?: { [key: string]: any }; /** * The role that is passed to CloudFormation to execute the change set 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 7acd21e5c3205..c8fa42098f76f 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 { @@ -93,11 +92,16 @@ export class CloudFormationStackArtifact extends CloudArtifact { readonly assumeRoleExternalId?: string; /** - * Session tags to use when assuming role for cloudformation deployments + * Additional options to pass to STS when assuming the role for cloudformation deployments. * - * @default - No external ID + * - `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 transtive by default. + * + * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/STS.html#assumeRole-property + * @default - No additional options. */ - readonly assumeRoleSessionTags?: { [key: string]: string }; + readonly assumeRoleAdditionalOptions?: { [key: string]: any }; /** * The role that is passed to CloudFormation to execute the change set @@ -167,7 +171,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { this.tags = properties.tags ?? this.tagsFromMetadata(); this.assumeRoleArn = properties.assumeRoleArn; this.assumeRoleExternalId = properties.assumeRoleExternalId; - this.assumeRoleSessionTags = properties.assumeRoleSessionTags; + 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 96385a375ed1d..8fa54c19afa8f 100644 --- a/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts +++ b/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts @@ -61,11 +61,8 @@ export class StageDeployment { const stepFromArtifact = new Map(); for (const artifact of assembly.stacks) { - if (artifact.assumeRoleSessionTags && artifact.assumeRoleArn) { - throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with session tags ${JSON.stringify(artifact.assumeRoleSessionTags)}, but assuming roles with session tags is not supported by CodePipeline.`); - } - if (artifact.assumeRoleExternalId && artifact.assumeRoleArn) { - throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with external id ${artifact.assumeRoleExternalId}, but assuming roles with an external id is not supported by CodePipeline.`); + if (artifact.assumeRoleAdditionalOptions?.Tags && artifact.assumeRoleArn) { + throw new Error(`Deployment of stack ${artifact.stackName} requires assuming the role ${artifact.assumeRoleArn} with session tags ${JSON.stringify(artifact.assumeRoleAdditionalOptions.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 488e23b955116..47463444a463c 100644 --- a/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts @@ -526,8 +526,8 @@ test('artifactBucket can be overridden', () => { test('throws when deploy role session tags are used', () => { const synthesizer = new cdk.DefaultStackSynthesizer({ - deployRoleSessionTags: { - Departement: 'Engineering', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Enginerring' }], }, }); @@ -544,25 +544,6 @@ test('throws when deploy role session tags are used', () => { }); -test('throws when deploy role external id is used', () => { - - const synthesizer = new cdk.DefaultStackSynthesizer({ - deployRoleExternalId: 'external-id', - }); - - 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 external id external-id, but assuming roles with an external id is not supported by CodePipeline.'); - -}); - interface ReuseCodePipelineStackProps extends cdk.StackProps { reuseCrossRegionSupportStacks?: boolean; reuseStageProps?: ReuseStageProps; 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 dac83c3c6eee2..7d38e51c7c400 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -13,6 +13,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 +201,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, options.assumeRoleSessionTags, 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,26 +365,26 @@ export class SdkProvider { masterCredentials: Exclude, roleArn: string, externalId: string | undefined, - sessionTags: { [key: string]: string } | undefined, + additionalOptions: AssumeRoleAdditionalOptions | undefined, region: string | undefined) { debug(`Assuming role '${roleArn}'.`); + if ( + additionalOptions && + additionalOptions.Tags && + additionalOptions.Tags.length > 0 && + additionalOptions.TransitiveTagKeys === undefined + ) { + additionalOptions.TransitiveTagKeys = additionalOptions.Tags?.map((t) => t.Key); + } + region = region ?? this.defaultRegion; const creds = new AWS.ChainableTemporaryCredentials({ params: { RoleArn: roleArn, - ...externalId ? { ExternalId: externalId } : {}, - ...sessionTags ? { - Tags: Object.entries(sessionTags).map(([key, value]) => ({ - Key: key, - Value: value, - })), - } : {}, - // We pass in the all Session Tags keys as TransitiveTagKeys so that - // they persist during role chaining. For more information, see: - // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining - ...sessionTags ? { TransitiveTagKeys: Object.keys(sessionTags) } : {}, + ExternalId: externalId, RoleSessionName: `aws-cdk-${safeUsername()}`, + ...additionalOptions, }, stsConfig: { region, @@ -527,7 +534,7 @@ export interface CredentialsOptions { /** * Session tags required to assume the given role. */ - readonly assumeRoleSessionTags?: { [key: string]: string }; + readonly assumeRoleAdditionalOptions?: AssumeRoleAdditionalOptions; } /** diff --git a/packages/aws-cdk/lib/api/deployments.ts b/packages/aws-cdk/lib/api/deployments.ts index 88ae463acc8b2..e72b7ec151962 100644 --- a/packages/aws-cdk/lib/api/deployments.ts +++ b/packages/aws-cdk/lib/api/deployments.ts @@ -492,7 +492,7 @@ export class Deployments { const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, mode, { assumeRoleArn: arns.assumeRoleArn, assumeRoleExternalId: stack.assumeRoleExternalId, - assumeRoleSessionTags: stack.assumeRoleSessionTags, + assumeRoleAdditionalOptions: stack.assumeRoleAdditionalOptions, }); return { @@ -539,7 +539,7 @@ export class Deployments { const stackSdk = await this.cachedSdkForEnvironment(resolvedEnvironment, Mode.ForReading, { assumeRoleArn: arns.lookupRoleArn, assumeRoleExternalId: stack.lookupRole?.assumeRoleExternalId, - assumeRoleSessionTags: stack.lookupRole?.assumeRoleSessionTags, + assumeRoleAdditionalOptions: stack.lookupRole?.assumeRoleAdditionalOptions, }); const envResources = this.environmentResources.for(resolvedEnvironment, stackSdk.sdk); @@ -673,14 +673,19 @@ export class Deployments { mode: Mode, options?: CredentialsOptions, ) { - const cacheKey = [ + const cacheKeyElements = [ environment.account, environment.region, `${mode}`, options?.assumeRoleArn ?? '', options?.assumeRoleExternalId ?? '', - options?.assumeRoleSessionTags ?? '', - ].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/util/asset-publishing.ts b/packages/aws-cdk/lib/util/asset-publishing.ts index 3c90973aef49f..1a7c5a0c9fc78 100644 --- a/packages/aws-cdk/lib/util/asset-publishing.ts +++ b/packages/aws-cdk/lib/util/asset-publishing.ts @@ -166,13 +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, - assumeRoleSessionTags: options.assumeRoleSessionTags, quiet: options.quiet, - }); + }; + + if (options.assumeRoleAdditionalOptions) { + cacheKeyMap.assumeRoleAdditionalOptions = options.assumeRoleAdditionalOptions; + } + + const cacheKey = JSON.stringify(cacheKeyMap); const maybeSdk = this.sdkCache.get(cacheKey); if (maybeSdk) { @@ -182,7 +187,7 @@ export class PublishingAws implements cdk_assets.IAws { const sdk = (await this.aws.forEnvironment(env, Mode.ForWriting, { assumeRoleArn: options.assumeRoleArn, assumeRoleExternalId: options.assumeRoleExternalId, - assumeRoleSessionTags: options.assumeRoleSessionTags, + 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 f42c59740c662..a839483a46ead 100644 --- a/packages/aws-cdk/test/api/fake-sts.ts +++ b/packages/aws-cdk/test/api/fake-sts.ts @@ -2,6 +2,7 @@ import * as nock from 'nock'; import * as uuid from 'uuid'; import * as xmlJs from 'xml-js'; +import type { AssumeRoleAdditionalOptions } from '../../lib/api/aws-auth/sdk-provider'; interface RegisteredIdentity { readonly account: string; @@ -21,8 +22,7 @@ interface AssumedRole { readonly serialNumber: string; readonly tokenCode: string; readonly roleSessionName: string; - readonly sessionTags?: { [key: string]: string }; - readonly transitiveTagKeys?: string[]; + readonly assumeRoleAdditionalOptions?: AssumeRoleAdditionalOptions; } /** @@ -168,8 +168,8 @@ export class FakeSts { * @param body - the parsed body of the mockRequest * @returns session tags dictionary */ - private parseSessionTagsFromRequestBody(body: Record): { [key: string]: string } { - const tags: { [key: string]: string } = {}; + private decodeTagsFromRequestBody(body: Record): AWS.STS.Tag[] { + const tags: AWS.STS.Tag[] = []; for (const key in body) { if (key.startsWith('Tags.member.') && key.endsWith('.Key')) { @@ -177,7 +177,7 @@ export class FakeSts { const tagKey = body[key]; const tagValueKey = `Tags.member.${tagIndex}.Value`; if (tagValueKey in body) { - tags[tagKey] = body[tagValueKey]; + tags.push({ Key: tagKey, Value: body[tagValueKey] }); } } } @@ -185,18 +185,42 @@ export class FakeSts { return tags; } + /** + * This function parses session tags from the STS mock request into a dictionary. This is necessary because + * the STS request body writes these dictionary values in the format Tags.member.X.Key and Tags.member.X.Value. + * + * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html + * @param body - the parsed body of the mockRequest + * @returns session tags dictionary + */ + private decodeTransitiveTagKeysFromRequestBody(body: Record): string[] { + const tags: string[] = []; + + for (const key in body) { + if (key.startsWith('TransitiveTagKeys.member.')) { + const transtiveTagKey = key.split('.')[2]; + tags.push(transtiveTagKey); + } + } + + return tags; + } + private handleAssumeRole(identity: RegisteredIdentity, mockRequest: MockRequest): Record { this.checkForFailure(mockRequest.parsedBody.RoleArn); - const parsedSessionTags = this.parseSessionTagsFromRequestBody(mockRequest.parsedBody); + const tags = this.decodeTagsFromRequestBody(mockRequest.parsedBody); + const transitiveTagKeys = this.decodeTransitiveTagKeysFromRequestBody(mockRequest.parsedBody); this.assumedRoles.push({ roleArn: mockRequest.parsedBody.RoleArn, roleSessionName: mockRequest.parsedBody.RoleSessionName, serialNumber: mockRequest.parsedBody.SerialNumber, tokenCode: mockRequest.parsedBody.TokenCode, - sessionTags: parsedSessionTags, - transitiveTagKeys: Object.keys(parsedSessionTags), + assumeRoleAdditionalOptions: { + Tags: tags, + TransitiveTagKeys: transitiveTagKeys, + }, }); const roleArn = mockRequest.parsedBody.RoleArn; diff --git a/packages/aws-cdk/test/api/sdk-provider.test.ts b/packages/aws-cdk/test/api/sdk-provider.test.ts index 6376e0f885465..9fb833aebc878 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -361,8 +361,8 @@ describe('with intercepted network calls', () => { { assumeRoleArn: 'arn:aws:role', assumeRoleExternalId: 'bruh', - assumeRoleSessionTags: { - Department: 'Engineering', + assumeRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }], }, })).sdk as SDK; await sdk.currentAccount(); From 2a9ef1f35d0de81017d71dc974294e88514477f6 Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 1 Sep 2024 23:37:13 +0300 Subject: [PATCH 55/78] mid work --- ...l => session-tags.all-roles-deny-all.yaml} | 0 ...=> session-tags.deploy-role-deny-sqs.yaml} | 0 .../cli-integ/resources/cdk-apps/app/app.js | 4 +-- .../bootstrapping.integtest.ts | 6 ++-- .../stack-synthesizers/default-synthesizer.ts | 2 +- .../new-style-synthesis.test.ts | 24 +++++++------- .../aws-cdk-lib/core/test/synthesis.test.ts | 16 +++++----- packages/aws-cdk/test/api/fake-sts.ts | 6 ++-- .../aws-cdk/test/api/sdk-provider.test.ts | 4 +-- yarn.lock | 31 ++----------------- 10 files changed, 33 insertions(+), 60 deletions(-) rename packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/{custom-bootstrap-with-session-tags.yaml => session-tags.all-roles-deny-all.yaml} (100%) rename packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/{custom-bootstrap-with-deploy-role-labmda-permissions.yaml => session-tags.deploy-role-deny-sqs.yaml} (100%) diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml similarity index 100% rename from packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-session-tags.yaml rename to packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.all-roles-deny-all.yaml diff --git a/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml b/packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml similarity index 100% rename from packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/custom-bootstrap-with-deploy-role-labmda-permissions.yaml rename to packages/@aws-cdk-testing/cli-integ/resources/bootstrap-templates/session-tags.deploy-role-deny-sqs.yaml 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 d4791f06cb740..6e6777bd3d139 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 @@ -468,8 +468,8 @@ class SessionTagsWithNoExecutionRoleCustomSynthesizerStack extends cdk.Stack { super(parent, id, { ...props, synthesizer: new NoExecutionRoleCustomSynthesizer({ - deployRoleSessionTags: { - 'Department' : 'Engineering', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }] }, }) }); 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 0f9219c3a557c..197ec46f6343a 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 @@ -82,13 +82,12 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); -// Custom Bootstrap test with Session Tags on the Deploy, FileAssetPublishing and ImageAssetPublishing Roles integTest('can deploy with session tags on the deploy, 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', 'custom-bootstrap-with-session-tags.yaml'), + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.all-roles-deny-all.yaml'), }); await fixture.cdkDeploy('session-tags', { @@ -100,13 +99,12 @@ integTest('can deploy with session tags on the deploy, file asset, and image ass }); })); -// Custom Bootstrap test without CloudFormationExecutionRole and Session Tags on the DeployRole 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', 'custom-bootstrap-with-deploy-role-labmda-permissions.yaml'), + bootstrapTemplate: path.join(__dirname, '..', '..', 'resources', 'bootstrap-templates', 'session-tags.deploy-role-deny-sqs.yaml'), }); await fixture.cdkDeploy('session-tags-with-custom-synthesizer', { 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 922f61de4db83..02b5001b647d2 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 @@ -358,7 +358,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab private bootstrapStackVersionSsmParameter?: string; private assetManifest = new AssetManifestBuilder(); - constructor(protected readonly props: DefaultStackSynthesizerProps = {}) { + constructor(private readonly props: DefaultStackSynthesizerProps = {}) { super(); this.useLookupRoleForStackOperations = props.useLookupRoleForStackOperations ?? true; 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 d482e1ceee4ed..bd97490845cc2 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 @@ -109,17 +109,17 @@ describe('new style synthesis', () => { // GIVEN stack = new Stack(app, 'SessionTagsStack', { synthesizer: new DefaultStackSynthesizer({ - deployRoleSessionTags: { - Department: 'Engineering-DeployRoleTag', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-DeployRoleTag' }], }, - fileAssetPublishingRoleSessionTags: { - Department: 'Engineering-FileAssetTag', + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-FileAssetTag' }], }, - imageAssetPublishingRoleSessionTags: { - Department: 'Engineering-ImageAssetTag', + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }], }, - lookupRoleSessionTags: { - Department: 'Engineering-LookupRoleTag', + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }], }, }), }); @@ -139,15 +139,15 @@ describe('new style synthesis', () => { const asm = app.synth(); const manifest = app.synth().getStackByName('SessionTagsStack').manifest; // Validates that the deploy and lookup role session tags were set in the Manifest: - expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).assumeRoleSessionTags).toEqual({ Department: 'Engineering-DeployRoleTag' }); - expect((manifest.properties as cxschema.AwsCloudFormationStackProperties).lookupRole?.assumeRoleSessionTags).toEqual({ Department: 'Engineering-LookupRoleTag' }); + 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['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-ImageAssetTag' }); - expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleSessionTags).toEqual({ Department: 'Engineering-FileAssetTag' }); + expect(assetManifestJSON.dockerImages?.dockerHash.destinations['current_account-current_region'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }]); + expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-FileAssetTag' }]); }); test('customize version parameter', () => { diff --git a/packages/aws-cdk-lib/core/test/synthesis.test.ts b/packages/aws-cdk-lib/core/test/synthesis.test.ts index b9758523bc047..8b67b371e0be8 100644 --- a/packages/aws-cdk-lib/core/test/synthesis.test.ts +++ b/packages/aws-cdk-lib/core/test/synthesis.test.ts @@ -289,8 +289,8 @@ describe('synthesis', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'stack', { synthesizer: new cdk.DefaultStackSynthesizer({ - deployRoleSessionTags: { - Departement: 'Engineering', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], }, }), }); @@ -313,8 +313,8 @@ describe('synthesis', () => { const app = new cdk.App(); new cdk.Stack(app, 'stack', { synthesizer: new cdk.DefaultStackSynthesizer({ - lookupRoleSessionTags: { - Departement: 'Engineering', + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], }, }), }); @@ -331,8 +331,8 @@ describe('synthesis', () => { const app = new cdk.App(); new cdk.Stack(app, 'stack', { synthesizer: new cdk.DefaultStackSynthesizer({ - fileAssetPublishingRoleSessionTags: { - Departement: 'Engineering', + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], }, }), }); @@ -349,8 +349,8 @@ describe('synthesis', () => { const app = new cdk.App(); new cdk.Stack(app, 'stack', { synthesizer: new cdk.DefaultStackSynthesizer({ - imageAssetPublishingRoleSessionTags: { - Departement: 'Engineering', + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Departement', Value: 'Engineering' }], }, }), }); diff --git a/packages/aws-cdk/test/api/fake-sts.ts b/packages/aws-cdk/test/api/fake-sts.ts index a839483a46ead..a4b1984240954 100644 --- a/packages/aws-cdk/test/api/fake-sts.ts +++ b/packages/aws-cdk/test/api/fake-sts.ts @@ -186,12 +186,12 @@ export class FakeSts { } /** - * This function parses session tags from the STS mock request into a dictionary. This is necessary because - * the STS request body writes these dictionary values in the format Tags.member.X.Key and Tags.member.X.Value. + * This function parses transitive session tags keys from the STS mock request into an array. This is necessary because + * the STS request body writes these array values in the format TransitiveTagKeys.member.N * * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html * @param body - the parsed body of the mockRequest - * @returns session tags dictionary + * @returns transitive session tags keys array */ private decodeTransitiveTagKeysFromRequestBody(body: Record): string[] { const tags: string[] = []; diff --git a/packages/aws-cdk/test/api/sdk-provider.test.ts b/packages/aws-cdk/test/api/sdk-provider.test.ts index 9fb833aebc878..746d397137bd0 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -369,8 +369,8 @@ describe('with intercepted network calls', () => { // THEN expect(fakeSts.assumedRoles[0]).toEqual(expect.objectContaining({ - sessionTags: { - Department: 'Engineering', + assumeRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }], }, transitiveTagKeys: ['Department'], })); diff --git a/yarn.lock b/yarn.lock index 2a7db95cf30bd..0ae663f39056d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15554,16 +15554,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: +"string-width-cjs@npm:string-width@^4.2.0", string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -15636,7 +15627,7 @@ stringify-package@^1.0.1: resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -15650,13 +15641,6 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -16643,7 +16627,7 @@ workerpool@^6.5.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -16661,15 +16645,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 67160f1c2ee33ae03c7f09f11a30698c21176010 Mon Sep 17 00:00:00 2001 From: epolon Date: Mon, 2 Sep 2024 10:41:33 +0300 Subject: [PATCH 56/78] mid work --- build.assume-role-additional-options.sh | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 build.assume-role-additional-options.sh diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh new file mode 100755 index 0000000000000..40f88e205cf28 --- /dev/null +++ b/build.assume-role-additional-options.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -euo pipefail + +set_dependency_version() { + local manifest_path="package.json" + local dependency="$1" + local version="$2" + + node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.dependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" +} + +workdir=$(mktemp -d) + +cd ${workdir} + +echo "Working directory: ${workdir}" + +git clone https://github.com/cdklabs/cloud-assembly-schema.git -b sumughan/add-session-tags-to-schema --single-branch +git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch +git clone https://github.com/aws/aws-cdk.git --single-branch -b sumughan/session-tags + +(cd cloud-assembly-schema && yarn install && yarn bump && yarn compile && yarn package:js) + +cloud_assembly_schema_pkg_version=$(jq -r '.version' cloud-assembly-schema/package.json) +cloud_assembly_schema_pkg="${PWD}/cloud-assembly-schema/dist/js/cloud-assembly-schema@${cloud_assembly_schema_pkg_version}.jsii.tgz" + +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk-lib && yarn install) +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd aws-cdk/packages/aws-cdk-lib && npx lerna run build --scope=aws-cdk-lib --skip-nx-cache) + +# cp ${cloud_assembly_schema_pkg} cdk-assets/cloud-assembly-schema.tgz +# (cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +# (cd cdk-assets && yarn install && yarn bump && yarn compile) +# (cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +# (cd cdk-assets && yarn package) + +# cdk_assets_pkg_version=$(jq -r '.version' cdk-assets/package.json) +# cdk_assets_pkg="${PWD}/cdk-assets/dist/js/cdk-assets-${cdk_assets_pkg_version}.tgz" + +# echo "cloud-assembly-schema package: ${cloud_assembly_schema_pkg}" +# echo "cdk-assets package: ${cdk_assets_pkg}" + From a1bfbdf43db8c5bfb208344e537d062b967372e9 Mon Sep 17 00:00:00 2001 From: epolon Date: Mon, 2 Sep 2024 18:29:57 +0300 Subject: [PATCH 57/78] mid work --- build.assume-role-additional-options.sh | 93 +++++++++++++++++++++---- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh index 40f88e205cf28..b4af4093b0f71 100755 --- a/build.assume-role-additional-options.sh +++ b/build.assume-role-additional-options.sh @@ -10,36 +10,105 @@ set_dependency_version() { node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.dependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" } -workdir=$(mktemp -d) +set_peer_dependency_version() { + local manifest_path="package.json" + local dependency="$1" + local version="$2" + + node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.peerDependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" +} + +set_dev_dependency_version() { + local manifest_path="package.json" + local dependency="$1" + local version="$2" + + node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.devDependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" +} -cd ${workdir} +builddir=$(mktemp -d) -echo "Working directory: ${workdir}" +cd ${builddir} + +echo "Working directory: ${builddir}" git clone https://github.com/cdklabs/cloud-assembly-schema.git -b sumughan/add-session-tags-to-schema --single-branch git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch git clone https://github.com/aws/aws-cdk.git --single-branch -b sumughan/session-tags +echo "Aligning versions in aws-cdk" +(cd aws-cdk && yarn install && ./scripts/align-version.sh) + +echo "Building @aws-cdk/cloud-assembly-schema" (cd cloud-assembly-schema && yarn install && yarn bump && yarn compile && yarn package:js) cloud_assembly_schema_pkg_version=$(jq -r '.version' cloud-assembly-schema/package.json) cloud_assembly_schema_pkg="${PWD}/cloud-assembly-schema/dist/js/cloud-assembly-schema@${cloud_assembly_schema_pkg_version}.jsii.tgz" +echo "Building aws-cdk-lib" cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz (cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/aws-cdk-lib && yarn install) (cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") (cd aws-cdk/packages/aws-cdk-lib && npx lerna run build --scope=aws-cdk-lib --skip-nx-cache) +(cd aws-cdk/packages/aws-cdk-lib && npx cdk-package --targets js) +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") + +aws_cdk_lib_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk-lib/package.json) +aws_cdk_lib_pkg="${PWD}/aws-cdk/packages/aws-cdk-lib/dist/js/aws-cdk-lib@${aws_cdk_lib_pkg_version}.jsii.tgz" + +echo "Building @aws-cdk/cx-api" +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/cx-api/cloud-assembly-schema.tgz +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/cx-api && yarn install) +(cd aws-cdk/packages/@aws-cdk/cx-api && npx lerna run build --scope=aws-cdk-lib --skip-nx-cache) +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd aws-cdk/packages/@aws-cdk/cx-api && npx cdk-package --targets js) +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") + +cx_api_pkg_version=$(jq -r '.version' aws-cdk/packages/@aws-cdk/cx-api/package.json) +cx_api_pkg="${PWD}/aws-cdk/packages/@aws-cdk/cx-api/dist/js/cx-api@${cx_api_pkg_version}.jsii.tgz" -# cp ${cloud_assembly_schema_pkg} cdk-assets/cloud-assembly-schema.tgz -# (cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -# (cd cdk-assets && yarn install && yarn bump && yarn compile) -# (cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") -# (cd cdk-assets && yarn package) +echo "Building cdk-assets" +cp ${cloud_assembly_schema_pkg} cdk-assets/cloud-assembly-schema.tgz +(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd cdk-assets && yarn install && yarn bump && yarn compile) +(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd cdk-assets && yarn package) -# cdk_assets_pkg_version=$(jq -r '.version' cdk-assets/package.json) -# cdk_assets_pkg="${PWD}/cdk-assets/dist/js/cdk-assets-${cdk_assets_pkg_version}.tgz" +cdk_assets_pkg_version=$(jq -r '.version' cdk-assets/package.json) +cdk_assets_pkg="${PWD}/cdk-assets/dist/js/cdk-assets-${cdk_assets_pkg_version}.tgz" + +echo "Building aws-cdk" +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz +cp ${cdk_assets_pkg} aws-cdk/packages/aws-cdk/cdk-assets.tgz +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "./cdk-assets.tgz") +(cd aws-cdk/packages/aws-cdk && yarn install) +(cd aws-cdk/packages/aws-cdk && npx lerna run build --scope=aws-cdk --skip-nx-cache) +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "${cdk_assets_pkg_version}") +(cd aws-cdk/packages/aws-cdk && yarn package) + +aws_cdk_pkg_version=$(jq -r '.version' packages/aws-cdk/package.json) +aws_cdk_pkg="${PWD}/packages/aws-cdk/dist/js/aws-cdk-${aws_cdk_pkg_version}.tgz" + +echo "cloud-assembly-schema package: ${cloud_assembly_schema_pkg}" +echo "cdk-assets package: ${cdk_assets_pkg}" +echo "aws-cdk-lib package: ${aws_cdk_lib_pkg}" +echo "aws-cdk package: ${aws_cdk_pkg}" + +workdir=$(mktemp -d) -# echo "cloud-assembly-schema package: ${cloud_assembly_schema_pkg}" -# echo "cdk-assets package: ${cdk_assets_pkg}" +mkdir aws-cdk-assume-role-additional-options +npm install --no-save ${cloud_assembly_schema_pkg} +npm install --no-save ${cx_api_pkg} +npm install --no-save ${cdk_assets_pkg} +npm install --no-save ${aws_cdk_lib_pkg} +npm install --no-save ${aws_cdk_pkg} +touch main.ts +echo "Done: ${PWD}" \ No newline at end of file From d9374a28871115033654f1af6609ca77f22b0adc Mon Sep 17 00:00:00 2001 From: epolon Date: Wed, 4 Sep 2024 21:52:21 +0300 Subject: [PATCH 58/78] mid work --- build.assume-role-additional-options.sh | 105 ++++++++++++++++-------- packages/aws-cdk-lib/core/README.md | 22 ++--- 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh index b4af4093b0f71..a35e2f0899ff0 100755 --- a/build.assume-role-additional-options.sh +++ b/build.assume-role-additional-options.sh @@ -36,79 +36,118 @@ git clone https://github.com/cdklabs/cloud-assembly-schema.git -b sumughan/add-s git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch git clone https://github.com/aws/aws-cdk.git --single-branch -b sumughan/session-tags -echo "Aligning versions in aws-cdk" -(cd aws-cdk && yarn install && ./scripts/align-version.sh) - echo "Building @aws-cdk/cloud-assembly-schema" -(cd cloud-assembly-schema && yarn install && yarn bump && yarn compile && yarn package:js) +(cd cloud-assembly-schema && yarn install && yarn compile && yarn package:js) cloud_assembly_schema_pkg_version=$(jq -r '.version' cloud-assembly-schema/package.json) cloud_assembly_schema_pkg="${PWD}/cloud-assembly-schema/dist/js/cloud-assembly-schema@${cloud_assembly_schema_pkg_version}.jsii.tgz" -echo "Building aws-cdk-lib" +allow_all_version_range=">=0.0.0" + +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/cx-api/cloud-assembly-schema.tgz +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/integ-runner/cloud-assembly-schema.tgz +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk-lib && yarn install) -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") -(cd aws-cdk/packages/aws-cdk-lib && npx lerna run build --scope=aws-cdk-lib --skip-nx-cache) -(cd aws-cdk/packages/aws-cdk-lib && npx cdk-package --targets js) -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -aws_cdk_lib_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk-lib/package.json) -aws_cdk_lib_pkg="${PWD}/aws-cdk/packages/aws-cdk-lib/dist/js/aws-cdk-lib@${aws_cdk_lib_pkg_version}.jsii.tgz" +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") echo "Building @aws-cdk/cx-api" cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/cx-api/cloud-assembly-schema.tgz (cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/@aws-cdk/cx-api && yarn install) -(cd aws-cdk/packages/@aws-cdk/cx-api && npx lerna run build --scope=aws-cdk-lib --skip-nx-cache) -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/@aws-cdk/cx-api && npx lerna run build --scope=@aws-cdk/cx-api) (cd aws-cdk/packages/@aws-cdk/cx-api && npx cdk-package --targets js) -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") cx_api_pkg_version=$(jq -r '.version' aws-cdk/packages/@aws-cdk/cx-api/package.json) cx_api_pkg="${PWD}/aws-cdk/packages/@aws-cdk/cx-api/dist/js/cx-api@${cx_api_pkg_version}.jsii.tgz" echo "Building cdk-assets" cp ${cloud_assembly_schema_pkg} cdk-assets/cloud-assembly-schema.tgz +cp ${cx_api_pkg} cdk-assets/cx-api.tgz (cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd cdk-assets && yarn install && yarn bump && yarn compile) -(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") +(cd cdk-assets && set_dependency_version "@aws-cdk/cx-api" "./cx-api.tgz") +(cd cdk-assets && yarn install && yarn compile) +(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd cdk-assets && set_dependency_version "@aws-cdk/cx-api" "${allow_all_version_range}") (cd cdk-assets && yarn package) cdk_assets_pkg_version=$(jq -r '.version' cdk-assets/package.json) cdk_assets_pkg="${PWD}/cdk-assets/dist/js/cdk-assets-${cdk_assets_pkg_version}.tgz" +echo "Building aws-cdk-lib" +cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk-lib && yarn install) +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/aws-cdk-lib && npx lerna run build --scope=aws-cdk-lib) +(cd aws-cdk/packages/aws-cdk-lib && npx cdk-package --targets js) + +aws_cdk_lib_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk-lib/package.json) +aws_cdk_lib_pkg="${PWD}/aws-cdk/packages/aws-cdk-lib/dist/js/aws-cdk-lib@${aws_cdk_lib_pkg_version}.jsii.tgz" + echo "Building aws-cdk" cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz cp ${cdk_assets_pkg} aws-cdk/packages/aws-cdk/cdk-assets.tgz +cp ${cx_api_pkg} aws-cdk/packages/aws-cdk/cx-api.tgz + +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") + (cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "./cdk-assets.tgz") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cx-api" "./cx-api.tgz") (cd aws-cdk/packages/aws-cdk && yarn install) -(cd aws-cdk/packages/aws-cdk && npx lerna run build --scope=aws-cdk --skip-nx-cache) -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${cloud_assembly_schema_pkg_version}") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "${cdk_assets_pkg_version}") + +(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") + +echo "Building @aws-cdk/cloudformation-diff" +(cd aws-cdk/packages/@aws-cdk/cloudformation-diff && npx lerna run build --scope=@aws-cdk/cloudformation-diff) + +echo "Building @aws-cdk/region-info" +(cd aws-cdk/packages/@aws-cdk/region-info && npx lerna run build --scope=@aws-cdk/region-info) + +(cd aws-cdk/packages/aws-cdk && yarn build --fix) +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "${allow_all_version_range}") +(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cx-api" "${allow_all_version_range}") (cd aws-cdk/packages/aws-cdk && yarn package) -aws_cdk_pkg_version=$(jq -r '.version' packages/aws-cdk/package.json) -aws_cdk_pkg="${PWD}/packages/aws-cdk/dist/js/aws-cdk-${aws_cdk_pkg_version}.tgz" +aws_cdk_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk/package.json) +aws_cdk_pkg="${PWD}/aws-cdk/packages/aws-cdk/dist/js/aws-cdk-${aws_cdk_pkg_version}.tgz" echo "cloud-assembly-schema package: ${cloud_assembly_schema_pkg}" +echo "cx-api package: ${cx_api_pkg}" echo "cdk-assets package: ${cdk_assets_pkg}" echo "aws-cdk-lib package: ${aws_cdk_lib_pkg}" echo "aws-cdk package: ${aws_cdk_pkg}" workdir=$(mktemp -d) -mkdir aws-cdk-assume-role-additional-options -npm install --no-save ${cloud_assembly_schema_pkg} -npm install --no-save ${cx_api_pkg} -npm install --no-save ${cdk_assets_pkg} -npm install --no-save ${aws_cdk_lib_pkg} -npm install --no-save ${aws_cdk_pkg} -touch main.ts +(cd ${workdir} && mv ${cloud_assembly_schema_pkg} "cloud-assembly-schema.tgz") +(cd ${workdir} && mv ${cx_api_pkg} "cx-api.tgz") +(cd ${workdir} && mv ${cdk_assets_pkg} "cdk-assets.tgz") +(cd ${workdir} && mv ${aws_cdk_lib_pkg} "aws-cdk-lib.tgz") +(cd ${workdir} && mv ${aws_cdk_pkg} "aws-cdk.tgz") + +(cd ${workdir} && npm install "./cloud-assembly-schema.tgz") +(cd ${workdir} && npm install "./cx-api.tgz") +(cd ${workdir} && npm install "./cdk-assets.tgz") +(cd ${workdir} && npm install "./aws-cdk-lib.tgz") +(cd ${workdir} && npm install "./aws-cdk.tgz") +(cd ${workdir} && touch main.ts) -echo "Done: ${PWD}" \ No newline at end of file +echo "Done: ${workdir}" \ No newline at end of file diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 7bc5d1cbb79e9..c09e5dd3e678f 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -169,17 +169,17 @@ class MyStack extends cdk.Stack { super(parent, id, { ...props, synthesizer: new DefaultStackSynthesizer({ - deployRoleSessionTags: { - Department : 'Engineering', + deployRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, - fileAssetPublishingRoleSessionTags: { - Department: 'Engineering', + fileAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, - imageAssetPublishingRoleSessionTags: { - Department: 'Engineering', + imageAssetPublishingRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, - lookupRoleSessionTags: { - Department: 'Engineering', + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, }) }); @@ -187,13 +187,13 @@ class MyStack extends cdk.Stack { } ``` -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. +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, you will need to rebootstrap your enviroment (once). +- 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 From 5d7b601b600ca7fca6d17bbff0f7e7717efa44d3 Mon Sep 17 00:00:00 2001 From: epolon Date: Tue, 10 Sep 2024 20:54:06 +0300 Subject: [PATCH 59/78] mid work --- packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) 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 7d38e51c7c400..89808864076ae 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -369,22 +369,16 @@ export class SdkProvider { region: string | undefined) { debug(`Assuming role '${roleArn}'.`); - if ( - additionalOptions && - additionalOptions.Tags && - additionalOptions.Tags.length > 0 && - additionalOptions.TransitiveTagKeys === undefined - ) { - additionalOptions.TransitiveTagKeys = additionalOptions.Tags?.map((t) => t.Key); - } - region = region ?? this.defaultRegion; const creds = new AWS.ChainableTemporaryCredentials({ params: { RoleArn: roleArn, ExternalId: externalId, RoleSessionName: `aws-cdk-${safeUsername()}`, - ...additionalOptions, + TransitiveTagKeys: additionalOptions?.Tags + ? additionalOptions.Tags.map((t) => t.Key) + : undefined, + ...(additionalOptions ?? {}), }, stsConfig: { region, From a51622aed4a4515273a53a0927d004cf2f1392c4 Mon Sep 17 00:00:00 2001 From: epolon Date: Wed, 11 Sep 2024 10:31:02 +0300 Subject: [PATCH 60/78] mid work --- build.assume-role-additional-options.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh index a35e2f0899ff0..a5eca5e02430e 100755 --- a/build.assume-role-additional-options.sh +++ b/build.assume-role-additional-options.sh @@ -150,4 +150,5 @@ workdir=$(mktemp -d) (cd ${workdir} && npm install "./aws-cdk.tgz") (cd ${workdir} && touch main.ts) -echo "Done: ${workdir}" \ No newline at end of file +echo "Workdir: ${workdir}" +echo "Buildir: ${builddir}" \ No newline at end of file From 30ce9f8439bc11a62077a734416fb9f5ebd31134 Mon Sep 17 00:00:00 2001 From: epolon Date: Wed, 11 Sep 2024 12:23:03 +0300 Subject: [PATCH 61/78] mid work --- .../session-tags.all-roles-deny-all.yaml | 2 +- .../session-tags.deploy-role-deny-sqs.yaml | 2 +- .../cli-integ/resources/cdk-apps/app/app.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) 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 index 955f060424967..ee0eba5f15282 100644 --- 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 @@ -667,7 +667,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-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 index 265341e7221b5..94e789a0493ce 100644 --- 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 @@ -665,7 +665,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-testing/cli-integ/resources/cdk-apps/app/app.js b/packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js index 6e6777bd3d139..920fd39735b8b 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 @@ -428,13 +428,13 @@ class SessionTagsStack extends cdk.Stack { ...props, synthesizer: new DefaultStackSynthesizer({ deployRoleAdditionalOptions: { - Tags: [{ Key: 'Departement', Value: 'Engineering' }] + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, fileAssetPublishingRoleAdditionalOptions: { - Tags: [{ Key: 'Departement', Value: 'Engineering' }] + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, imageAssetPublishingRoleAdditionalOptions: { - Tags: [{ Key: 'Departement', Value: 'Engineering' }] + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, }) }); @@ -469,7 +469,7 @@ class SessionTagsWithNoExecutionRoleCustomSynthesizerStack extends cdk.Stack { ...props, synthesizer: new NoExecutionRoleCustomSynthesizer({ deployRoleAdditionalOptions: { - Tags: [{ Key: 'Departement', Value: 'Engineering' }] + Tags: [{ Key: 'Department', Value: 'Engineering' }] }, }) }); From 315b169ee69e89022dc8cea7ca153e39aed2b256 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 09:08:28 +0300 Subject: [PATCH 62/78] test vpc lookup --- .../session-tags.all-roles-deny-all.yaml | 11 ++++++----- .../cli-integ/resources/cdk-apps/app/app.js | 11 ++++++++++- .../tests/cli-integ-tests/bootstrapping.integtest.ts | 5 ++++- 3 files changed, 20 insertions(+), 7 deletions(-) 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 index ee0eba5f15282..e2b80c535d615 100644 --- 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 @@ -367,16 +367,17 @@ Resources: - 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 + - Sid: AllowEc2OnlyIfEngineeringDepartement + Effect: Allow Action: - - kms:Decrypt + - ec2:* Resource: "*" + Condition: + StringEquals: + aws:PrincipalTag/Department: "Engineering" Version: '2012-10-17' PolicyName: LookupRolePolicy Tags: 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 920fd39735b8b..37bbede6ffbf2 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 @@ -439,6 +439,9 @@ class SessionTagsStack extends cdk.Stack { }) }); + // 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')), @@ -727,7 +730,13 @@ switch (stackSet) { new MissingSSMParameterStack(app, `${stackPrefix}-missing-ssm-parameter`, { env: defaultEnv }); new LambdaStack(app, `${stackPrefix}-lambda`); - new SessionTagsStack(app, `${stackPrefix}-session-tags`); + + 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`); 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 27a360d6bce95..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,7 +87,7 @@ integTest('can and deploy if omitting execution policies', withoutBootstrap(asyn }); })); -integTest('can deploy with session tags on the deploy, file asset, and image asset publishing roles', withoutBootstrap(async (fixture) => { +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({ @@ -101,6 +101,9 @@ integTest('can deploy with session tags on the deploy, file asset, and image ass '--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`, '--context', '@aws-cdk/core:newStyleStackSynthesis=1', ], + modEnv: { + ENABLE_VPC_TESTING: 'IMPORT', + }, }); })); From ff0034dc819bbb13e522e9a356484466ea6abe87 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 15:09:35 +0300 Subject: [PATCH 63/78] mid work --- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 2 +- .../lib/api/plugin/context-provider-plugin.ts | 18 ++++++++++++++++++ packages/aws-cdk/lib/context-providers/ami.ts | 7 ++----- .../context-providers/availability-zones.ts | 5 ++--- .../endpoint-service-availability-zones.ts | 10 ++++------ .../lib/context-providers/hosted-zones.ts | 7 ++----- .../aws-cdk/lib/context-providers/index.ts | 7 ++++++- packages/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 +++++-------- packages/aws-cdk/lib/context-providers/vpcs.ts | 9 ++------- 12 files changed, 49 insertions(+), 55 deletions(-) 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 89808864076ae..9ccc3d177ae86 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -367,7 +367,7 @@ export class SdkProvider { externalId: string | undefined, additionalOptions: AssumeRoleAdditionalOptions | undefined, region: string | undefined) { - debug(`Assuming role '${roleArn}'.`); + debug(`Assuming role '${roleArn}' with additional options: ${JSON.stringify(additionalOptions ?? {}, null, 2)}.`); region = region ?? this.defaultRegion; const creds = new AWS.ChainableTemporaryCredentials({ diff --git a/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts b/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts index ea92987068617..110879fbca30b 100644 --- a/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts +++ b/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts @@ -1,3 +1,7 @@ +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; +import * as cxapi from '@aws-cdk/cx-api'; +import { CredentialsOptions, ISDK, Mode, SdkProvider } from '../aws-auth'; + export interface ContextProviderPlugin { getValue(args: {[key: string]: any}): Promise; } @@ -5,3 +9,17 @@ export interface ContextProviderPlugin { export function isContextProviderPlugin(x: unknown): x is ContextProviderPlugin { return typeof x === 'object' && !!x && !!(x as any).getValue; } + +export async function initPluginSdk(aws: SdkProvider, options: cxschema.ContextLookupRoleOptions): Promise { + + const account = options.account; + const region = options.region; + + const creds: CredentialsOptions = { + assumeRoleArn: options.lookupRoleArn, + assumeRoleAdditionalOptions: options.lookupRoleAdditionalOptions, + assumeRoleExternalId: options.lookupRoleExternalId, + }; + + 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/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts index 062cb445e29ac..aaf2703b4fa77 100644 --- a/packages/aws-cdk/lib/context-providers/ami.ts +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -1,8 +1,6 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } 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 initPluginSdk(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..dd32d91ef0109 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -2,7 +2,7 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; /** @@ -16,8 +16,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 initPluginSdk(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..3ce985c7746fa 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,7 +1,6 @@ -import * as cxapi from '@aws-cdk/cx-api'; -import { Mode } from '../api/aws-auth/credentials'; +import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } 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 initPluginSdk(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..bf0e9a6ca6fdd 100644 --- a/packages/aws-cdk/lib/context-providers/hosted-zones.ts +++ b/packages/aws-cdk/lib/context-providers/hosted-zones.ts @@ -1,8 +1,6 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; export class HostedZoneContextProviderPlugin implements ContextProviderPlugin { @@ -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 initPluginSdk(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/index.ts b/packages/aws-cdk/lib/context-providers/index.ts index 7c4eeee8a6789..63b26bca93e94 100644 --- a/packages/aws-cdk/lib/context-providers/index.ts +++ b/packages/aws-cdk/lib/context-providers/index.ts @@ -68,7 +68,12 @@ export async function provideContextValues( lookupRoleArn: missingContext.props.lookupRoleArn, }, resolvedEnvironment, sdk); - value = await provider.getValue({ ...missingContext.props, lookupRoleArn: arns.lookupRoleArn }); + value = await provider.getValue({ + ...missingContext.props, + lookupRoleArn: arns.lookupRoleArn, + lookupRoleExternalId: missingContext.props.lookupRoleExternalId, + assumeRoleAdditionalOptions: missingContext.props.lookupRoleAdditionalOptions, + }); } catch (e: any) { // Set a specially formatted provider value which will be interpreted // as a lookup failure in the toolkit. diff --git a/packages/aws-cdk/lib/context-providers/keys.ts b/packages/aws-cdk/lib/context-providers/keys.ts index c0ec05a57d665..11ea7813190fc 100644 --- a/packages/aws-cdk/lib/context-providers/keys.ts +++ b/packages/aws-cdk/lib/context-providers/keys.ts @@ -2,9 +2,8 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; export class KeyContextProviderPlugin implements ContextProviderPlugin { @@ -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 initPluginSdk(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..15ffe495313af 100644 --- a/packages/aws-cdk/lib/context-providers/load-balancers.ts +++ b/packages/aws-cdk/lib/context-providers/load-balancers.ts @@ -1,9 +1,8 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; /** * Provides load balancer context information. @@ -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 initPluginSdk(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 initPluginSdk(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..3a5f9f2054e8d 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -1,17 +1,14 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { } 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 initPluginSdk(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 5adaa00116d30..f76380a4bb0e1 100644 --- a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts +++ b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts @@ -1,9 +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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; /** @@ -22,7 +20,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); if (!response.Parameter || response.Parameter.Value === undefined) { throw new Error(`SSM parameter not available in account ${account}, region ${region}: ${parameterName}`); } @@ -40,12 +38,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 initPluginSdk(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..a6c592a11755f 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -1,9 +1,8 @@ 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 { ContextProviderPlugin } from '../api/plugin'; +import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { @@ -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 initPluginSdk(this.aws, args)).ec2(); const vpcId = await this.findVpc(ec2, args); From 8cc1d927d5b89d3747cac953f88cfe6907c57e96 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 15:46:28 +0300 Subject: [PATCH 64/78] mid work --- packages/aws-cdk/lib/context-providers/availability-zones.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/aws-cdk/lib/context-providers/availability-zones.ts b/packages/aws-cdk/lib/context-providers/availability-zones.ts index dd32d91ef0109..4009c6ed3faeb 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -1,6 +1,4 @@ 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 { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; import { debug } from '../logging'; From b24e8797233259297f7b79dc48a56ce340dafa50 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 18:13:06 +0300 Subject: [PATCH 65/78] mid work --- build.assume-role-additional-options.sh | 4 +++- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 15 +++++++++++++++ .../lib/api/plugin/context-provider-plugin.ts | 18 ------------------ packages/aws-cdk/lib/context-providers/ami.ts | 4 ++-- .../context-providers/availability-zones.ts | 4 ++-- .../endpoint-service-availability-zones.ts | 4 ++-- .../lib/context-providers/hosted-zones.ts | 4 ++-- packages/aws-cdk/lib/context-providers/keys.ts | 4 ++-- .../lib/context-providers/load-balancers.ts | 4 ++-- .../lib/context-providers/security-groups.ts | 4 ++-- .../lib/context-providers/ssm-parameters.ts | 4 ++-- packages/aws-cdk/lib/context-providers/vpcs.ts | 4 ++-- 12 files changed, 36 insertions(+), 37 deletions(-) diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh index a5eca5e02430e..4eca0e3f6084f 100755 --- a/build.assume-role-additional-options.sh +++ b/build.assume-role-additional-options.sh @@ -26,6 +26,8 @@ set_dev_dependency_version() { node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.devDependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" } +yarn cache clean + builddir=$(mktemp -d) cd ${builddir} @@ -34,7 +36,7 @@ echo "Working directory: ${builddir}" git clone https://github.com/cdklabs/cloud-assembly-schema.git -b sumughan/add-session-tags-to-schema --single-branch git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch -git clone https://github.com/aws/aws-cdk.git --single-branch -b sumughan/session-tags +git clone https://github.com/aws/aws-cdk.git -b sumughan/session-tags --single-branch echo "Building @aws-cdk/cloud-assembly-schema" (cd cloud-assembly-schema && yarn install && yarn compile && yarn package:js) 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 9ccc3d177ae86..dd01774fee251 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'; @@ -591,3 +592,17 @@ function fmtObtainedCredentials( return msg.join(''); } } + +export async function initPluginSdk(aws: SdkProvider, options: cxschema.ContextLookupRoleOptions): Promise { + + const account = options.account; + const region = options.region; + + const creds: CredentialsOptions = { + assumeRoleArn: options.lookupRoleArn, + assumeRoleAdditionalOptions: options.lookupRoleAdditionalOptions, + assumeRoleExternalId: options.lookupRoleExternalId, + }; + + 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/plugin/context-provider-plugin.ts b/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts index 110879fbca30b..ea92987068617 100644 --- a/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts +++ b/packages/aws-cdk/lib/api/plugin/context-provider-plugin.ts @@ -1,7 +1,3 @@ -import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import * as cxapi from '@aws-cdk/cx-api'; -import { CredentialsOptions, ISDK, Mode, SdkProvider } from '../aws-auth'; - export interface ContextProviderPlugin { getValue(args: {[key: string]: any}): Promise; } @@ -9,17 +5,3 @@ export interface ContextProviderPlugin { export function isContextProviderPlugin(x: unknown): x is ContextProviderPlugin { return typeof x === 'object' && !!x && !!(x as any).getValue; } - -export async function initPluginSdk(aws: SdkProvider, options: cxschema.ContextLookupRoleOptions): Promise { - - const account = options.account; - const region = options.region; - - const creds: CredentialsOptions = { - assumeRoleArn: options.lookupRoleArn, - assumeRoleAdditionalOptions: options.lookupRoleAdditionalOptions, - assumeRoleExternalId: options.lookupRoleExternalId, - }; - - 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/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts index aaf2703b4fa77..c1f063f1db502 100644 --- a/packages/aws-cdk/lib/context-providers/ami.ts +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -1,6 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug, print } from '../logging'; /** diff --git a/packages/aws-cdk/lib/context-providers/availability-zones.ts b/packages/aws-cdk/lib/context-providers/availability-zones.ts index 4009c6ed3faeb..82f999a1f1d3f 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -1,6 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; /** 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 3ce985c7746fa..377bdc71170f8 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,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; /** diff --git a/packages/aws-cdk/lib/context-providers/hosted-zones.ts b/packages/aws-cdk/lib/context-providers/hosted-zones.ts index bf0e9a6ca6fdd..cc16ec79dc1d9 100644 --- a/packages/aws-cdk/lib/context-providers/hosted-zones.ts +++ b/packages/aws-cdk/lib/context-providers/hosted-zones.ts @@ -1,6 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; export class HostedZoneContextProviderPlugin implements ContextProviderPlugin { diff --git a/packages/aws-cdk/lib/context-providers/keys.ts b/packages/aws-cdk/lib/context-providers/keys.ts index 11ea7813190fc..847e2b7a73abe 100644 --- a/packages/aws-cdk/lib/context-providers/keys.ts +++ b/packages/aws-cdk/lib/context-providers/keys.ts @@ -2,8 +2,8 @@ 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 { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; export class KeyContextProviderPlugin implements ContextProviderPlugin { diff --git a/packages/aws-cdk/lib/context-providers/load-balancers.ts b/packages/aws-cdk/lib/context-providers/load-balancers.ts index 15ffe495313af..d60954ed4b888 100644 --- a/packages/aws-cdk/lib/context-providers/load-balancers.ts +++ b/packages/aws-cdk/lib/context-providers/load-balancers.ts @@ -1,8 +1,8 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; /** * Provides load balancer context information. diff --git a/packages/aws-cdk/lib/context-providers/security-groups.ts b/packages/aws-cdk/lib/context-providers/security-groups.ts index 3a5f9f2054e8d..eea3a19e2fc6e 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -1,8 +1,8 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin { constructor(private readonly aws: SdkProvider) { diff --git a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts index f76380a4bb0e1..dd26966495daa 100644 --- a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts +++ b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts @@ -1,7 +1,7 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as AWS from 'aws-sdk'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; /** diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index a6c592a11755f..6d21f8e1e2771 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -1,8 +1,8 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as cxapi from '@aws-cdk/cx-api'; import * as AWS from 'aws-sdk'; -import { SdkProvider } from '../api/aws-auth/sdk-provider'; -import { ContextProviderPlugin, initPluginSdk } from '../api/plugin'; +import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { From fc455f1bf3bdbeb0cbf0cd46bd283140c74cd9eb Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 21:35:44 +0300 Subject: [PATCH 66/78] mid work --- .../core/lib/stack-synthesizers/default-synthesizer.ts | 4 ++-- .../core/lib/stack-synthesizers/stack-synthesizer.ts | 5 +++-- packages/aws-cdk-lib/core/lib/stack.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) 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 02b5001b647d2..750e8a0ba1d22 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 @@ -463,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.lookupRoleAdditionalOptions); } /** @@ -490,7 +490,7 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab this.addBootstrapVersionRule(this._requiredBootstrapVersionForDeployment, this.bootstrapStackVersionSsmParameter!); } - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn); + const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn, this.props.lookupRoleAdditionalOptions); const templateAsset = this.addFileAsset(templateAssetSource); const assetManifestId = this.assetManifest.emitManifest(this.boundStack, session, { 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 f89ee468aa9fa..1877a7eb99fc9 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,9 @@ 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, + lookupRoleAdditionalOptions?: { [key: string]: any }): FileAssetSource { + this.boundStack._synthesizeTemplate(session, lookupRoleArn, lookupRoleAdditionalOptions); return stackTemplateFileAsset(this.boundStack, session); } diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index ce3cb9c9b9fd8..d04b15d69847a 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -1061,7 +1061,7 @@ 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, lookupRoleAdditionalOptions?: { [key: string]: any }): void { // In principle, stack synthesis is delegated to the // StackSynthesis object. // @@ -1105,7 +1105,7 @@ export class Stack extends Construct implements ITaggable { for (const ctx of this._missingContext) { if (lookupRoleArn != null) { - builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn } }); + builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn, ...(lookupRoleAdditionalOptions ?? {}) } }); } else { builder.addMissing(ctx); } From ff9b20a196529e4febbbaee0c2dff45fc523bf8f Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 22:04:16 +0300 Subject: [PATCH 67/78] mid work --- packages/aws-cdk-lib/core/lib/stack.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index d04b15d69847a..a507b6def0c97 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -1104,11 +1104,7 @@ 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, ...(lookupRoleAdditionalOptions ?? {}) } }); - } else { - builder.addMissing(ctx); - } + builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn, lookupRoleAdditionalOptions } }); } } From da2cb72f5ecb5eeee02ca49018ec105a2fd6e9f1 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 12 Sep 2024 22:39:02 +0300 Subject: [PATCH 68/78] mid work --- .../@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js | 3 +++ 1 file changed, 3 insertions(+) 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 37bbede6ffbf2..8c3a4b4f20e20 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 @@ -436,6 +436,9 @@ class SessionTagsStack extends cdk.Stack { imageAssetPublishingRoleAdditionalOptions: { Tags: [{ Key: 'Department', Value: 'Engineering' }] }, + lookupRoleAdditionalOptions: { + Tags: [{ Key: 'Department', Value: 'Engineering' }] + } }) }); From 1539bfe359eb085c2808b369d00f8399296317cd Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 15 Sep 2024 11:06:56 +0300 Subject: [PATCH 69/78] mid work --- .../stack-synthesizers/default-synthesizer.ts | 5 +++-- .../lib/stack-synthesizers/stack-synthesizer.ts | 6 ++++-- packages/aws-cdk-lib/core/lib/stack.ts | 17 +++++++++++++++-- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) 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 750e8a0ba1d22..2b4e4203a3466 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 @@ -463,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, this.props.lookupRoleAdditionalOptions); + stack._synthesizeTemplate(session, this.lookupRoleArn, this.props.lookupRoleExternalId, this.props.lookupRoleAdditionalOptions); } /** @@ -490,7 +490,8 @@ export class DefaultStackSynthesizer extends StackSynthesizer implements IReusab this.addBootstrapVersionRule(this._requiredBootstrapVersionForDeployment, this.bootstrapStackVersionSsmParameter!); } - const templateAssetSource = this.synthesizeTemplate(session, this.lookupRoleArn, this.props.lookupRoleAdditionalOptions); + 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, { 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 1877a7eb99fc9..3cde73d8b8933 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,9 +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, + protected synthesizeTemplate(session: ISynthesisSession, + lookupRoleArn?: string, + lookupRoleExternalId?: string, lookupRoleAdditionalOptions?: { [key: string]: any }): FileAssetSource { - this.boundStack._synthesizeTemplate(session, lookupRoleArn, lookupRoleAdditionalOptions); + this.boundStack._synthesizeTemplate(session, lookupRoleArn, lookupRoleExternalId, lookupRoleAdditionalOptions); return stackTemplateFileAsset(this.boundStack, session); } diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index a507b6def0c97..2a5947e38b31d 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -1061,7 +1061,10 @@ export class Stack extends Construct implements ITaggable { * Synthesizes the cloudformation template into a cloud assembly. * @internal */ - public _synthesizeTemplate(session: ISynthesisSession, lookupRoleArn?: string, lookupRoleAdditionalOptions?: { [key: string]: any }): void { + public _synthesizeTemplate(session: ISynthesisSession, + lookupRoleArn?: string, + lookupRoleExternalId?: string, + lookupRoleAdditionalOptions?: { [key: string]: any }): void { // In principle, stack synthesis is delegated to the // StackSynthesis object. // @@ -1104,7 +1107,17 @@ export class Stack extends Construct implements ITaggable { fs.writeFileSync(outPath, templateData); for (const ctx of this._missingContext) { - builder.addMissing({ ...ctx, props: { ...ctx.props, lookupRoleArn, lookupRoleAdditionalOptions } }); + + // '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/api/aws-auth/sdk-provider.ts b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts index dd01774fee251..780c4d6db5457 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -368,7 +368,7 @@ export class SdkProvider { externalId: string | undefined, additionalOptions: AssumeRoleAdditionalOptions | undefined, region: string | undefined) { - debug(`Assuming role '${roleArn}' with additional options: ${JSON.stringify(additionalOptions ?? {}, null, 2)}.`); + debug(`Assuming role '${roleArn}'.`); region = region ?? this.defaultRegion; const creds = new AWS.ChainableTemporaryCredentials({ From da328ee2a32a76ae2c99e59542b20a40a2e36f52 Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 15 Sep 2024 11:22:25 +0300 Subject: [PATCH 70/78] mid work --- .../pipelines/lib/blueprint/stage-deployment.ts | 2 +- .../pipelines/test/codepipeline/codepipeline.test.ts | 2 +- packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts | 2 +- packages/aws-cdk/lib/context-providers/index.ts | 7 +------ 4 files changed, 4 insertions(+), 9 deletions(-) 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 8fa54c19afa8f..0ad9d60082243 100644 --- a/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts +++ b/packages/aws-cdk-lib/pipelines/lib/blueprint/stage-deployment.ts @@ -62,7 +62,7 @@ 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 ${JSON.stringify(artifact.assumeRoleAdditionalOptions.Tags)}, but assuming roles with session tags is not supported by CodePipeline.`); + 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 47463444a463c..28821733c27f2 100644 --- a/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts +++ b/packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts @@ -540,7 +540,7 @@ test('throws when deploy role session tags are used', () => { }, }, }); - }).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 {"Departement":"Engineering"}, but assuming roles with session tags is not supported by CodePipeline.'); + }).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.'); }); 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 780c4d6db5457..7e20c9b3f7492 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -600,7 +600,7 @@ export async function initPluginSdk(aws: SdkProvider, options: cxschema.ContextL const creds: CredentialsOptions = { assumeRoleArn: options.lookupRoleArn, - assumeRoleAdditionalOptions: options.lookupRoleAdditionalOptions, + assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, assumeRoleExternalId: options.lookupRoleExternalId, }; diff --git a/packages/aws-cdk/lib/context-providers/index.ts b/packages/aws-cdk/lib/context-providers/index.ts index 63b26bca93e94..7c4eeee8a6789 100644 --- a/packages/aws-cdk/lib/context-providers/index.ts +++ b/packages/aws-cdk/lib/context-providers/index.ts @@ -68,12 +68,7 @@ export async function provideContextValues( lookupRoleArn: missingContext.props.lookupRoleArn, }, resolvedEnvironment, sdk); - value = await provider.getValue({ - ...missingContext.props, - lookupRoleArn: arns.lookupRoleArn, - lookupRoleExternalId: missingContext.props.lookupRoleExternalId, - assumeRoleAdditionalOptions: missingContext.props.lookupRoleAdditionalOptions, - }); + value = await provider.getValue({ ...missingContext.props, lookupRoleArn: arns.lookupRoleArn }); } catch (e: any) { // Set a specially formatted provider value which will be interpreted // as a lookup failure in the toolkit. From 0b47cd7974bd607a42d315cb2c460665ff3696ca Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 15 Sep 2024 16:20:32 +0300 Subject: [PATCH 71/78] mid work --- .../new-style-synthesis.test.ts | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) 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 bd97490845cc2..097599934c060 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 @@ -105,7 +105,7 @@ describe('new style synthesis', () => { }); - test('can set session tags on default stack synthesizer', () => { + test('can set role additional options tags on default stack synthesizer', () => { // GIVEN stack = new Stack(app, 'SessionTagsStack', { synthesizer: new DefaultStackSynthesizer({ @@ -122,6 +122,9 @@ describe('new style synthesis', () => { Tags: [{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }], }, }), + env: { + account: '111111111111', region: 'us-east-1', + }, }); stack.synthesizer.addFileAsset({ @@ -135,9 +138,15 @@ describe('new style synthesis', () => { sourceHash: 'dockerHash', }); + ContextProvider.getValue(stack, { + provider: cxschema.ContextProvider.VPC_PROVIDER, + props: {}, + dummyValue: undefined, + }).value; + // THEN const asm = app.synth(); - const manifest = app.synth().getStackByName('SessionTagsStack').manifest; + 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' }]); @@ -148,6 +157,10 @@ describe('new style synthesis', () => { // Validates that the image and file asset session tags were set in the asset manifest: expect(assetManifestJSON.dockerImages?.dockerHash.destinations['current_account-current_region'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }]); expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].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({}); + }); test('customize version parameter', () => { @@ -233,6 +246,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(); From 0ae1a1b1b4847b01b4d904d752ee3856d89e3933 Mon Sep 17 00:00:00 2001 From: epolon Date: Sun, 15 Sep 2024 17:03:56 +0300 Subject: [PATCH 72/78] mid work --- packages/aws-cdk-lib/core/README.md | 44 +++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index c09e5dd3e678f..39b1f1aa074ba 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -155,13 +155,53 @@ 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 Session Tags +### 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 `DefaultStackSynthesizer`: +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 { From 07137e34474282bee7feaadf74ab0e43e8b36c60 Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 19 Sep 2024 14:22:34 +0300 Subject: [PATCH 73/78] mid work --- build.assume-role-additional-options.sh | 15 +++-- packages/@aws-cdk/cx-api/package.json | 4 +- packages/@aws-cdk/integ-runner/package.json | 4 +- packages/aws-cdk/package.json | 4 +- yarn.lock | 71 +++++++++------------ 5 files changed, 46 insertions(+), 52 deletions(-) diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh index 4eca0e3f6084f..d7fb2af22250a 100755 --- a/build.assume-role-additional-options.sh +++ b/build.assume-role-additional-options.sh @@ -34,7 +34,7 @@ cd ${builddir} echo "Working directory: ${builddir}" -git clone https://github.com/cdklabs/cloud-assembly-schema.git -b sumughan/add-session-tags-to-schema --single-branch +git clone https://github.com/cdklabs/cloud-assembly-schema.git -b main --single-branch git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch git clone https://github.com/aws/aws-cdk.git -b sumughan/session-tags --single-branch @@ -98,10 +98,12 @@ echo "Building aws-cdk" cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz cp ${cdk_assets_pkg} aws-cdk/packages/aws-cdk/cdk-assets.tgz cp ${cx_api_pkg} aws-cdk/packages/aws-cdk/cx-api.tgz +cp ${cdk_assets_pkg} aws-cdk/packages/@aws-cdk/integ-runner/cdk-assets.tgz (cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") +(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "cdk-assets" "./cdk-assets.tgz") (cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") (cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") @@ -113,6 +115,7 @@ cp ${cx_api_pkg} aws-cdk/packages/aws-cdk/cx-api.tgz (cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") (cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") (cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") +(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "cdk-assets" "${allow_all_version_range}") (cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") (cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") @@ -139,11 +142,11 @@ echo "aws-cdk package: ${aws_cdk_pkg}" workdir=$(mktemp -d) -(cd ${workdir} && mv ${cloud_assembly_schema_pkg} "cloud-assembly-schema.tgz") -(cd ${workdir} && mv ${cx_api_pkg} "cx-api.tgz") -(cd ${workdir} && mv ${cdk_assets_pkg} "cdk-assets.tgz") -(cd ${workdir} && mv ${aws_cdk_lib_pkg} "aws-cdk-lib.tgz") -(cd ${workdir} && mv ${aws_cdk_pkg} "aws-cdk.tgz") +(cd ${workdir} && cp ${cloud_assembly_schema_pkg} "cloud-assembly-schema.tgz") +(cd ${workdir} && cp ${cx_api_pkg} "cx-api.tgz") +(cd ${workdir} && cp ${cdk_assets_pkg} "cdk-assets.tgz") +(cd ${workdir} && cp ${aws_cdk_lib_pkg} "aws-cdk-lib.tgz") +(cd ${workdir} && cp ${aws_cdk_pkg} "aws-cdk.tgz") (cd ${workdir} && npm install "./cloud-assembly-schema.tgz") (cd ${workdir} && npm install "./cx-api.tgz") diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index 750fe90d3e2d4..31cc267e1ecc8 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -82,12 +82,12 @@ "semver": "^7.6.3" }, "peerDependencies": { - "@aws-cdk/cloud-assembly-schema": "^36.0.5" + "@aws-cdk/cloud-assembly-schema": "^38.0.0" }, "license": "Apache-2.0", "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^29.5.12", "@types/mock-fs": "^4.13.4", diff --git a/packages/@aws-cdk/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index 756b87472a57a..65a03c0dee58d 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -71,11 +71,11 @@ }, "dependencies": { "chokidar": "^3.6.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/aws-service-spec": "^0.1.23", - "cdk-assets": "^2.151.29", + "cdk-assets": "^2.152.0", "@aws-cdk/cdk-cli-wrapper": "0.0.0", "aws-cdk": "0.0.0", "chalk": "^4", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index 79f9e8064b5d6..b57114cf0c19a 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -96,7 +96,7 @@ "xml-js": "^1.6.11" }, "dependencies": { - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/region-info": "0.0.0", @@ -104,7 +104,7 @@ "archiver": "^5.3.2", "aws-sdk": "^2.1691.0", "camelcase": "^6.3.0", - "cdk-assets": "^2.151.29", + "cdk-assets": "^2.152.0", "cdk-from-cfn": "^0.162.0", "chalk": "^4", "chokidar": "^3.6.0", diff --git a/yarn.lock b/yarn.lock index ec85df378061c..1c36618fa6caf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,6 +59,14 @@ "@aws-cdk/service-spec-types" "^0.0.90" "@cdklabs/tskb" "^0.0.3" +"@aws-cdk/cloud-assembly-schema@2.153.0": + version "2.153.0" + resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-2.153.0.tgz#43d86b52f4fd9aa5ad384d30e7001e116917dab2" + integrity sha512-m304DL4pIfY7N3qWcXzkpKEUL/kqpOdmPgGvWrx5JDW5/oxT5xByzBiWyNTU3u+IlEcaPXndKI02dUMbVJ3bSA== + dependencies: + jsonschema "^1.4.1" + semver "^7.6.2" + "@aws-cdk/cloud-assembly-schema@^36.0.24": version "36.0.24" resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.0.24.tgz#f6f05615223e800771ca99c88ad631c32b33d642" @@ -67,10 +75,18 @@ jsonschema "^1.4.1" semver "^7.6.3" -"@aws-cdk/cx-api@^2.157.0": - version "2.157.0" - resolved "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.157.0.tgz#c0721f1d27778dd740c98f12efaf89b105cf89ce" - integrity sha512-PRZAbVVPyhcrnNW4tmSKIp8WMxjo9ImSa1K7MAbK8ufafD+KFWGQgxhVIl3bY6WVeZVaduoXRD2gQZwUaDj3XQ== +"@aws-cdk/cloud-assembly-schema@^38.0.0": + version "38.0.1" + resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-38.0.1.tgz#cdf4684ae8778459e039cd44082ea644a3504ca9" + integrity sha512-KvPe+NMWAulfNVwY7jenFhzhuLhLqJ/OPy5jx7wUstbjnYnjRVLpUHPU3yCjXFE0J8cuJVdx95BJ4rOs66Pi9w== + dependencies: + jsonschema "^1.4.1" + semver "^7.6.3" + +"@aws-cdk/cx-api@2.153.0": + version "2.153.0" + resolved "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.153.0.tgz#3115624a66a0eb4b5409b696916859d851ba856e" + integrity sha512-64NWRwjgAHuMQ9FyBmuNDMUUa26vNlhmgXZwccw7iH/8VhfksY8KWlcpWp2hUSSpByoSIBjpF6zngr0/uJmwsw== dependencies: semver "^7.6.2" @@ -8305,7 +8321,7 @@ aws-sdk-mock@5.6.0: sinon "^11.1.1" traverse "^0.6.6" -aws-sdk@^2.1691.0, aws-sdk@^2.928.0: +aws-sdk@^2.1653.0, aws-sdk@^2.1691.0, aws-sdk@^2.928.0: version "2.1691.0" resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz#9d6ccdcbae03c806fc62667b76eb3e33e5294dcc" integrity sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g== @@ -8761,15 +8777,15 @@ case@1.6.3, case@^1.6.3: resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== -cdk-assets@^2.151.29: - version "2.151.30" - resolved "https://registry.npmjs.org/cdk-assets/-/cdk-assets-2.151.30.tgz#6b8bc669540641370c18de8b103821cc1c44ec3e" - integrity sha512-adI0yZIJDh/rz/9FUMcAKokc/9BSj2NzTkVYe03Ca4347SHsWzzLwo3fRFywfCg4/QNGvh9aNNNz6YR7zLII9g== +cdk-assets@^2.152.0: + version "2.153.0" + resolved "https://registry.npmjs.org/cdk-assets/-/cdk-assets-2.153.0.tgz#828330cfe8bd3d3aaf9448a29283cfa15dc45daf" + integrity sha512-uXPVTvzUlVG9OdOM1B77SRmhzwJod2pW3bMLRiCwKfL5qsiYVYumX/FVa6CveF6GEGa31PVRu12SBzcR5d+qyQ== dependencies: - "@aws-cdk/cloud-assembly-schema" "^36.0.24" - "@aws-cdk/cx-api" "^2.157.0" + "@aws-cdk/cloud-assembly-schema" "2.153.0" + "@aws-cdk/cx-api" "2.153.0" archiver "^5.3.2" - aws-sdk "^2.1691.0" + aws-sdk "^2.1653.0" glob "^7.2.3" mime "^2.6.0" yargs "^16.2.0" @@ -16563,16 +16579,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: +"string-width-cjs@npm:string-width@^4.2.0", string-width@*, string-width@^1.0.1, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.1, string-width@^5.1.2: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16637,7 +16644,7 @@ stringify-package@^1.0.1: resolved "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16651,13 +16658,6 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -17655,7 +17655,7 @@ workerpool@^6.5.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -17673,15 +17673,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 4734465d0c4d02a3e1e79c38c6a0b71540f5de8e Mon Sep 17 00:00:00 2001 From: epolon Date: Thu, 19 Sep 2024 21:19:22 +0300 Subject: [PATCH 74/78] mid work --- packages/@aws-cdk/cx-api/FEATURE_FLAGS.md | 22 ++++++++++- packages/@aws-cdk/integ-runner/package.json | 2 +- packages/aws-cdk-lib/package.json | 2 +- packages/aws-cdk/package.json | 2 +- yarn.lock | 44 +++++++-------------- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md b/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md index 634630f6e9b41..beadd60aa4ed2 100644 --- a/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md +++ b/packages/@aws-cdk/cx-api/FEATURE_FLAGS.md @@ -73,6 +73,7 @@ Flags come in three types: | [@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) | @@ -134,7 +135,8 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@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-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true } } ``` @@ -1378,4 +1380,22 @@ When this feature flag is enabled, specify newly introduced props 's3InputUri' a **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/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index 09410f8d028f5..5c148cd4cda8e 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -75,7 +75,7 @@ "@aws-cdk/cloudformation-diff": "0.0.0", "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/aws-service-spec": "^0.1.24", - "cdk-assets": "^2.152.0", + "cdk-assets": "^2.154.0", "@aws-cdk/cdk-cli-wrapper": "0.0.0", "aws-cdk": "0.0.0", "chalk": "^4", diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index bb7f78cfe3553..3003b87c37138 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -122,7 +122,7 @@ "@aws-cdk/asset-awscli-v1": "^2.2.202", "@aws-cdk/asset-kubectl-v20": "^2.1.2", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^36.0.24", + "@aws-cdk/cloud-assembly-schema": "^38.0.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.2.0", diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index b57114cf0c19a..7946d4256d6e5 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -104,7 +104,7 @@ "archiver": "^5.3.2", "aws-sdk": "^2.1691.0", "camelcase": "^6.3.0", - "cdk-assets": "^2.152.0", + "cdk-assets": "^2.154.0", "cdk-from-cfn": "^0.162.0", "chalk": "^4", "chokidar": "^3.6.0", diff --git a/yarn.lock b/yarn.lock index 4ccc97ff85c0f..3c101cd0d04e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -59,22 +59,6 @@ "@aws-cdk/service-spec-types" "^0.0.91" "@cdklabs/tskb" "^0.0.3" -"@aws-cdk/cloud-assembly-schema@2.153.0": - version "2.153.0" - resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-2.153.0.tgz#43d86b52f4fd9aa5ad384d30e7001e116917dab2" - integrity sha512-m304DL4pIfY7N3qWcXzkpKEUL/kqpOdmPgGvWrx5JDW5/oxT5xByzBiWyNTU3u+IlEcaPXndKI02dUMbVJ3bSA== - dependencies: - jsonschema "^1.4.1" - semver "^7.6.2" - -"@aws-cdk/cloud-assembly-schema@^36.0.24": - version "36.0.24" - resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.0.24.tgz#f6f05615223e800771ca99c88ad631c32b33d642" - integrity sha512-dHyb4lvd6nbNHLVvdyxVPgwc0MyzN3VzIJnWwGJWKOIwVqL7hvU2NkQQrktY9T2MtdhzUdDFm9qluxuLRV5Cfw== - dependencies: - jsonschema "^1.4.1" - semver "^7.6.3" - "@aws-cdk/cloud-assembly-schema@^38.0.0": version "38.0.1" resolved "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-38.0.1.tgz#cdf4684ae8778459e039cd44082ea644a3504ca9" @@ -83,12 +67,12 @@ jsonschema "^1.4.1" semver "^7.6.3" -"@aws-cdk/cx-api@2.153.0": - version "2.153.0" - resolved "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.153.0.tgz#3115624a66a0eb4b5409b696916859d851ba856e" - integrity sha512-64NWRwjgAHuMQ9FyBmuNDMUUa26vNlhmgXZwccw7iH/8VhfksY8KWlcpWp2hUSSpByoSIBjpF6zngr0/uJmwsw== +"@aws-cdk/cx-api@^2.158.0": + version "2.159.0" + resolved "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-2.159.0.tgz#567c0ae0d7a6fc2f7cb9bda7e6cb23fac8d99094" + integrity sha512-HVkHCKQjVi3PCSOF22zLztZMEL+cJcyVvFctS3vXPetgl77L+e/onaGt1AUwRcNY44tvbqJm3oIVQt2HqM3q7w== dependencies: - semver "^7.6.2" + semver "^7.6.3" "@aws-cdk/lambda-layer-kubectl-v24@^2.0.242": version "2.0.242" @@ -8314,7 +8298,7 @@ aws-sdk-mock@5.6.0: sinon "^11.1.1" traverse "^0.6.6" -aws-sdk@^2.1653.0, aws-sdk@^2.1691.0, aws-sdk@^2.928.0: +aws-sdk@^2.1691.0, aws-sdk@^2.928.0: version "2.1691.0" resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1691.0.tgz#9d6ccdcbae03c806fc62667b76eb3e33e5294dcc" integrity sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g== @@ -8770,15 +8754,15 @@ case@1.6.3, case@^1.6.3: resolved "https://registry.npmjs.org/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== -cdk-assets@^2.152.0: - version "2.153.0" - resolved "https://registry.npmjs.org/cdk-assets/-/cdk-assets-2.153.0.tgz#828330cfe8bd3d3aaf9448a29283cfa15dc45daf" - integrity sha512-uXPVTvzUlVG9OdOM1B77SRmhzwJod2pW3bMLRiCwKfL5qsiYVYumX/FVa6CveF6GEGa31PVRu12SBzcR5d+qyQ== +cdk-assets@^2.154.0: + version "2.154.0" + resolved "https://registry.npmjs.org/cdk-assets/-/cdk-assets-2.154.0.tgz#675d239c0156ca05c4a2809b30858c843f984ead" + integrity sha512-8M3zLHCx8nj5Fv5ubEps53jh22NN9G7ZLuq1AJwPdXZP7+nb4q5tdl2Ah2ZPMM/dob9u3KTwNeN34oLKHfDzbw== dependencies: - "@aws-cdk/cloud-assembly-schema" "2.153.0" - "@aws-cdk/cx-api" "2.153.0" + "@aws-cdk/cloud-assembly-schema" "^38.0.0" + "@aws-cdk/cx-api" "^2.158.0" archiver "^5.3.2" - aws-sdk "^2.1653.0" + aws-sdk "^2.1691.0" glob "^7.2.3" mime "^2.6.0" yargs "^16.2.0" @@ -16106,7 +16090,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: +semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== From ebce5fe3fc6a7f5795a123c46201d750327c1a37 Mon Sep 17 00:00:00 2001 From: epolon Date: Fri, 20 Sep 2024 20:29:58 +0300 Subject: [PATCH 75/78] mid work --- .../new-style-synthesis.test.ts | 8 ++- packages/aws-cdk/test/api/fake-sts.ts | 57 +++++-------------- .../aws-cdk/test/api/sdk-provider.test.ts | 4 +- 3 files changed, 20 insertions(+), 49 deletions(-) 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 097599934c060..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 @@ -155,11 +155,13 @@ describe('new style synthesis', () => { const assetManifestJSON = readAssetManifest(assetManifest); // Validates that the image and file asset session tags were set in the asset manifest: - expect(assetManifestJSON.dockerImages?.dockerHash.destinations['current_account-current_region'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-ImageAssetTag' }]); - expect(assetManifestJSON.files?.fileHash.destinations['current_account-current_region'].assumeRoleAdditionalOptions?.Tags).toEqual([{ Key: 'Department', Value: 'Engineering-FileAssetTag' }]); + 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({}); + expect(asm.manifest.missing![0].props.assumeRoleAdditionalOptions).toEqual({ + Tags: [{ Key: 'Department', Value: 'Engineering-LookupRoleTag' }], + }); }); diff --git a/packages/aws-cdk/test/api/fake-sts.ts b/packages/aws-cdk/test/api/fake-sts.ts index a4b1984240954..26447de20cf02 100644 --- a/packages/aws-cdk/test/api/fake-sts.ts +++ b/packages/aws-cdk/test/api/fake-sts.ts @@ -2,7 +2,6 @@ import * as nock from 'nock'; import * as uuid from 'uuid'; import * as xmlJs from 'xml-js'; -import type { AssumeRoleAdditionalOptions } from '../../lib/api/aws-auth/sdk-provider'; interface RegisteredIdentity { readonly account: string; @@ -22,7 +21,8 @@ interface AssumedRole { readonly serialNumber: string; readonly tokenCode: string; readonly roleSessionName: string; - readonly assumeRoleAdditionalOptions?: AssumeRoleAdditionalOptions; + readonly tags?: AWS.STS.Tag[]; + readonly transitiveTagKeys?: string[]; } /** @@ -161,66 +161,37 @@ export class FakeSts { } /** - * This function parses session tags from the STS mock request into a dictionary. This is necessary because - * the STS request body writes these dictionary values in the format Tags.member.X.Key and Tags.member.X.Value. + * Maps have a funky encoding to them when sent to STS. * * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html - * @param body - the parsed body of the mockRequest - * @returns session tags dictionary */ - private decodeTagsFromRequestBody(body: Record): AWS.STS.Tag[] { - const tags: AWS.STS.Tag[] = []; - - for (const key in body) { - if (key.startsWith('Tags.member.') && key.endsWith('.Key')) { - const tagIndex = key.split('.')[2]; - const tagKey = body[key]; - const tagValueKey = `Tags.member.${tagIndex}.Value`; - if (tagValueKey in body) { - tags.push({ Key: tagKey, Value: body[tagValueKey] }); - } - } - } - - return tags; + 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`] })); } /** - * This function parses transitive session tags keys from the STS mock request into an array. This is necessary because - * the STS request body writes these array values in the format TransitiveTagKeys.member.N + * Lists have a funky encoding when sent to STS. * * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html - * @param body - the parsed body of the mockRequest - * @returns transitive session tags keys array */ - private decodeTransitiveTagKeysFromRequestBody(body: Record): string[] { - const tags: string[] = []; - - for (const key in body) { - if (key.startsWith('TransitiveTagKeys.member.')) { - const transtiveTagKey = key.split('.')[2]; - tags.push(transtiveTagKey); - } - } - - return tags; + 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); - const tags = this.decodeTagsFromRequestBody(mockRequest.parsedBody); - const transitiveTagKeys = this.decodeTransitiveTagKeysFromRequestBody(mockRequest.parsedBody); - this.assumedRoles.push({ roleArn: mockRequest.parsedBody.RoleArn, roleSessionName: mockRequest.parsedBody.RoleSessionName, serialNumber: mockRequest.parsedBody.SerialNumber, tokenCode: mockRequest.parsedBody.TokenCode, - assumeRoleAdditionalOptions: { - Tags: tags, - TransitiveTagKeys: transitiveTagKeys, - }, + tags: this.decodeMapFromRequestBody('Tags', mockRequest.parsedBody), + transitiveTagKeys: this.decodeListKeysFromRequestBody('TransitiveTagKeys', mockRequest.parsedBody), }); const roleArn = mockRequest.parsedBody.RoleArn; diff --git a/packages/aws-cdk/test/api/sdk-provider.test.ts b/packages/aws-cdk/test/api/sdk-provider.test.ts index 746d397137bd0..05f3cba57b4bf 100644 --- a/packages/aws-cdk/test/api/sdk-provider.test.ts +++ b/packages/aws-cdk/test/api/sdk-provider.test.ts @@ -369,9 +369,7 @@ describe('with intercepted network calls', () => { // THEN expect(fakeSts.assumedRoles[0]).toEqual(expect.objectContaining({ - assumeRoleAdditionalOptions: { - Tags: [{ Key: 'Department', Value: 'Engineering' }], - }, + tags: [{ Key: 'Department', Value: 'Engineering' }], transitiveTagKeys: ['Department'], })); }); From b32c570c7cee3526ca00a992958d3ee61ac26049 Mon Sep 17 00:00:00 2001 From: epolon Date: Fri, 20 Sep 2024 21:16:21 +0300 Subject: [PATCH 76/78] mid work --- build.assume-role-additional-options.sh | 159 -- packages/@aws-cdk/cx-api/FEATURE_FLAGS.md | 1401 ----------------- .../asset-manifest-builder.ts | 2 +- .../stack-synthesizers/default-synthesizer.ts | 8 +- .../stack-synthesizers/stack-synthesizer.ts | 2 +- .../lib/artifacts/cloudformation-artifact.ts | 2 +- .../aws-cdk/lib/api/aws-auth/sdk-provider.ts | 8 +- packages/aws-cdk/lib/context-providers/ami.ts | 4 +- .../context-providers/availability-zones.ts | 4 +- .../endpoint-service-availability-zones.ts | 4 +- .../lib/context-providers/hosted-zones.ts | 4 +- .../aws-cdk/lib/context-providers/keys.ts | 4 +- .../lib/context-providers/load-balancers.ts | 6 +- .../lib/context-providers/security-groups.ts | 4 +- .../lib/context-providers/ssm-parameters.ts | 4 +- .../aws-cdk/lib/context-providers/vpcs.ts | 4 +- 16 files changed, 32 insertions(+), 1588 deletions(-) delete mode 100755 build.assume-role-additional-options.sh delete mode 100644 packages/@aws-cdk/cx-api/FEATURE_FLAGS.md diff --git a/build.assume-role-additional-options.sh b/build.assume-role-additional-options.sh deleted file mode 100755 index d7fb2af22250a..0000000000000 --- a/build.assume-role-additional-options.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -set_dependency_version() { - local manifest_path="package.json" - local dependency="$1" - local version="$2" - - node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.dependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" -} - -set_peer_dependency_version() { - local manifest_path="package.json" - local dependency="$1" - local version="$2" - - node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.peerDependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" -} - -set_dev_dependency_version() { - local manifest_path="package.json" - local dependency="$1" - local version="$2" - - node -e "let fs=require('fs'), manifestPath='${manifest_path}'; let manifest=JSON.parse(fs.readFileSync(manifestPath, 'utf8')); manifest.devDependencies['${dependency}']='${version}'; fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));" -} - -yarn cache clean - -builddir=$(mktemp -d) - -cd ${builddir} - -echo "Working directory: ${builddir}" - -git clone https://github.com/cdklabs/cloud-assembly-schema.git -b main --single-branch -git clone https://github.com/cdklabs//cdk-assets.git -b sumughan/add-session-tags-to-client-options --single-branch -git clone https://github.com/aws/aws-cdk.git -b sumughan/session-tags --single-branch - -echo "Building @aws-cdk/cloud-assembly-schema" -(cd cloud-assembly-schema && yarn install && yarn compile && yarn package:js) - -cloud_assembly_schema_pkg_version=$(jq -r '.version' cloud-assembly-schema/package.json) -cloud_assembly_schema_pkg="${PWD}/cloud-assembly-schema/dist/js/cloud-assembly-schema@${cloud_assembly_schema_pkg_version}.jsii.tgz" - -allow_all_version_range=">=0.0.0" - -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/cx-api/cloud-assembly-schema.tgz -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/integ-runner/cloud-assembly-schema.tgz -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz - -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") - -echo "Building @aws-cdk/cx-api" -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/@aws-cdk/cx-api/cloud-assembly-schema.tgz -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/cx-api && yarn install) -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/@aws-cdk/cx-api && npx lerna run build --scope=@aws-cdk/cx-api) -(cd aws-cdk/packages/@aws-cdk/cx-api && npx cdk-package --targets js) - -cx_api_pkg_version=$(jq -r '.version' aws-cdk/packages/@aws-cdk/cx-api/package.json) -cx_api_pkg="${PWD}/aws-cdk/packages/@aws-cdk/cx-api/dist/js/cx-api@${cx_api_pkg_version}.jsii.tgz" - -echo "Building cdk-assets" -cp ${cloud_assembly_schema_pkg} cdk-assets/cloud-assembly-schema.tgz -cp ${cx_api_pkg} cdk-assets/cx-api.tgz -(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd cdk-assets && set_dependency_version "@aws-cdk/cx-api" "./cx-api.tgz") -(cd cdk-assets && yarn install && yarn compile) -(cd cdk-assets && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd cdk-assets && set_dependency_version "@aws-cdk/cx-api" "${allow_all_version_range}") -(cd cdk-assets && yarn package) - -cdk_assets_pkg_version=$(jq -r '.version' cdk-assets/package.json) -cdk_assets_pkg="${PWD}/cdk-assets/dist/js/cdk-assets-${cdk_assets_pkg_version}.tgz" - -echo "Building aws-cdk-lib" -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk-lib/cloud-assembly-schema.tgz -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk-lib && yarn install) -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk-lib && npx lerna run build --scope=aws-cdk-lib) -(cd aws-cdk/packages/aws-cdk-lib && npx cdk-package --targets js) - -aws_cdk_lib_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk-lib/package.json) -aws_cdk_lib_pkg="${PWD}/aws-cdk/packages/aws-cdk-lib/dist/js/aws-cdk-lib@${aws_cdk_lib_pkg_version}.jsii.tgz" - -echo "Building aws-cdk" -cp ${cloud_assembly_schema_pkg} aws-cdk/packages/aws-cdk/cloud-assembly-schema.tgz -cp ${cdk_assets_pkg} aws-cdk/packages/aws-cdk/cdk-assets.tgz -cp ${cx_api_pkg} aws-cdk/packages/aws-cdk/cx-api.tgz -cp ${cdk_assets_pkg} aws-cdk/packages/@aws-cdk/integ-runner/cdk-assets.tgz - -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "cdk-assets" "./cdk-assets.tgz") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") - -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "./cloud-assembly-schema.tgz") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "./cdk-assets.tgz") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cx-api" "./cx-api.tgz") -(cd aws-cdk/packages/aws-cdk && yarn install) - -(cd aws-cdk/packages/@aws-cdk/cx-api && set_peer_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/@aws-cdk/cx-api && set_dev_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/@aws-cdk/integ-runner && set_dependency_version "cdk-assets" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk-lib && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") - -echo "Building @aws-cdk/cloudformation-diff" -(cd aws-cdk/packages/@aws-cdk/cloudformation-diff && npx lerna run build --scope=@aws-cdk/cloudformation-diff) - -echo "Building @aws-cdk/region-info" -(cd aws-cdk/packages/@aws-cdk/region-info && npx lerna run build --scope=@aws-cdk/region-info) - -(cd aws-cdk/packages/aws-cdk && yarn build --fix) -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cloud-assembly-schema" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "cdk-assets" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk && set_dependency_version "@aws-cdk/cx-api" "${allow_all_version_range}") -(cd aws-cdk/packages/aws-cdk && yarn package) - -aws_cdk_pkg_version=$(jq -r '.version' aws-cdk/packages/aws-cdk/package.json) -aws_cdk_pkg="${PWD}/aws-cdk/packages/aws-cdk/dist/js/aws-cdk-${aws_cdk_pkg_version}.tgz" - -echo "cloud-assembly-schema package: ${cloud_assembly_schema_pkg}" -echo "cx-api package: ${cx_api_pkg}" -echo "cdk-assets package: ${cdk_assets_pkg}" -echo "aws-cdk-lib package: ${aws_cdk_lib_pkg}" -echo "aws-cdk package: ${aws_cdk_pkg}" - -workdir=$(mktemp -d) - -(cd ${workdir} && cp ${cloud_assembly_schema_pkg} "cloud-assembly-schema.tgz") -(cd ${workdir} && cp ${cx_api_pkg} "cx-api.tgz") -(cd ${workdir} && cp ${cdk_assets_pkg} "cdk-assets.tgz") -(cd ${workdir} && cp ${aws_cdk_lib_pkg} "aws-cdk-lib.tgz") -(cd ${workdir} && cp ${aws_cdk_pkg} "aws-cdk.tgz") - -(cd ${workdir} && npm install "./cloud-assembly-schema.tgz") -(cd ${workdir} && npm install "./cx-api.tgz") -(cd ${workdir} && npm install "./cdk-assets.tgz") -(cd ${workdir} && npm install "./aws-cdk-lib.tgz") -(cd ${workdir} && npm install "./aws-cdk.tgz") -(cd ${workdir} && touch main.ts) - -echo "Workdir: ${workdir}" -echo "Buildir: ${builddir}" \ No newline at end of file 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/lib/stack-synthesizers/asset-manifest-builder.ts b/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts index 69ffd44cfe1e1..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 @@ -239,7 +239,7 @@ export interface RoleOptions { * * - `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 transtive by default. + * - `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. 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 2b4e4203a3466..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 @@ -225,7 +225,7 @@ export interface DefaultStackSynthesizerProps { * * - `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 transtive by default. + * - `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. @@ -237,7 +237,7 @@ export interface DefaultStackSynthesizerProps { * * - `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 transtive by default. + * - `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. @@ -249,7 +249,7 @@ export interface DefaultStackSynthesizerProps { * * - `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 transtive by default. + * - `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. @@ -261,7 +261,7 @@ export interface DefaultStackSynthesizerProps { * * - `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 transtive by default. + * - `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. 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 3cde73d8b8933..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 @@ -248,7 +248,7 @@ export interface SynthesizeStackArtifactOptions { * * - `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 transtive by default. + * - `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. 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 c8fa42098f76f..116a1a41bbd6e 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 @@ -96,7 +96,7 @@ export class CloudFormationStackArtifact extends CloudArtifact { * * - `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 transtive by default. + * - `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. 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 7e20c9b3f7492..6d8d341d7a8c5 100644 --- a/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts +++ b/packages/aws-cdk/lib/api/aws-auth/sdk-provider.ts @@ -593,15 +593,19 @@ function fmtObtainedCredentials( } } -export async function initPluginSdk(aws: SdkProvider, options: cxschema.ContextLookupRoleOptions): Promise { +/** + * 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, - assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, assumeRoleExternalId: options.lookupRoleExternalId, + assumeRoleAdditionalOptions: options.assumeRoleAdditionalOptions, }; return (await aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, creds)).sdk; diff --git a/packages/aws-cdk/lib/context-providers/ami.ts b/packages/aws-cdk/lib/context-providers/ami.ts index c1f063f1db502..9c511cb6569a6 100644 --- a/packages/aws-cdk/lib/context-providers/ami.ts +++ b/packages/aws-cdk/lib/context-providers/ami.ts @@ -1,5 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider, initPluginSdk } 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'; @@ -19,7 +19,7 @@ export class AmiContextProviderPlugin implements ContextProviderPlugin { print(`Searching for AMI in ${account}:${region}`); debug(`AMI search parameters: ${JSON.stringify(args)}`); - const ec2 = (await initPluginSdk(this.aws, args)).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 82f999a1f1d3f..f1910c37709f2 100644 --- a/packages/aws-cdk/lib/context-providers/availability-zones.ts +++ b/packages/aws-cdk/lib/context-providers/availability-zones.ts @@ -1,5 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -14,7 +14,7 @@ export class AZContextProviderPlugin implements ContextProviderPlugin { const region = args.region; const account = args.account; debug(`Reading AZs for ${account}:${region}`); - const ec2 = (await initPluginSdk(this.aws, args)).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 377bdc71170f8..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,5 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -15,7 +15,7 @@ export class EndpointServiceAZContextProviderPlugin implements ContextProviderPl const account = args.account; const serviceName = args.serviceName; debug(`Reading AZs for ${account}:${region}:${serviceName}`); - const ec2 = (await initPluginSdk(this.aws, args)).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 cc16ec79dc1d9..fe5a4be52c4a0 100644 --- a/packages/aws-cdk/lib/context-providers/hosted-zones.ts +++ b/packages/aws-cdk/lib/context-providers/hosted-zones.ts @@ -1,5 +1,5 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; -import { SdkProvider, initPluginSdk } 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,7 +16,7 @@ export class HostedZoneContextProviderPlugin implements ContextProviderPlugin { } const domainName = args.domainName; debug(`Reading hosted zone ${account}:${region}:${domainName}`); - const r53 = (await initPluginSdk(this.aws, args)).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 847e2b7a73abe..52c5dd08f140d 100644 --- a/packages/aws-cdk/lib/context-providers/keys.ts +++ b/packages/aws-cdk/lib/context-providers/keys.ts @@ -2,7 +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 { SdkProvider, initPluginSdk } 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,7 +12,7 @@ export class KeyContextProviderPlugin implements ContextProviderPlugin { } public async getValue(args: cxschema.KeyContextQuery) { - const kms = (await initPluginSdk(this.aws, args)).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 d60954ed4b888..0d5095f277e51 100644 --- a/packages/aws-cdk/lib/context-providers/load-balancers.ts +++ b/packages/aws-cdk/lib/context-providers/load-balancers.ts @@ -1,7 +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 { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; /** @@ -12,7 +12,7 @@ export class LoadBalancerContextProviderPlugin implements ContextProviderPlugin } async getValue(query: cxschema.LoadBalancerContextQuery): Promise { - const elbv2 = (await initPluginSdk(this.aws, query)).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`'); @@ -57,7 +57,7 @@ export class LoadBalancerListenerContextProviderPlugin implements ContextProvide } async getValue(query: LoadBalancerListenerQuery): Promise { - const elbv2 = (await initPluginSdk(this.aws, query)).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 eea3a19e2fc6e..a801841ca9ecf 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -1,7 +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 { SdkProvider, initPluginSdk } 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 { @@ -18,7 +18,7 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin throw new Error('\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group'); } - const ec2 = (await initPluginSdk(this.aws, args)).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 b1225d3d50381..f85fc4c96f117 100644 --- a/packages/aws-cdk/lib/context-providers/ssm-parameters.ts +++ b/packages/aws-cdk/lib/context-providers/ssm-parameters.ts @@ -1,6 +1,6 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import * as AWS from 'aws-sdk'; -import { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -47,7 +47,7 @@ export class SSMContextProviderPlugin implements ContextProviderPlugin { */ private async getSsmParameterValue(args: cxschema.SSMParameterContextQuery) : Promise { - const ssm = (await initPluginSdk(this.aws, args)).ssm(); + const ssm = (await initContextProviderSdk(this.aws, args)).ssm(); try { return await ssm.getParameter({ Name: args.parameterName }).promise(); } catch (e: any) { diff --git a/packages/aws-cdk/lib/context-providers/vpcs.ts b/packages/aws-cdk/lib/context-providers/vpcs.ts index 6d21f8e1e2771..d578df659b4ec 100644 --- a/packages/aws-cdk/lib/context-providers/vpcs.ts +++ b/packages/aws-cdk/lib/context-providers/vpcs.ts @@ -1,7 +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 { SdkProvider, initPluginSdk } from '../api/aws-auth/sdk-provider'; +import { SdkProvider, initContextProviderSdk } from '../api/aws-auth/sdk-provider'; import { ContextProviderPlugin } from '../api/plugin'; import { debug } from '../logging'; @@ -11,7 +11,7 @@ export class VpcNetworkContextProviderPlugin implements ContextProviderPlugin { } public async getValue(args: cxschema.VpcContextQuery) { - const ec2 = (await initPluginSdk(this.aws, args)).ec2(); + const ec2 = (await initContextProviderSdk(this.aws, args)).ec2(); const vpcId = await this.findVpc(ec2, args); From a33c6e23c43edeb74efe6b17b92460a9f7f82f09 Mon Sep 17 00:00:00 2001 From: epolon Date: Mon, 23 Sep 2024 12:25:00 +0300 Subject: [PATCH 77/78] mid work --- packages/@aws-cdk/integ-runner/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index 11cae66f55db9..cb9d98f8f3c8d 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -76,6 +76,7 @@ "@aws-cdk/cx-api": "0.0.0", "cdk-assets": "^2.154.0", "@aws-cdk/aws-service-spec": "^0.1.24", + "@aws-cdk/cdk-cli-wrapper": "0.0.0", "aws-cdk": "0.0.0", "chalk": "^4", From c57f0badbc62bfd7f9f2cd0fc8ab15f2c978ba3a Mon Sep 17 00:00:00 2001 From: epolon Date: Mon, 23 Sep 2024 12:32:29 +0300 Subject: [PATCH 78/78] mid work --- packages/@aws-cdk/integ-runner/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/integ-runner/package.json b/packages/@aws-cdk/integ-runner/package.json index cb9d98f8f3c8d..84312ef8dd01a 100644 --- a/packages/@aws-cdk/integ-runner/package.json +++ b/packages/@aws-cdk/integ-runner/package.json @@ -76,7 +76,7 @@ "@aws-cdk/cx-api": "0.0.0", "cdk-assets": "^2.154.0", "@aws-cdk/aws-service-spec": "^0.1.24", - + "@aws-cdk/cdk-cli-wrapper": "0.0.0", "aws-cdk": "0.0.0", "chalk": "^4",