From aca22cb9e412199c5e276e471d3d48d1f7883090 Mon Sep 17 00:00:00 2001 From: Rado Smogura Date: Mon, 17 Dec 2018 05:39:52 -0800 Subject: [PATCH] =?UTF-8?q?feat(aws-codepipeline):=20support=20for=20pipel?= =?UTF-8?q?ine=20action=E2=80=99s=20service=20role?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In realation to https://github.com/awslabs/aws-cdk/issues/49 The action’s service roles is a role which will be assumed by pipeline during execution of this action. The pipeline action’s service role can be used to perform more advanced configuration, when i.e. elevation of permissions is required, or when fine grained access control may be required. https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages-actions.html This commit is motivated by enabling cross-account deployments, for which service role will be used as jump role to assume one used by Cloud Formation in target account. --- .gitignore | 2 + .../lib/pipeline-actions.ts | 3 +- .../aws-codecommit/lib/pipeline-action.ts | 3 +- .../aws-codecommit/test/test.codecommit.ts | 24 +- .../aws-codedeploy/lib/pipeline-action.ts | 3 +- .../test/test.pipeline-action.ts | 46 +++ .../aws-codepipeline-api/lib/action.ts | 20 ++ .../lib/github-source-action.ts | 3 +- .../@aws-cdk/aws-codepipeline/lib/stage.ts | 1 + ...ipeline-cfn-wtih-action-role.expected.json | 317 ++++++++++++++++++ .../integ.pipeline-cfn-wtih-action-role.ts | 48 +++ .../test.cloudformation-pipeline-actions.ts | 51 ++- .../aws-codepipeline/test/test.pipeline.ts | 19 +- .../aws-lambda/lib/pipeline-action.ts | 1 + 14 files changed, 528 insertions(+), 13 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/test/test.pipeline-action.ts create mode 100644 packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json create mode 100644 packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.ts diff --git a/.gitignore b/.gitignore index 7b2c8a301c245..9b8a449c7187b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .vscode +# VSCode extension +/.favorites.json .DS_Store node_modules lerna-debug.log diff --git a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts index f38df0a6c4d22..aa50d05d1dedd 100644 --- a/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts +++ b/packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts @@ -59,8 +59,7 @@ export abstract class PipelineCloudFormationAction extends codepipeline.Action { constructor(scope: cdk.Construct, id: string, props: PipelineCloudFormationActionProps, configuration?: any) { super(scope, id, { - stage: props.stage, - runOrder: props.runOrder, + ...props, region: props.region, artifactBounds: { minInputs: 0, diff --git a/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts b/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts index 596a7d7882cad..68d0444565b5b 100644 --- a/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts @@ -48,8 +48,7 @@ export interface PipelineSourceActionProps extends CommonPipelineSourceActionPro export class PipelineSourceAction extends codepipeline.SourceAction { constructor(scope: cdk.Construct, id: string, props: PipelineSourceActionProps) { super(scope, id, { - stage: props.stage, - runOrder: props.runOrder, + ...props, provider: 'CodeCommit', configuration: { RepositoryName: props.repository.repositoryName, diff --git a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts index ef60996354dd3..87f453ac88230 100644 --- a/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts +++ b/packages/@aws-cdk/aws-codecommit/test/test.codecommit.ts @@ -1,6 +1,8 @@ +import * as iam from '@aws-cdk/aws-iam'; import { App, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { Repository, RepositoryProps } from '../lib'; +import { Pipeline } from '../../aws-codepipeline/lib/pipeline'; +import { PipelineSourceAction, Repository, RepositoryProps } from '../lib'; export = { 'default properties': { @@ -48,6 +50,26 @@ export = { test.throws(() => myRepository.notify('myTrigger')); + test.done(); + }, + + 'test passing properties to action'(test: Test) { + const app = new TestApp(); + const pipe = new Pipeline(app.stack, 'Pipe'); + const stage = pipe.addStage('MyStage1'); + + const action = new PipelineSourceAction(new TestApp().stack, 'Id', { + runOrder: 123, + stage, + repository: new Repository(app.stack, 'MyRepository', {repositoryName: 'MyRepo1'}), + actionRole: iam.Role.import(app.stack, 'Role', { + roleArn: 'arn:aws:iam::123456789012:root' + }) + }); + + test.equals(action.runOrder, 123); + test.equals(action.actionRole!.roleArn, 'arn:aws:iam::123456789012:root'); + test.done(); } } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts b/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts index 8461eb85a6734..e984d8dc8e027 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts @@ -31,8 +31,7 @@ export interface PipelineDeployActionProps extends CommonPipelineDeployActionPro export class PipelineDeployAction extends codepipeline.DeployAction { constructor(scope: cdk.Construct, id: string, props: PipelineDeployActionProps) { super(scope, id, { - stage: props.stage, - runOrder: props.runOrder, + ...props, artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, provider: 'CodeDeploy', inputArtifact: props.inputArtifact, diff --git a/packages/@aws-cdk/aws-codedeploy/test/test.pipeline-action.ts b/packages/@aws-cdk/aws-codedeploy/test/test.pipeline-action.ts new file mode 100644 index 0000000000000..b38cc283e877b --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/test.pipeline-action.ts @@ -0,0 +1,46 @@ +import * as iam from '@aws-cdk/aws-iam'; +import { App, Stack } from '@aws-cdk/cdk'; +import { Test } from 'nodeunit'; +import { Pipeline } from '../../aws-codepipeline/lib/pipeline'; +import { Bucket, PipelineSourceAction } from '../../aws-s3/lib'; +import { PipelineDeployAction, ServerDeploymentGroup } from '../lib'; + +export = { + 'test passing properties to action'(test: Test) { + const app = new TestApp(); + const pipe = new Pipeline(app.stack, 'Pipe'); + const stage = pipe.addStage('MyStage1'); + + const source = new PipelineSourceAction(app.stack, 'Src', { + bucket: Bucket.import(app.stack, 'Bucket', {bucketName: 'someName'}), + bucketKey: 'bkkey', + stage + }); + + const action = new PipelineDeployAction(app.stack, 'Id', { + runOrder: 456, + stage, + actionRole: iam.Role.import(app.stack, 'Role', { + roleArn: 'arn:aws:iam::123456789012:role/superUser' + }), + deploymentGroup: new ServerDeploymentGroup(app.stack, 'DeployGroup', { + deploymentGroupName: 'DGName' + }), + inputArtifact: source.outputArtifact + }); + + test.equals(action.runOrder, 456); + test.equals(action.actionRole!.roleArn, 'arn:aws:iam::123456789012:role/superUser'); + test.done(); + } +}; + +class TestApp { + private readonly app = new App(); + // tslint:disable-next-line:member-ordering + public readonly stack: Stack = new Stack(this.app, 'MyStack'); + + public synthesizeTemplate() { + return this.app.synthesizeStack(this.stack.name).template; + } +} diff --git a/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts index 81aa0cb18095b..0540c7e55fa51 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts +++ b/packages/@aws-cdk/aws-codepipeline-api/lib/action.ts @@ -136,6 +136,15 @@ export interface CommonActionProps { * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html */ runOrder?: number; + + /** + * The service role that is assumed during execution of action. + * This role is not mandatory, however more advanced configuration + * may require specifying it. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages-actions.html + */ + actionRole?: iam.IRole; } /** @@ -205,6 +214,15 @@ export abstract class Action extends cdk.Construct { */ public readonly configuration?: any; + /** + * The service role that is assumed during execution of action. + * This role is not mandatory, however more advanced configuration + * may require specifying it. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages-actions.html + */ + public readonly actionRole?: iam.IRole; + /** * The order in which AWS CodePipeline runs this action. * For more information, see the AWS CodePipeline User Guide. @@ -218,6 +236,7 @@ export abstract class Action extends cdk.Construct { private readonly _actionInputArtifacts = new Array(); private readonly _actionOutputArtifacts = new Array(); + private readonly artifactBounds: ActionArtifactBounds; private readonly stage: IStage; @@ -235,6 +254,7 @@ export abstract class Action extends cdk.Construct { this.artifactBounds = props.artifactBounds; this.runOrder = props.runOrder === undefined ? 1 : props.runOrder; this.stage = props.stage; + this.actionRole = props.actionRole; this.stage._internal._attachAction(this); } diff --git a/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts b/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts index 652b13e6bbcfa..0af4892e468fa 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/github-source-action.ts @@ -58,8 +58,7 @@ export interface GitHubSourceActionProps extends actions.CommonActionProps, export class GitHubSourceAction extends actions.SourceAction { constructor(scope: cdk.Construct, id: string, props: GitHubSourceActionProps) { super(scope, id, { - stage: props.stage, - runOrder: props.runOrder, + ...props, owner: 'ThirdParty', provider: 'GitHub', configuration: { diff --git a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts index ee51f5c5fa33b..bf4825fc0a259 100644 --- a/packages/@aws-cdk/aws-codepipeline/lib/stage.ts +++ b/packages/@aws-cdk/aws-codepipeline/lib/stage.ts @@ -162,6 +162,7 @@ export class Stage extends cdk.Construct implements cpapi.IStage, cpapi.IInterna configuration: action.configuration, outputArtifacts: action._outputArtifacts.map(a => ({ name: a.name })), runOrder: action.runOrder, + roleArn: action.actionRole ? action.actionRole.roleArn : undefined }; } diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json new file mode 100644 index 0000000000000..80f387afaf375 --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.expected.json @@ -0,0 +1,317 @@ +{ + "Resources": { + "MyBucketF68F3FF0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "VersioningConfiguration": { + "Status": "Enabled" + } + } + }, + "MyPipelineRoleC0D47CA4": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "codepipeline.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "MyPipelineRoleDefaultPolicy34F09EFA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + "s3:PutObject*", + "s3:Abort*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "MyBucketF68F3FF0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "CFNDeployRole68D5E8D3", + "Arn" + ] + } + }, + { + "Action": [ + "cloudformation:CreateStack", + "cloudformation:DescribeStack*", + "cloudformation:GetStackPolicy", + "cloudformation:GetTemplate*", + "cloudformation:SetStackPolicy", + "cloudformation:UpdateStack", + "cloudformation:ValidateTemplate" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":cloudformation:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":stack/aws-cdk-codepipeline-cross-region-deploy-stack/*" + ] + ] + } + }, + { + "Action": [ + "sts:AssumeRole", + "iam:PassRole" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "MyPipelineRoleDefaultPolicy34F09EFA", + "Roles": [ + { + "Ref": "MyPipelineRoleC0D47CA4" + } + ] + } + }, + "MyPipelineAED38ECF": { + "Type": "AWS::CodePipeline::Pipeline", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "MyPipelineRoleC0D47CA4", + "Arn" + ] + }, + "Stages": [ + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Source", + "Owner": "AWS", + "Provider": "S3", + "Version": "1" + }, + "Configuration": { + "S3Bucket": { + "Ref": "MyBucketF68F3FF0" + }, + "S3ObjectKey": "some/path", + "PollForSourceChanges": true + }, + "InputArtifacts": [], + "Name": "S3", + "OutputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinecloudformationcrossregionwithactionroleMyBucketS30423514B" + } + ], + "RunOrder": 1 + } + ], + "Name": "Source" + }, + { + "Actions": [ + { + "ActionTypeId": { + "Category": "Deploy", + "Owner": "AWS", + "Provider": "CloudFormation", + "Version": "1" + }, + "Configuration": { + "StackName": "aws-cdk-codepipeline-cross-region-deploy-stack", + "ActionMode": "CREATE_UPDATE", + "TemplatePath": "Artifact_awscdkcodepipelinecloudformationcrossregionwithactionroleMyBucketS30423514B::template.yml", + "RoleArn": { + "Fn::GetAtt": [ + "CFNDeployRole68D5E8D3", + "Arn" + ] + } + }, + "InputArtifacts": [ + { + "Name": "Artifact_awscdkcodepipelinecloudformationcrossregionwithactionroleMyBucketS30423514B" + } + ], + "Name": "CFN_Deploy", + "OutputArtifacts": [], + "RoleArn": { + "Fn::GetAtt": [ + "ActionRole60B0EDF7", + "Arn" + ] + }, + "RunOrder": 1 + } + ], + "Name": "CFN" + } + ], + "ArtifactStore": { + "Location": { + "Ref": "MyBucketF68F3FF0" + }, + "Type": "S3" + } + }, + "DependsOn": [ + "MyPipelineRoleC0D47CA4", + "MyPipelineRoleDefaultPolicy34F09EFA" + ] + }, + "ActionRole60B0EDF7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ActionRoleDefaultPolicyCA33BE56": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:*", + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ActionRoleDefaultPolicyCA33BE56", + "Roles": [ + { + "Ref": "ActionRole60B0EDF7" + } + ] + } + }, + "CFNDeployRole68D5E8D3": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "cloudformation.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.ts b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.ts new file mode 100644 index 0000000000000..0b9f7749ca60f --- /dev/null +++ b/packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn-wtih-action-role.ts @@ -0,0 +1,48 @@ +import cloudformation = require('@aws-cdk/aws-cloudformation'); +import iam = require('@aws-cdk/aws-iam'); +import s3 = require('@aws-cdk/aws-s3'); +import cdk = require('@aws-cdk/cdk'); +import codepipeline = require('../lib'); + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-cloudformation-cross-region-with-action-role', {}); + +const bucket = new s3.Bucket(stack, 'MyBucket', { + versioned: true, + removalPolicy: cdk.RemovalPolicy.Destroy, +}); + +const pipeline = new codepipeline.Pipeline(stack, 'MyPipeline', { + artifactBucket: bucket, +}); + +const sourceStage = pipeline.addStage('Source'); +const sourceAction = bucket.addToPipeline(sourceStage, 'S3', { + bucketKey: 'some/path', +}); + +const cfnStage = pipeline.addStage('CFN'); + +const role = new iam.Role(stack, 'ActionRole', { + assumedBy: new iam.AccountPrincipal(new cdk.AwsAccountId().toString()) +}); +role.addToPolicy(new iam.PolicyStatement() + .addAction('sqs:*') + .addAllResources() +); + +new cloudformation.PipelineCreateUpdateStackAction(stack, 'CFN_Deploy', { + stage: cfnStage, + stackName: 'aws-cdk-codepipeline-cross-region-deploy-stack', + templatePath: sourceAction.outputArtifact.atPath('template.yml'), + adminPermissions: false, + actionRole: role +}); + +pipeline.addToRolePolicy(new iam.PolicyStatement() + .addActions("sts:AssumeRole", "iam:PassRole") + .addAllResources() +); + +app.run(); diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts index a33581084b12a..8abe7f5f7a46b 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.cloudformation-pipeline-actions.ts @@ -62,7 +62,9 @@ export = { stage: prodStage, stackName, changeSetName, + runOrder: 321, role: changeSetExecRole, + deploymentRole: changeSetExecRole, templatePath: new ArtifactPath(buildAction.outputArtifact, 'template.yaml'), templateConfiguration: new ArtifactPath(buildAction.outputArtifact, 'templateConfig.json'), adminPermissions: false, @@ -169,7 +171,7 @@ export = { "InputArtifacts": [{"Name": "OutputYo"}], "Name": "BuildChangeSetProd", "OutputArtifacts": [], - "RunOrder": 1 + "RunOrder": 321 }, { "ActionTypeId": { @@ -356,6 +358,53 @@ export = { })); test.done(); + }, + + 'Action service role is passed to template'(test: Test) { + const stack = new TestFixture(); + + const importedRole = Role.import(stack, 'ImportedRole', { + roleArn: 'arn:aws:iam::000000000000:role/action-role' + }); + const freshRole = new Role(stack, 'FreshRole', { + assumedBy: new ServicePrincipal('magicservice') + }); + + new PipelineExecuteChangeSetAction(stack.pipeline, 'ImportedRoleAction', { + actionRole: importedRole, + changeSetName: 'magicSet', + stackName: 'magicStack', + stage: stack.deployStage + }); + + new PipelineExecuteChangeSetAction(stack.pipeline, 'FreshRoleAction', { + actionRole: freshRole, + changeSetName: 'magicSet', + stackName: 'magicStack', + stage: stack.deployStage + }); + + expect(stack).to(haveResourceLike('AWS::CodePipeline::Pipeline', { + "Stages": [{ + "Name": "Source" /* don't care about the rest */ + }, { + "Name": "Deploy", + "Actions": [{ + "Name": "ImportedRoleAction", + "RoleArn": "arn:aws:iam::000000000000:role/action-role" + }, { + "Name": "FreshRoleAction", + "RoleArn": { + "Fn::GetAtt": [ + "FreshRole472F6E18", + "Arn" + ] + } + }] + }] + })); + + test.done(); } }; diff --git a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts index 5a29a47764a2f..e5f31cac32275 100644 --- a/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts +++ b/packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts @@ -2,6 +2,7 @@ import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; import cloudformation = require('@aws-cdk/aws-cloudformation'); import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); +import iam = require( '@aws-cdk/aws-iam'); import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import sns = require('@aws-cdk/aws-sns'); @@ -49,6 +50,10 @@ export = { const p = new codepipeline.Pipeline(stack, 'P'); + const role = iam.Role.import(stack, 'PipelineActionRole', { + roleArn: 'arn:aws:iam::123456789012:root' + }); + const s1 = new codepipeline.Stage(stack, 'Source', { pipeline: p }); new codepipeline.GitHubSourceAction(stack, 'GH', { stage: s1, @@ -57,7 +62,8 @@ export = { branch: 'branch', oauthToken: secret.value, owner: 'foo', - repo: 'bar' + repo: 'bar', + actionRole: role }); const s2 = new codepipeline.Stage(stack, 'Two', { pipeline: p }); @@ -102,7 +108,8 @@ export = { "Name": "A" } ], - "RunOrder": 8 + "RunOrder": 8, + "RoleArn": "arn:aws:iam::123456789012:root" } ], "Name": "Source" @@ -288,11 +295,16 @@ export = { outputArtifactName: 'sourceArtifact2', }); + const role = iam.Role.import(stack, 'PipelineActionRole', { + roleArn: 'arn:aws:iam::123456789012:root' + }); + const stage = new codepipeline.Stage(stack, 'Stage', { pipeline }); const lambdaAction = new lambda.PipelineInvokeAction(stack, 'InvokeAction', { stage, lambda: lambdaFun, userParameters: 'foo-bar/42', + actionRole: role, inputArtifacts: [ source2.outputArtifact, source1.outputArtifact, @@ -346,6 +358,7 @@ export = { { "Name": "lambdaOutput2" }, { "Name": "lambdaOutput3" }, ], + "RoleArn": "arn:aws:iam::123456789012:root", "RunOrder": 1 } ], @@ -518,7 +531,7 @@ export = { test.done(); }, }, -}; +} as any; function stageForTesting(stack: cdk.Stack): codepipeline.Stage { const pipeline = new codepipeline.Pipeline(stack, 'pipeline'); diff --git a/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts b/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts index ac14ea505feb0..d8f7c21e7f5c6 100644 --- a/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts +++ b/packages/@aws-cdk/aws-lambda/lib/pipeline-action.ts @@ -84,6 +84,7 @@ export class PipelineInvokeAction extends codepipeline.Action { constructor(scope: cdk.Construct, id: string, props: PipelineInvokeActionProps) { super(scope, id, { stage: props.stage, + actionRole: props.actionRole, runOrder: props.runOrder, category: codepipeline.ActionCategory.Invoke, provider: 'Lambda',