Skip to content

Commit

Permalink
fix(codebuild): validate if a CodePipeline action that is cross-accou…
Browse files Browse the repository at this point in the history
…nt does not have outputs

Fixes #4032
  • Loading branch information
skinny85 committed Sep 20, 2019
1 parent 2ce4a87 commit 358a00a
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ export interface CodeBuildActionProps extends codepipeline.CommonAwsActionProps
* @default CodeBuildActionType.BUILD
*/
readonly type?: CodeBuildActionType;

/**
* Whether to validate if the action,
* if it references a project in a different account,
* does not have any outputs.
* This is a known CodeBuild limitation.
* If the action is cross-account, and has outputs,
* and this property is true (which is the default),
* an exception will be thrown.
* You can set this to false to skip this validation.
*
* @default true
* @see https://github.com/aws/aws-cdk/issues/4169
*/
readonly validateCrossAccountOutputs?: boolean;
}

/**
Expand All @@ -83,8 +98,21 @@ export class CodeBuildAction extends Action {
this.props = props;
}

protected bound(_scope: cdk.Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
protected bound(scope: cdk.Construct, _stage: codepipeline.IStage, options: codepipeline.ActionBindOptions):
codepipeline.ActionConfig {
// check for a cross-account action if there are any outputs
if (this.props.validateCrossAccountOutputs !== false &&
(this.actionProperties.outputs || []).length > 0) {
const pipelineStack = cdk.Stack.of(scope);
const projectStack = cdk.Stack.of(this.props.project);
if (pipelineStack.account !== projectStack.account) {
throw new Error('A cross-account CodeBuild action cannot have outputs. ' +
'This is a know CodeBuild limitation. ' +
'See https://github.com/aws/aws-cdk/issues/4169 for details. ' +
'You can pass the validateCrossAccountOutputs property as false to skip this validation');
}
}

// grant the Pipeline role the required permissions to this Project
options.role.addToPolicy(new iam.PolicyStatement({
resources: [this.props.project.projectArn],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import codebuild = require('@aws-cdk/aws-codebuild');
import codecommit = require('@aws-cdk/aws-codecommit');
import codepipeline = require('@aws-cdk/aws-codepipeline');
import { App, Stack } from '@aws-cdk/core';
import { Test } from 'nodeunit';
import cpactions = require('../../lib');

// tslint:disable:object-literal-key-quotes

export = {
'a cross-account CodeBuild action with outputs': {
'causes an error by default'(test: Test) {
const app = new App();

const projectStack = new Stack(app, 'ProjectStack', {
env: {
region: 'us-west-2',
account: '012345678901',
},
});
const project = new codebuild.PipelineProject(projectStack, 'Project');

const pipelineStack = new Stack(app, 'PipelineStack', {
env: {
region: 'us-west-2',
account: '123456789012',
},
});
const sourceOutput = new codepipeline.Artifact();
const pipeline = new codepipeline.Pipeline(pipelineStack, 'Pipeline', {
stages: [
{
stageName: 'Source',
actions: [new cpactions.CodeCommitSourceAction({
actionName: 'CodeCommit',
repository: codecommit.Repository.fromRepositoryName(pipelineStack, 'Repo', 'repo-name'),
output: sourceOutput,
})],
},
],
});
const buildStage = pipeline.addStage({
stageName: 'Build',
});

// this works fine - no outputs!
buildStage.addAction(new cpactions.CodeBuildAction({
actionName: 'Build1',
input: sourceOutput,
project,
}));

const buildAction2 = new cpactions.CodeBuildAction({
actionName: 'Build2',
input: sourceOutput,
project,
outputs: [new codepipeline.Artifact()],
});

test.throws(() => {
buildStage.addAction(buildAction2);
}, /https:\/\/github\.com\/aws\/aws-cdk\/issues\/4169/);

test.done();
},

'works if validateCrossAccountOutputs is passed as false'(test: Test) {
const app = new App();

const projectStack = new Stack(app, 'ProjectStack', {
env: {
region: 'us-west-2',
account: '012345678901',
},
});
const project = new codebuild.PipelineProject(projectStack, 'Project');

const pipelineStack = new Stack(app, 'PipelineStack', {
env: {
region: 'us-west-2',
account: '123456789012',
},
});
const sourceOutput = new codepipeline.Artifact();
const pipeline = new codepipeline.Pipeline(pipelineStack, 'Pipeline', {
stages: [
{
stageName: 'Source',
actions: [new cpactions.CodeCommitSourceAction({
actionName: 'CodeCommit',
repository: codecommit.Repository.fromRepositoryName(pipelineStack, 'Repo', 'repo-name'),
output: sourceOutput,
})],
},
],
});
const buildStage = pipeline.addStage({
stageName: 'Build',
});

buildStage.addAction(new cpactions.CodeBuildAction({
actionName: 'Build2',
input: sourceOutput,
project,
outputs: [new codepipeline.Artifact()],
validateCrossAccountOutputs: false,
}));

test.done();
},
},
};

0 comments on commit 358a00a

Please sign in to comment.