Skip to content

Commit 88d563f

Browse files
authored
fix(pipelines): reduce assets IAM policy size (#9333)
Collapse the PipelineAssetsRoleDefaultPolicy into a single up-front policy that doesn't grow per-asset. This relaxes some of the permissions in exchange for avoiding an O(N) policy size. fixes #9316 _Testing notes:_ Successfully deployed a pipeline with 49 assets (25 file assets and 24 docker assets). 50 assets is the limit for a single stage of the pipeline. To scale out past 50 assets, we will need to segment the assets pipeline stage into multiple stages. I'm considering that out-of-scope for this bugfix. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 2a48495 commit 88d563f

File tree

3 files changed

+213
-166
lines changed

3 files changed

+213
-166
lines changed

packages/@aws-cdk/pipelines/lib/pipeline.ts

+77-12
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,11 @@ interface AssetPublishingProps {
242242
*/
243243
class AssetPublishing extends Construct {
244244
private readonly publishers: Record<string, PublishAssetsAction> = {};
245+
private readonly assetRoles: Record<string, iam.IRole> = {};
245246
private readonly myCxAsmRoot: string;
246247

247248
private readonly stage: codepipeline.IStage;
248-
private assetRole?: iam.Role;
249+
private readonly pipeline: codepipeline.Pipeline;
249250
private _fileAssetCtr = 1;
250251
private _dockerAssetCtr = 1;
251252

@@ -256,6 +257,7 @@ class AssetPublishing extends Construct {
256257
// We MUST add the Stage immediately here, otherwise it will be in the wrong place
257258
// in the pipeline!
258259
this.stage = this.props.pipeline.addStage({ stageName: 'Assets' });
260+
this.pipeline = this.props.pipeline;
259261
}
260262

261263
/**
@@ -269,15 +271,9 @@ class AssetPublishing extends Construct {
269271
// FIXME: this is silly, we need the relative path here but no easy way to get it
270272
const relativePath = path.relative(this.myCxAsmRoot, command.assetManifestPath);
271273

272-
// This role is used by both the CodePipeline build action and related CodeBuild project. Consolidating these two
273-
// roles into one, and re-using across all assets, saves significant size of the final synthesized output.
274-
// Modeled after the CodePipeline role and 'CodePipelineActionRole' roles.
275-
// Late-binding here to prevent creating the role in cases where no asset actions are created.
276-
if (!this.assetRole) {
277-
this.assetRole = new iam.Role(this, 'Role', {
278-
roleName: PhysicalName.GENERATE_IF_NEEDED,
279-
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('codebuild.amazonaws.com'), new iam.AccountPrincipal(Stack.of(this).account)),
280-
});
274+
// Late-binding here (rather than in the constructor) to prevent creating the role in cases where no asset actions are created.
275+
if (!this.assetRoles[command.assetType]) {
276+
this.generateAssetRole(command.assetType);
281277
}
282278

283279
let action = this.publishers[command.assetId];
@@ -298,7 +294,7 @@ class AssetPublishing extends Construct {
298294
cloudAssemblyInput: this.props.cloudAssemblyInput,
299295
cdkCliVersion: this.props.cdkCliVersion,
300296
assetType: command.assetType,
301-
role: this.assetRole,
297+
role: this.assetRoles[command.assetType],
302298
});
303299
this.stage.addAction(action);
304300
}
@@ -321,9 +317,78 @@ class AssetPublishing extends Construct {
321317
}
322318
}
323319
}
320+
321+
/**
322+
* This role is used by both the CodePipeline build action and related CodeBuild project. Consolidating these two
323+
* roles into one, and re-using across all assets, saves significant size of the final synthesized output.
324+
* Modeled after the CodePipeline role and 'CodePipelineActionRole' roles.
325+
* Generates one role per asset type to separate file and Docker/image-based permissions.
326+
*/
327+
private generateAssetRole(assetType: AssetType) {
328+
if (this.assetRoles[assetType]) { return this.assetRoles[assetType]; }
329+
330+
const rolePrefix = assetType === AssetType.DOCKER_IMAGE ? 'Docker' : 'File';
331+
const assetRole = new iam.Role(this, `${rolePrefix}Role`, {
332+
roleName: PhysicalName.GENERATE_IF_NEEDED,
333+
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('codebuild.amazonaws.com'), new iam.AccountPrincipal(Stack.of(this).account)),
334+
});
335+
336+
// Logging permissions
337+
const logGroupArn = Stack.of(this).formatArn({
338+
service: 'logs',
339+
resource: 'log-group',
340+
sep: ':',
341+
resourceName: '/aws/codebuild/*',
342+
});
343+
assetRole.addToPolicy(new iam.PolicyStatement({
344+
resources: [logGroupArn],
345+
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
346+
}));
347+
348+
// CodeBuild report groups
349+
const codeBuildArn = Stack.of(this).formatArn({
350+
service: 'codebuild',
351+
resource: 'report-group',
352+
resourceName: '*',
353+
});
354+
assetRole.addToPolicy(new iam.PolicyStatement({
355+
actions: [
356+
'codebuild:CreateReportGroup',
357+
'codebuild:CreateReport',
358+
'codebuild:UpdateReport',
359+
'codebuild:BatchPutTestCases',
360+
],
361+
resources: [codeBuildArn],
362+
}));
363+
364+
// CodeBuild start/stop
365+
assetRole.addToPolicy(new iam.PolicyStatement({
366+
resources: ['*'],
367+
actions: [
368+
'codebuild:BatchGetBuilds',
369+
'codebuild:StartBuild',
370+
'codebuild:StopBuild',
371+
],
372+
}));
373+
374+
// Publishing role access
375+
const rolePattern = assetType === AssetType.DOCKER_IMAGE
376+
? 'arn:*:iam::*:role/*-image-publishing-role-*'
377+
: 'arn:*:iam::*:role/*-file-publishing-role-*';
378+
assetRole.addToPolicy(new iam.PolicyStatement({
379+
actions: ['sts:AssumeRole'],
380+
resources: [rolePattern],
381+
}));
382+
383+
// Artifact access
384+
this.pipeline.artifactBucket.grantRead(assetRole);
385+
386+
this.assetRoles[assetType] = assetRole.withoutPolicyUpdates();
387+
return this.assetRoles[assetType];
388+
}
324389
}
325390

326391
function maybeSuffix(x: string | undefined, suffix: string): string | undefined {
327392
if (x === undefined) { return undefined; }
328393
return `${x}${suffix}`;
329-
}
394+
}

0 commit comments

Comments
 (0)