Skip to content

Commit

Permalink
fix(pipelines): Reduce template size by combining IAM roles and polic…
Browse files Browse the repository at this point in the history
…ies (#9243)

This change combines the two roles (and policies) per pipeline asset into a
single role + policy for all assets in a pipeline. In a small pipeline with only
3 assets, this reduced the overall template size by a third.

With a pipeline stack with 30 assets, this change reduces the template size from
363111 bytes to 190223 bytes.

Tested on a cross-account pipeline to verify permissions were retained.

fixes #9066
mitigates #9225
mitigates #9237


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
njlynch authored Jul 28, 2020
1 parent c80cf56 commit 1ac6863
Show file tree
Hide file tree
Showing 5 changed files with 1,861 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export interface PublishAssetsActionProps {
* @default - Automatically generated
*/
readonly projectName?: string;

/**
* Role to use for CodePipeline and CodeBuild to build and publish the assets.
*
* @default - Automatically generated
*/
readonly role?: iam.IRole;
}

/**
Expand Down Expand Up @@ -87,6 +94,7 @@ export class PublishAssetsAction extends Construct implements codepipeline.IActi
}),
// Needed to perform Docker builds
environment: props.assetType === AssetType.DOCKER_IMAGE ? { privileged: true } : undefined,
role: props.role,
});

const rolePattern = props.assetType === AssetType.DOCKER_IMAGE
Expand All @@ -102,6 +110,7 @@ export class PublishAssetsAction extends Construct implements codepipeline.IActi
actionName: props.actionName,
project,
input: this.props.cloudAssemblyInput,
role: props.role,
});
}

Expand Down
19 changes: 16 additions & 3 deletions packages/@aws-cdk/pipelines/lib/pipeline.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import { App, CfnOutput, Construct, Stack, Stage } from '@aws-cdk/core';
import * as path from 'path';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as iam from '@aws-cdk/aws-iam';
import { App, CfnOutput, Construct, PhysicalName, Stack, Stage } from '@aws-cdk/core';
import { AssetType, DeployCdkStackAction, PublishAssetsAction, UpdatePipelineAction } from './actions';
import { appOf, assemblyBuilderOf } from './private/construct-internals';
import { AddStageOptions, AssetPublishingCommand, CdkStage, StackOutput } from './stage';
Expand Down Expand Up @@ -244,6 +245,7 @@ class AssetPublishing extends Construct {
private readonly myCxAsmRoot: string;

private readonly stage: codepipeline.IStage;
private assetRole?: iam.Role;
private _fileAssetCtr = 1;
private _dockerAssetCtr = 1;

Expand All @@ -267,6 +269,17 @@ class AssetPublishing extends Construct {
// FIXME: this is silly, we need the relative path here but no easy way to get it
const relativePath = path.relative(this.myCxAsmRoot, command.assetManifestPath);

// This role is used by both the CodePipeline build action and related CodeBuild project. Consolidating these two
// roles into one, and re-using across all assets, saves significant size of the final synthesized output.
// Modeled after the CodePipeline role and 'CodePipelineActionRole' roles.
// Late-binding here to prevent creating the role in cases where no asset actions are created.
if (!this.assetRole) {
this.assetRole = new iam.Role(this, 'Role', {
roleName: PhysicalName.GENERATE_IF_NEEDED,
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('codebuild.amazonaws.com'), new iam.AccountPrincipal(Stack.of(this).account)),
});
}

let action = this.publishers[command.assetId];
if (!action) {
// The asset ID would be a logical candidate for the construct path and project names, but if the asset
Expand All @@ -275,7 +288,6 @@ class AssetPublishing extends Construct {
//
// FIXME: The ultimate best solution is probably to generate a single Project per asset type
// and reuse that for all assets.

const id = command.assetType === AssetType.FILE ? `FileAsset${this._fileAssetCtr++}` : `DockerAsset${this._dockerAssetCtr++}`;

// NOTE: It's important that asset changes don't force a pipeline self-mutation.
Expand All @@ -286,6 +298,7 @@ class AssetPublishing extends Construct {
cloudAssemblyInput: this.props.cloudAssemblyInput,
cdkCliVersion: this.props.cdkCliVersion,
assetType: command.assetType,
role: this.assetRole,
});
this.stage.addAction(action);
}
Expand Down
Loading

0 comments on commit 1ac6863

Please sign in to comment.