From f6b47a5b9f837160fa3124890fcf0bebd2e1e4e0 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Mon, 30 Aug 2021 17:36:09 -0700 Subject: [PATCH] fix(codepipeline): large cross-region CodePipeline exceed IAM policy size limit When we generate CodePipelines, we need to add an `sts:AssumeRole` statement for each Action in the pipeline, and a `Bucket.grantReadWrite()` statement for each region the pipeline is in, to the policy statement of the pipeline's Role. For pipelines with many Actions and/or regions, this makes the policy exceed IAM limit of 10240 bytes. Extract a new class from the CodePipeline CloudFormation Actions that caches the statements added to a given Principal by the 'Action' field, and groups the statements with the same 'Actions' by adding elements to the 'Resource' field. This dramatically reduces the duplication in the statement, and increases the chances of it being smaller than the limit. Use this new class in the `Pipeline` construct. Fixes #16244 --- .../test/integ.cicd.expected.json | 30 ++-- .../lib/cloudformation/pipeline-actions.ts | 89 +++------- .../cloudformation/pipeline-actions.test.ts | 9 +- ...g.cfn-template-from-repo.lit.expected.json | 62 +++---- ...yed-through-codepipeline.lit.expected.json | 78 ++++---- .../test/integ.lambda-pipeline.expected.json | 30 ++-- ...eg.pipeline-cfn-cross-region.expected.json | 30 ++-- ...ipeline-cfn-with-action-role.expected.json | 30 ++-- .../test/integ.pipeline-cfn.expected.json | 46 +++-- ...eg.pipeline-code-build-batch.expected.json | 14 +- ...uild-multiple-inputs-outputs.expected.json | 14 +- ...g.pipeline-code-commit-build.expected.json | 46 +++-- .../integ.pipeline-code-commit.expected.json | 30 ++-- ...teg.pipeline-code-deploy-ecs.expected.json | 30 ++-- .../integ.pipeline-code-deploy.expected.json | 30 ++-- .../integ.pipeline-ecr-source.expected.json | 30 ++-- .../integ.pipeline-ecs-deploy.expected.json | 46 +++-- ...line-ecs-separate-source.lit.expected.json | 78 ++++---- .../test/integ.pipeline-events.expected.json | 30 ++-- ...teg.pipeline-manual-approval.expected.json | 30 ++-- .../integ.pipeline-s3-deploy.expected.json | 30 ++-- ...integ.pipeline-stepfunctions.expected.json | 30 ++-- .../@aws-cdk/aws-codepipeline/lib/pipeline.ts | 8 +- .../integ.pipeline-event-target.expected.json | 30 ++-- .../lib/grouping-by-actions-principal.ts | 68 +++++++ packages/@aws-cdk/aws-iam/lib/index.ts | 1 + .../@aws-cdk/aws-iam/lib/policy-statement.ts | 16 ++ packages/@aws-cdk/aws-iam/package.json | 1 + packages/@aws-cdk/aws-kms/lib/key.ts | 7 +- packages/@aws-cdk/core/lib/token.ts | 18 +- .../integ.newpipeline-with-vpc.expected.json | 110 +++++------- .../test/integ.newpipeline.expected.json | 78 ++++---- .../integ.pipeline-security.expected.json | 168 +++++++----------- ...ne-with-assets-single-upload.expected.json | 88 ++++----- .../integ.pipeline-with-assets.expected.json | 88 ++++----- .../test/integ.pipeline.expected.json | 72 ++++---- .../test/__snapshots__/synth.test.js.snap | 46 +++-- 37 files changed, 768 insertions(+), 873 deletions(-) create mode 100644 packages/@aws-cdk/aws-iam/lib/grouping-by-actions-principal.ts diff --git a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json index 9b2ed51da9ff2..936ca87056bb7 100644 --- a/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json +++ b/packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json @@ -63,22 +63,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployExecuteCodePipelineActionRoleAE36AF49", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployChangeSetCodePipelineActionRoleB3BCDD8A", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "CodePipelineDeployExecuteCodePipelineActionRoleAE36AF49", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "CodePipelineDeployChangeSetCodePipelineActionRoleB3BCDD8A", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/lib/cloudformation/pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline-actions/lib/cloudformation/pipeline-actions.ts index 63618e086ed91..54015c0c2c744 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/lib/cloudformation/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/lib/cloudformation/pipeline-actions.ts @@ -522,7 +522,7 @@ export class CloudFormationDeleteStackAction extends CloudFormationDeployAction * Statements created outside of this class are not considered when adding new * permissions. */ -class SingletonPolicy extends Construct implements iam.IGrantable { +class SingletonPolicy extends iam.GroupingByActionsPrincipal { /** * Obtain a SingletonPolicy for a given role. * @param role the Role this policy is bound to. @@ -535,28 +535,24 @@ class SingletonPolicy extends Construct implements iam.IGrantable { private static readonly UUID = '8389e75f-0810-4838-bf64-d6f85a95cf83'; - public readonly grantPrincipal: iam.IPrincipal; - - private statements: { [key: string]: iam.PolicyStatement } = {}; - - private constructor(private readonly role: iam.IRole) { - super(role as unknown as cdk.Construct, SingletonPolicy.UUID); - this.grantPrincipal = role; + private constructor(role: iam.IRole) { + super(role, SingletonPolicy.UUID); } public grantExecuteChangeSet(props: { stackName: string, changeSetName: string, region?: string }): void { - this.statementFor({ + this.addToPrincipalPolicy(new iam.PolicyStatement({ actions: [ - 'cloudformation:DescribeStacks', 'cloudformation:DescribeChangeSet', + 'cloudformation:DescribeStacks', 'cloudformation:ExecuteChangeSet', ], - conditions: { StringEqualsIfExists: { 'cloudformation:ChangeSetName': props.changeSetName } }, - }).addResources(this.stackArnFromProps(props)); + conditions: { StringEqualsIfExists: { 'cloudformation:ChangeSetName': props.changeSetName } }, + resources: [this.stackArnFromProps(props)], + })); } public grantCreateReplaceChangeSet(props: { stackName: string, changeSetName: string, region?: string }): void { - this.statementFor({ + this.addToPrincipalPolicy(new iam.PolicyStatement({ actions: [ 'cloudformation:CreateChangeSet', 'cloudformation:DeleteChangeSet', @@ -564,68 +560,44 @@ class SingletonPolicy extends Construct implements iam.IGrantable { 'cloudformation:DescribeStacks', ], conditions: { StringEqualsIfExists: { 'cloudformation:ChangeSetName': props.changeSetName } }, - }).addResources(this.stackArnFromProps(props)); + resources: [this.stackArnFromProps(props)], + })); } public grantCreateUpdateStack(props: { stackName: string, replaceOnFailure?: boolean, region?: string }): void { const actions = [ - 'cloudformation:DescribeStack*', 'cloudformation:CreateStack', - 'cloudformation:UpdateStack', - 'cloudformation:GetTemplate*', - 'cloudformation:ValidateTemplate', + 'cloudformation:DescribeStack*', 'cloudformation:GetStackPolicy', + 'cloudformation:GetTemplate*', 'cloudformation:SetStackPolicy', + 'cloudformation:UpdateStack', + 'cloudformation:ValidateTemplate', ]; if (props.replaceOnFailure) { actions.push('cloudformation:DeleteStack'); } - this.statementFor({ actions }).addResources(this.stackArnFromProps(props)); + this.addToPrincipalPolicy(new iam.PolicyStatement({ + actions, + resources: [this.stackArnFromProps(props)], + })); } public grantDeleteStack(props: { stackName: string, region?: string }): void { - this.statementFor({ + this.addToPrincipalPolicy(new iam.PolicyStatement({ actions: [ - 'cloudformation:DescribeStack*', 'cloudformation:DeleteStack', + 'cloudformation:DescribeStack*', ], - }).addResources(this.stackArnFromProps(props)); + resources: [this.stackArnFromProps(props)], + })); } public grantPassRole(role: iam.IRole): void { - this.statementFor({ actions: ['iam:PassRole'] }).addResources(role.roleArn); - } - - private statementFor(template: StatementTemplate): iam.PolicyStatement { - const key = keyFor(template); - if (!(key in this.statements)) { - this.statements[key] = new iam.PolicyStatement({ actions: template.actions }); - if (template.conditions) { - this.statements[key].addConditions(template.conditions); - } - this.role.addToPolicy(this.statements[key]); - } - return this.statements[key]; - - function keyFor(props: StatementTemplate): string { - const actions = `${props.actions.sort().join('\x1F')}`; - const conditions = formatConditions(props.conditions); - return `${actions}\x1D${conditions}`; - - function formatConditions(cond?: StatementCondition): string { - if (cond == null) { return ''; } - let result = ''; - for (const op of Object.keys(cond).sort()) { - result += `${op}\x1E`; - const condition = cond[op]; - for (const attribute of Object.keys(condition).sort()) { - const value = condition[attribute]; - result += `${value}\x1F`; - } - } - return result; - } - } + this.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['iam:PassRole'], + resources: [role.roleArn], + })); } private stackArnFromProps(props: { stackName: string, region?: string }): string { @@ -638,13 +610,6 @@ class SingletonPolicy extends Construct implements iam.IGrantable { } } -interface StatementTemplate { - actions: string[]; - conditions?: StatementCondition; -} - -type StatementCondition = { [op: string]: { [attribute: string]: string } }; - function parseCapabilities(capabilities: cdk.CfnCapabilities[] | undefined): string | undefined { if (capabilities === undefined) { return undefined; diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/pipeline-actions.test.ts b/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/pipeline-actions.test.ts index 3d9594a9ddfbd..5e0526ea75846 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/pipeline-actions.test.ts +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/pipeline-actions.test.ts @@ -442,10 +442,15 @@ class RoleDouble extends iam.Role { } public addToPolicy(statement: iam.PolicyStatement): boolean { - super.addToPolicy(statement); - this.statements.push(statement); + this.addToPrincipalPolicy(statement); return true; } + + public addToPrincipalPolicy(statement: iam.PolicyStatement): iam.AddToPrincipalPolicyResult { + const ret = super.addToPrincipalPolicy(statement); + this.statements.push(statement); + return ret; + } } class BucketDouble extends s3.Bucket { diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json index 2ce8cf8f817bf..8cc3a8836e120 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.cfn-template-from-repo.lit.expected.json @@ -157,42 +157,32 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployPrepareChangesCodePipelineActionRole41931444", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployApproveChangesCodePipelineActionRole5AA6E21B", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployExecuteChangesCodePipelineActionRole6AA2756F", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceCodePipelineActionRoleC6F9E7F5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployPrepareChangesCodePipelineActionRole41931444", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployApproveChangesCodePipelineActionRole5AA6E21B", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployExecuteChangesCodePipelineActionRole6AA2756F", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json index 3605965c27ac5..f11f639ae81f1 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-deployed-through-codepipeline.lit.expected.json @@ -151,52 +151,38 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceCdkCodeSourceCodePipelineActionRole237947B8", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceLambdaCodeSourceCodePipelineActionRole4E89EF60", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildCDKBuildCodePipelineActionRole15F4B424", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildLambdaBuildCodePipelineActionRole2DAE39E9", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployLambdaCFNDeployCodePipelineActionRoleF8A74488", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceCdkCodeSourceCodePipelineActionRole237947B8", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceLambdaCodeSourceCodePipelineActionRole4E89EF60", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineBuildCDKBuildCodePipelineActionRole15F4B424", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineBuildLambdaBuildCodePipelineActionRole2DAE39E9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployLambdaCFNDeployCodePipelineActionRoleF8A74488", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json index 8f55220a1be97..1cc01e3112e57 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.lambda-pipeline.expected.json @@ -151,22 +151,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineLambdaCodePipelineActionRoleC6032822", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceCodePipelineActionRoleC6F9E7F5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineLambdaCodePipelineActionRoleC6032822", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json index 85ddb7d7dc4a9..eb12087ab0088 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-cross-region.expected.json @@ -68,22 +68,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceS3CodePipelineActionRole9F003087", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineCFNCFNDeployCodePipelineActionRole31B1904C", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceS3CodePipelineActionRole9F003087", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineCFNCFNDeployCodePipelineActionRole31B1904C", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json index 81c9c5fc2a998..701aa4f3d4eb5 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn-with-action-role.expected.json @@ -193,22 +193,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceS3CodePipelineActionRole9F003087", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "ActionRole60B0EDF7", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceS3CodePipelineActionRole9F003087", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ActionRole60B0EDF7", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json index 52df200d381a2..ce55ac9bdaa5f 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-cfn.expected.json @@ -151,32 +151,26 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceAdditionalSourceCodePipelineActionRole0897461A", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineCFNDeployCFNCodePipelineActionRole444CF5DD", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceCodePipelineActionRoleC6F9E7F5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineSourceAdditionalSourceCodePipelineActionRole0897461A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineCFNDeployCFNCodePipelineActionRole444CF5DD", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json index 67025b2e96c68..9ba56e476d02f 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-batch.expected.json @@ -139,12 +139,14 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineRoleD68726F7", + "Arn" + ] + } + ] }, { "Action": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json index 46205d6455441..e396f81d434ca 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-build-multiple-inputs-outputs.expected.json @@ -139,12 +139,14 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineRoleD68726F7", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineRoleD68726F7", + "Arn" + ] + } + ] }, { "Action": [ diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json index 6292ae43e5811..9ae6fd13b8b5c 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit-build.expected.json @@ -408,32 +408,26 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinesourceCodePipelineActionRoleB7E0306A", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinebuildCodePipelineActionRole11BCD4FF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinebuildtestCodePipelineActionRole467D0DFA", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelinesourceCodePipelineActionRoleB7E0306A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinebuildCodePipelineActionRole11BCD4FF", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinebuildtestCodePipelineActionRole467D0DFA", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json index fe71ea09ade8d..310c029130800 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-commit.expected.json @@ -222,22 +222,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinesourceCodePipelineActionRoleB7E0306A", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinebuildmanualCodePipelineActionRoleE3306AB0", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelinesourceCodePipelineActionRoleB7E0306A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinebuildmanualCodePipelineActionRoleE3306AB0", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json index ad229d36e2207..a8e31f0d1dccb 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy-ecs.expected.json @@ -68,22 +68,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceS3SourceCodePipelineActionRole8DE11A40", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployCodeDeployCodePipelineActionRoleFA7F8EEF", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceS3SourceCodePipelineActionRole8DE11A40", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployCodeDeployCodePipelineActionRoleFA7F8EEF", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json index d2d0bea52821f..06174512eafb1 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-code-deploy.expected.json @@ -153,22 +153,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceS3SourceCodePipelineActionRole8DE11A40", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployCodeDeployCodePipelineActionRoleFA7F8EEF", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceS3SourceCodePipelineActionRole8DE11A40", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployCodeDeployCodePipelineActionRoleFA7F8EEF", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json index c200ab454d71a..71e9715e5f31c 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecr-source.expected.json @@ -63,22 +63,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceECRSourceCodePipelineActionRole4C6714EE", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineApproveManualApprovalCodePipelineActionRole9E338F01", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json index 3f7139d458fad..e5f3f84d9dbbb 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-deploy.expected.json @@ -602,32 +602,26 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceCodePipelineActionRoleAA05D76F", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineBuildCodeBuildCodePipelineActionRoleCAE538CA", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineDeployDeployActionCodePipelineActionRole854184EF", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceCodePipelineActionRoleAA05D76F", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineBuildCodeBuildCodePipelineActionRoleCAE538CA", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineDeployDeployActionCodePipelineActionRole854184EF", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json index e8cbcadeef665..bbd851fa61c38 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-ecs-separate-source.lit.expected.json @@ -662,52 +662,38 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployingEcsApplicationSourceAppCodeSourceCodePipelineActionRole6D88B36F", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployingEcsApplicationSourceCdkCodeSourceCodePipelineActionRoleA1E3A5E9", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployingEcsApplicationBuildAppCodeDockerImageBuildAndPushCodePipelineActionRole9B025737", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployingEcsApplicationBuildCdkCodeBuildAndSynthCodePipelineActionRole54094521", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "CodePipelineDeployingEcsApplicationDeployCFNDeployCodePipelineActionRoleC97FFCE2", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "CodePipelineDeployingEcsApplicationSourceAppCodeSourceCodePipelineActionRole6D88B36F", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "CodePipelineDeployingEcsApplicationSourceCdkCodeSourceCodePipelineActionRoleA1E3A5E9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "CodePipelineDeployingEcsApplicationBuildAppCodeDockerImageBuildAndPushCodePipelineActionRole9B025737", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "CodePipelineDeployingEcsApplicationBuildCdkCodeBuildAndSynthCodePipelineActionRole54094521", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "CodePipelineDeployingEcsApplicationDeployCFNDeployCodePipelineActionRoleC97FFCE2", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json index 7506be08efa70..e173ec9b942a3 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-events.expected.json @@ -151,22 +151,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceCodeCommitSourceCodePipelineActionRole0B6D0F4F", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineBuildCodeBuildActionCodePipelineActionRole3185ADC7", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceCodeCommitSourceCodePipelineActionRole0B6D0F4F", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineBuildCodeBuildActionCodePipelineActionRole3185ADC7", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json index f0c66384b1709..6e549ed506d3a 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-manual-approval.expected.json @@ -63,22 +63,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceS3CodePipelineActionRole3CAFD08F", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineApproveManualApprovalCodePipelineActionRole51D669A5", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceS3CodePipelineActionRole3CAFD08F", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineApproveManualApprovalCodePipelineActionRole51D669A5", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json index ecd97ebdd239d..0745c28cde329 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-s3-deploy.expected.json @@ -73,22 +73,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineDeployDeployActionCodePipelineActionRole1C288A60", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineSourceCodePipelineActionRoleC6F9E7F5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineDeployDeployActionCodePipelineActionRole1C288A60", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json index 142c2c1d92e45..36095eaed8769 100644 --- a/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json +++ b/packages/@aws-cdk/aws-codepipeline-actions/test/integ.pipeline-stepfunctions.expected.json @@ -194,22 +194,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineSourceCodePipelineActionRoleAA05D76F", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "MyPipelineInvokeCodePipelineActionRole006B5BAD", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "MyPipelineSourceCodePipelineActionRoleAA05D76F", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "MyPipelineInvokeCodePipelineActionRole006B5BAD", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts index 65b7e84abbf61..1e37998bb4a1b 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts @@ -317,6 +317,7 @@ export class Pipeline extends PipelineBase { private readonly _crossRegionSupport: { [region: string]: CrossRegionSupport } = {}; private readonly _crossAccountSupport: { [account: string]: Stack } = {}; private readonly crossAccountKeys: boolean; + private readonly principalForGrants: iam.GroupingByActionsPrincipal; constructor(scope: Construct, id: string, props: PipelineProps = {}) { super(scope, id, { @@ -368,6 +369,7 @@ export class Pipeline extends PipelineBase { this.role = props.role || new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'), }); + this.principalForGrants = new iam.GroupingByActionsPrincipal(this.role, 'RoleWrapper'); const codePipeline = new CfnPipeline(this, 'Resource', { artifactStore: Lazy.any({ produce: () => this.renderArtifactStoreProperty() }), @@ -381,7 +383,7 @@ export class Pipeline extends PipelineBase { // this will produce a DependsOn for both the role and the policy resources. codePipeline.node.addDependency(this.role); - this.artifactBucket.grantReadWrite(this.role); + this.artifactBucket.grantReadWrite(this.principalForGrants); this.pipelineName = this.getResourceNameAttribute(codePipeline.ref); this.pipelineVersion = codePipeline.attrVersion; this.crossRegionBucketsPassed = !!props.crossRegionReplicationBuckets; @@ -549,7 +551,7 @@ export class Pipeline extends PipelineBase { // the stack containing the replication bucket must be deployed before the pipeline Stack.of(this).addDependency(crossRegionSupport.stack); // The Pipeline role must be able to replicate to that bucket - crossRegionSupport.replicationBucket.grantReadWrite(this.role); + crossRegionSupport.replicationBucket.grantReadWrite(this.principalForGrants); return { artifactBucket: crossRegionSupport.replicationBucket, @@ -666,7 +668,7 @@ export class Pipeline extends PipelineBase { // the pipeline role needs assumeRole permissions to the action role if (actionRole) { - this.role.addToPrincipalPolicy(new iam.PolicyStatement({ + this.principalForGrants.addToPrincipalPolicy(new iam.PolicyStatement({ actions: ['sts:AssumeRole'], resources: [actionRole.roleArn], })); diff --git a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json index 7f2c9d48da34b..27054e856f6d9 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codepipeline/integ.pipeline-event-target.expected.json @@ -157,22 +157,20 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "pipelinePipeline22F2A91DSourceCodeCommitCodePipelineActionRoleE54633E5", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "pipelinePipeline22F2A91DBuildHelloCodePipelineActionRoleA9729116", - "Arn" - ] - } + "Resource": [ + { + "Fn::GetAtt": [ + "pipelinePipeline22F2A91DSourceCodeCommitCodePipelineActionRoleE54633E5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "pipelinePipeline22F2A91DBuildHelloCodePipelineActionRoleA9729116", + "Arn" + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/aws-iam/lib/grouping-by-actions-principal.ts b/packages/@aws-cdk/aws-iam/lib/grouping-by-actions-principal.ts new file mode 100644 index 0000000000000..f294225493298 --- /dev/null +++ b/packages/@aws-cdk/aws-iam/lib/grouping-by-actions-principal.ts @@ -0,0 +1,68 @@ +import * as crypto from 'crypto'; +import { IIdentity } from './identity-base'; +import { PolicyStatement } from './policy-statement'; +import { AddToPrincipalPolicyResult, IGrantable, IPrincipal, PrincipalPolicyFragment } from './principals'; + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct } from '@aws-cdk/core'; + +/** + * An IAM Principal that wraps a different Principal, + * and groups multiple Policy Statements that share the same 'actions' property together, + * thus compressing the overall size of the Policy attached the given Principal, + * and making it less likely it hits the 10240 bytes IAM limit. + */ +export class GroupingByActionsPrincipal extends Construct implements IPrincipal, IGrantable { + public readonly assumeRoleAction: string; + public readonly policyFragment: PrincipalPolicyFragment; + public readonly principalAccount?: string | undefined; + public readonly grantPrincipal: IPrincipal; + + /** The original principal that this class wraps. */ + public readonly wrappedIdentity: IIdentity; + private readonly statements: { [key: string]: { statement: PolicyStatement, grantResult: AddToPrincipalPolicyResult } }; + + constructor(grantPrincipal: IIdentity, id: string) { + super(grantPrincipal as unknown as Construct, id); + + this.assumeRoleAction = grantPrincipal.assumeRoleAction; + this.policyFragment = grantPrincipal.policyFragment; + this.principalAccount = grantPrincipal.principalAccount; + this.grantPrincipal = this; + + this.wrappedIdentity = grantPrincipal; + this.statements = {}; + } + + public addToPolicy(statement: PolicyStatement): boolean { + return this.addToPrincipalPolicy(statement).statementAdded; + } + + public addToPrincipalPolicy(statement: PolicyStatement): AddToPrincipalPolicyResult { + const key = this.keyFor(statement); + const statementCache = this.statements[key]; + if (!statementCache) { + const grantResult = this.wrappedIdentity.addToPrincipalPolicy(statement); + this.statements[key] = { statement, grantResult }; + return grantResult; + } else { + statementCache.statement.addResources(...statement.resources); + statementCache.statement.addNotResources(...statement.notResources); + return statementCache.grantResult; + } + } + + private keyFor(statement: PolicyStatement): string { + const hashBuilder = crypto.createHash('sha256'); + + const statementJson = statement.toStatementJson(); + // don't include the Resource and NotResource parts in the hash, + // so that later Statements that share the other parts can be grouped with this one + delete statementJson.Resource; + delete statementJson.NotResource; + + hashBuilder.update(JSON.stringify(statementJson)); + return hashBuilder.digest('hex'); + } +} diff --git a/packages/@aws-cdk/aws-iam/lib/index.ts b/packages/@aws-cdk/aws-iam/lib/index.ts index 06c2a9bb6cdcd..e6d32353922d4 100644 --- a/packages/@aws-cdk/aws-iam/lib/index.ts +++ b/packages/@aws-cdk/aws-iam/lib/index.ts @@ -13,6 +13,7 @@ export * from './unknown-principal'; export * from './oidc-provider'; export * from './permissions-boundary'; export * from './saml-provider'; +export * from './grouping-by-actions-principal'; // AWS::IAM CloudFormation Resources: export * from './iam.generated'; diff --git a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts index 14ca172de5506..f6ccb66bb5fb9 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts @@ -290,6 +290,22 @@ export class PolicyStatement { return this.resource && this.resource.length > 0; } + /** + * Returns the current list of resources + * (the Resource field of the policy statement). + */ + public get resources(): string[] { + return [...this.resource]; + } + + /** + * Returns the current list of notResources + * (the NotResource field of the policy statement). + */ + public get notResources(): string[] { + return [...this.notResource]; + } + // // Condition // diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index ef766c83c1a25..04da22c6e2058 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -101,6 +101,7 @@ "awslint": { "exclude": [ "from-signature:@aws-cdk/aws-iam.Role.fromRoleArn", + "construct-ctor:@aws-cdk/aws-iam.GroupingByActionsPrincipal..params[0]", "construct-interface-extends-iconstruct:@aws-cdk/aws-iam.IManagedPolicy", "props-physical-name:@aws-cdk/aws-iam.OpenIdConnectProviderProps", "props-physical-name:@aws-cdk/aws-iam.SamlProviderProps", diff --git a/packages/@aws-cdk/aws-kms/lib/key.ts b/packages/@aws-cdk/aws-kms/lib/key.ts index aae71efb460a7..fffd704b3852f 100644 --- a/packages/@aws-cdk/aws-kms/lib/key.ts +++ b/packages/@aws-cdk/aws-kms/lib/key.ts @@ -223,6 +223,11 @@ abstract class KeyBase extends Resource implements IKey { private principalIsANewlyCreatedResource(principal: IConstruct): boolean { // yes, this sucks // this is just a temporary stopgap to stem the bleeding while we work on a proper fix + + if (principal instanceof iam.GroupingByActionsPrincipal) { + return this.principalIsANewlyCreatedResource(principal.wrappedIdentity); + } + return principal instanceof iam.Role || principal instanceof iam.User || principal instanceof iam.Group; @@ -691,4 +696,4 @@ function isConstruct(x: any): x is Construct { (x instanceof Construct // happy fast case || !!(x as any).node // constructs v10 || !!(x as any)[sym])); // constructs v3 -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/core/lib/token.ts b/packages/@aws-cdk/core/lib/token.ts index 9b87a0792fa2b..b629e9a0a72cd 100644 --- a/packages/@aws-cdk/core/lib/token.ts +++ b/packages/@aws-cdk/core/lib/token.ts @@ -16,21 +16,29 @@ export class TokenComparison { * This means we're certain the two components are NOT * Tokens, and identical. */ - public static readonly SAME = new TokenComparison(); + public static readonly SAME = new TokenComparison('SAME'); /** * This means we're certain the two components are NOT * Tokens, and different. */ - public static readonly DIFFERENT = new TokenComparison(); + public static readonly DIFFERENT = new TokenComparison('DIFFERENT'); /** This means exactly one of the components is a Token. */ - public static readonly ONE_UNRESOLVED = new TokenComparison(); + public static readonly ONE_UNRESOLVED = new TokenComparison('ONE_UNRESOLVED'); /** This means both components are Tokens. */ - public static readonly BOTH_UNRESOLVED = new TokenComparison(); + public static readonly BOTH_UNRESOLVED = new TokenComparison('BOTH_UNRESOLVED'); - private constructor() { + private constructor(private readonly label: string) { + } + + /** + * Returns the name of the constant as the string representation of this class. + * This is useful for debugging purposes. + */ + public toString() { + return this.label; } } diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json index e0075942e6069..4ef39530ec1ec 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline-with-vpc.expected.json @@ -662,69 +662,55 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildSynthCodePipelineActionRole4E7A6C97", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineAssetsFileAsset2CodePipelineActionRole06965A59", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::", - { - "Ref": "AWS::AccountId" - }, - ":role/cdk-hnb659fds-deploy-role-", - { - "Ref": "AWS::AccountId" - }, - "-", - { - "Ref": "AWS::Region" - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset1CodePipelineActionRoleC0EC649A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineAssetsFileAsset2CodePipelineActionRole06965A59", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json index 07e65b9bd643f..0157b975fa9f6 100644 --- a/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.newpipeline.expected.json @@ -148,49 +148,43 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildSynthCodePipelineActionRole4E7A6C97", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::", - { - "Ref": "AWS::AccountId" - }, - ":role/cdk-hnb659fds-deploy-role-", - { - "Ref": "AWS::AccountId" - }, - "-", - { - "Ref": "AWS::Region" - } + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/cdk-hnb659fds-deploy-role-", + { + "Ref": "AWS::AccountId" + }, + "-", + { + "Ref": "AWS::Region" + } + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json index 82ad5c418fb70..ed99a836decce 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-security.expected.json @@ -226,108 +226,74 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelineBuildSynthCodePipelineActionRoleF7BF5926", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelineUnattachedStageSingleStageSecurityCheckCodePipelineActionRoleFF6E43E2", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelineUnattachedStageSingleStageManualApprovalCodePipelineActionRoleF7A614C8", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelinePreProductionPreProductionSecurityCheckCodePipelineActionRole4E54C194", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelinePreProductionPreProductionManualApprovalCodePipelineActionRole81B9C4F9", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelinePreProductionSafeProductionSecurityCheckCodePipelineActionRole399C68A6", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelinePreProductionSafeProductionManualApprovalCodePipelineActionRole4F30C0D9", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelineNoSecurityCheckEnableSecurityCheckSecurityCheckCodePipelineActionRole8D10AA6D", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "TestPipelineNoSecurityCheckEnableSecurityCheckManualApprovalCodePipelineActionRole27FC4015", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + "Resource": [ + { + "Fn::GetAtt": [ + "TestPipelineBuildSynthCodePipelineActionRoleF7BF5926", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "TestPipelineUnattachedStageSingleStageSecurityCheckCodePipelineActionRoleFF6E43E2", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelineUnattachedStageSingleStageManualApprovalCodePipelineActionRoleF7A614C8", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelinePreProductionPreProductionSecurityCheckCodePipelineActionRole4E54C194", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelinePreProductionPreProductionManualApprovalCodePipelineActionRole81B9C4F9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelinePreProductionSafeProductionSecurityCheckCodePipelineActionRole399C68A6", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelinePreProductionSafeProductionManualApprovalCodePipelineActionRole4F30C0D9", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelineNoSecurityCheckEnableSecurityCheckSecurityCheckCodePipelineActionRole8D10AA6D", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "TestPipelineNoSecurityCheckEnableSecurityCheckManualApprovalCodePipelineActionRole27FC4015", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json index 8250f113b53e3..4750e33741e55 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets-single-upload.expected.json @@ -226,58 +226,44 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildSynthCodePipelineActionRole4E7A6C97", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineAssetsFileRole59943A77", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json index c2e4cddc58aef..26ab54cd5485f 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline-with-assets.expected.json @@ -226,58 +226,44 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildSynthCodePipelineActionRole4E7A6C97", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineAssetsFileRole59943A77", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelineAssetsFileRole59943A77", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json index 32a0a50bd90d5..c3bd6d936f43a 100644 --- a/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json +++ b/packages/@aws-cdk/pipelines/test/integ.pipeline.expected.json @@ -226,48 +226,38 @@ { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineBuildSynthCodePipelineActionRole4E7A6C97", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", - "Arn" - ] - } - }, - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + "Resource": [ + { + "Fn::GetAtt": [ + "PipelineBuildSynthCodePipelineActionRole4E7A6C97", + "Arn" ] - ] - } + }, + { + "Fn::GetAtt": [ + "PipelineUpdatePipelineSelfMutateCodePipelineActionRoleD6D4E5CF", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "PipelinePreProdUseSourceCodePipelineActionRoleA2043BDA", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region" + ] + ] + } + ] } ], "Version": "2012-10-17" diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap index 86a1fdd32f534..40cc4e1bee52a 100644 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ b/packages/decdk/test/__snapshots__/synth.test.js.snap @@ -2504,32 +2504,26 @@ Object { Object { "Action": "sts:AssumeRole", "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineSourceCodePipelineActionRoleC6F9E7F5", - "Arn", - ], - }, - }, - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineBuildCodePipelineActionRoleD77A08E6", - "Arn", - ], - }, - }, - Object { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Resource": Object { - "Fn::GetAtt": Array [ - "PipelineDeployCodePipelineActionRole8B83082E", - "Arn", - ], - }, + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "PipelineSourceCodePipelineActionRoleC6F9E7F5", + "Arn", + ], + }, + Object { + "Fn::GetAtt": Array [ + "PipelineBuildCodePipelineActionRoleD77A08E6", + "Arn", + ], + }, + Object { + "Fn::GetAtt": Array [ + "PipelineDeployCodePipelineActionRole8B83082E", + "Arn", + ], + }, + ], }, ], "Version": "2012-10-17",