Skip to content

Commit

Permalink
feat(pipelines): allow use of custom role for pipeline (#21299)
Browse files Browse the repository at this point in the history
fixes: #18167, fixes #21412

- adds a new role prop for `pipelines.CodePipeline` to pass on to the generated `codepipeline.Pipeline`
- This role will be assumed by the pipeline

----

### All Submissions:

* [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
peterwoodworth committed Aug 1, 2022
1 parent e82ba52 commit ff3c01a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/@aws-cdk/core/lib/cfn-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ export enum TagType {
export interface ICfnResourceOptions {
/**
* A condition to associate with this resource. This means that only if the condition evaluates to 'true' when the stack
* is deployed, the resource will be included. This is provided to allow CDK projects to produce legacy templates, but noramlly
* is deployed, the resource will be included. This is provided to allow CDK projects to produce legacy templates, but normally
* there is no need to use it in CDK projects.
*/
condition?: CfnCondition;
Expand Down
28 changes: 28 additions & 0 deletions packages/@aws-cdk/pipelines/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,34 @@ class MyJenkinsStep extends pipelines.Step implements pipelines.ICodePipelineAct
}
```

### Using an existing AWS Codepipeline

If you wish to use an existing `CodePipeline.Pipeline` while using the modern API's
methods and classes, you can pass in the existing `CodePipeline.Pipeline` to be built upon
instead of having the `pipelines.CodePipeline` construct create a new `CodePipeline.Pipeline`.
This also gives you more direct control over the underlying `CodePipeline.Pipeline` construct
if the way the modern API creates it doesn't allow for desired configurations.

Here's an example of passing in an existing pipeline:

```ts
declare const codePipeline: codepipeline.Pipeline;

const pipeline = new pipelines.CodePipeline(this, 'Pipeline', {
synth: new pipelines.ShellStep('Synth', {
input: pipelines.CodePipelineSource.connection('my-org/my-app', 'main', {
connectionArn: 'arn:aws:codestar-connections:us-east-1:222222222222:connection/7d2469ff-514a-4e4f-9003-5ca4a43cdc41', // Created using the AWS console * });',
}),
commands: ['npm ci','npm run build','npx cdk synth'],
}),
codePipeline: codePipeline,
});
```

Note that if you provide an existing pipeline, you cannot provide values for
`pipelineName`, `crossAccountKeys`, `reuseCrossRegionSupportStacks`, or `role`
because those values are passed in directly to the underlying `codepipeline.Pipeline`.

## Using Docker in the pipeline

Docker can be used in 3 different places in the pipeline:
Expand Down
11 changes: 11 additions & 0 deletions packages/@aws-cdk/pipelines/lib/codepipeline/codepipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ export interface CodePipelineProps {
* @default - true (Use the same support stack for all pipelines in App)
*/
readonly reuseCrossRegionSupportStacks?: boolean;

/**
* The IAM role to be assumed by this Pipeline
*
* @default - A new role is created
*/
readonly role?: iam.IRole;
}

/**
Expand Down Expand Up @@ -362,6 +369,9 @@ export class CodePipeline extends PipelineBase {
if (this.props.reuseCrossRegionSupportStacks !== undefined) {
throw new Error('Cannot set \'reuseCrossRegionSupportStacks\' if an existing CodePipeline is given using \'codePipeline\'');
}
if (this.props.role !== undefined) {
throw new Error('Cannot set \'role\' if an existing CodePipeline is given using \'codePipeline\'');
}

this._pipeline = this.props.codePipeline;
} else {
Expand All @@ -372,6 +382,7 @@ export class CodePipeline extends PipelineBase {
// This is necessary to make self-mutation work (deployments are guaranteed
// to happen only after the builds of the latest pipeline definition).
restartExecutionOnUpdate: true,
role: this.props.role,
});
}

Expand Down
65 changes: 65 additions & 0 deletions packages/@aws-cdk/pipelines/test/codepipeline/codepipeline.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Template, Annotations, Match } from '@aws-cdk/assertions';
import * as ccommit from '@aws-cdk/aws-codecommit';
import { Pipeline } from '@aws-cdk/aws-codepipeline';
import * as iam from '@aws-cdk/aws-iam';
import * as sqs from '@aws-cdk/aws-sqs';
import * as cdk from '@aws-cdk/core';
import { Stack } from '@aws-cdk/core';
import { Construct } from 'constructs';
import * as cdkp from '../../lib';
import { CodePipeline } from '../../lib';
import { PIPELINE_ENV, TestApp, ModernTestGitHubNpmPipeline, FileAssetApp } from '../testhelpers';

let app: TestApp;
Expand Down Expand Up @@ -88,6 +91,15 @@ describe('Providing codePipeline parameter and prop(s) of codePipeline parameter
reuseCrossRegionSupportStacks: true,
}).create()).toThrowError('Cannot set \'reuseCrossRegionSupportStacks\' if an existing CodePipeline is given using \'codePipeline\'');
});
test('Providing codePipeline parameter and role parameter should throw error', () => {
const stack = new Stack(app, 'Stack');

expect(() => new CodePipelinePropsCheckTest(stack, 'CodePipeline', {
role: new iam.Role(stack, 'Role', {
assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com'),
}),
}).create()).toThrowError('Cannot set \'role\' if an existing CodePipeline is given using \'codePipeline\'');
});
});

test('Policy sizes do not exceed the maximum size', () => {
Expand Down Expand Up @@ -180,6 +192,51 @@ test('CodeBuild action role has the right AssumeRolePolicyDocument', () => {
});
});

test('CodePipeline supports use of existing role', () => {
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
const repo = new ccommit.Repository(pipelineStack, 'Repo', {
repositoryName: 'MyRepo',
});
const cdkInput = cdkp.CodePipelineSource.codeCommit(
repo,
'main',
);

new CodePipeline(pipelineStack, 'Pipeline', {
synth: new cdkp.ShellStep('Synth', {
input: cdkInput,
installCommands: ['npm ci'],
commands: [
'npm run build',
'npx cdk synth',
],
}),
role: new iam.Role(pipelineStack, 'CustomRole', {
assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
roleName: 'MyCustomPipelineRole',
}),
});

const template = Template.fromStack(pipelineStack);
template.hasResourceProperties('AWS::IAM::Role', {
AssumeRolePolicyDocument: {
Statement: [
{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'codepipeline.amazonaws.com',
},
},
],
},
RoleName: 'MyCustomPipelineRole',
});
template.hasResourceProperties('AWS::CodePipeline::Pipeline', {
RoleArn: { 'Fn::GetAtt': ['CustomRole6D8E6809', 'Arn'] },
});
});

interface ReuseCodePipelineStackProps extends cdk.StackProps {
reuseCrossRegionSupportStacks?: boolean;
}
Expand Down Expand Up @@ -241,6 +298,7 @@ interface CodePipelineStackProps extends cdk.StackProps {
pipelineName?: string;
crossAccountKeys?: boolean;
reuseCrossRegionSupportStacks?: boolean;
role?: iam.IRole;
}

class CodePipelinePropsCheckTest extends cdk.Stack {
Expand Down Expand Up @@ -271,5 +329,12 @@ class CodePipelinePropsCheckTest extends cdk.Stack {
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
}).buildPipeline();
}
if (this.cProps.role !== undefined) {
new cdkp.CodePipeline(this, 'CodePipeline4', {
role: this.cProps.role,
codePipeline: new Pipeline(this, 'Pipline4'),
synth: new cdkp.ShellStep('Synth', { commands: ['ls'] }),
}).buildPipeline();
}
}
}

0 comments on commit ff3c01a

Please sign in to comment.