Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/aws-cdk-lib/pipelines/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ pass in order to promote from the `PreProd` to the `Prod` environment:
declare const pipeline: pipelines.CodePipeline;
const preprod = new MyApplicationStage(this, 'PreProd');
const prod = new MyApplicationStage(this, 'Prod');
const topic = new sns.Topic(this, 'ChangeApprovalTopic');

pipeline.addStage(preprod, {
post: [
Expand All @@ -574,7 +575,12 @@ pipeline.addStage(preprod, {
],
});
pipeline.addStage(prod, {
pre: [new pipelines.ManualApprovalStep('PromoteToProd')],
pre: [new pipelines.ManualApprovalStep('PromoteToProd', {
//All options below are optional
comment: 'Please validate changes',
reviewUrl: 'https://my.webservice.com/',
notificationTopic: topic,
})],
});
```

Expand Down
32 changes: 31 additions & 1 deletion packages/aws-cdk-lib/pipelines/lib/blueprint/manual-approval.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Step } from './step';

import { ITopic } from '../../../aws-sns';
/**
* Construction properties for a `ManualApprovalStep`
*/
Expand All @@ -10,6 +10,20 @@ export interface ManualApprovalStepProps {
* @default - No comment
*/
readonly comment?: string;

/**
* The URL for review associated with this manual approval
*
* @default - No URL
*/
readonly reviewUrl?: string;

/**
* Optional SNS topic to send notifications to when an approval is pending
*
* @default - No notifications
*/
readonly notificationTopic?: ITopic;
}

/**
Expand All @@ -29,10 +43,26 @@ export class ManualApprovalStep extends Step {
*/
public readonly comment?: string;

/**
* The URL for review associated with this manual approval
*
* @default - No URL
*/
public readonly reviewUrl?: string;

/**
* Optional SNS topic to send notifications
*
* @default - No notifications
*/
public readonly notificationTopic?: ITopic;

constructor(id: string, props: ManualApprovalStepProps = {}) {
super(id);

this.comment = props.comment;
this.reviewUrl = props.reviewUrl;
this.notificationTopic = props.notificationTopic;

this.discoverReferencedOutputs(props.comment);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,8 @@ export class CodePipeline extends PipelineBase {
actionName: options.actionName,
runOrder: options.runOrder,
additionalInformation: step.comment,
externalEntityLink: step.reviewUrl,
notificationTopic: step.notificationTopic,
}));
return { runOrdersConsumed: 1 };
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as ccommit from '../../../aws-codecommit';
import { Pipeline, PipelineType } from '../../../aws-codepipeline';
import * as iam from '../../../aws-iam';
import * as s3 from '../../../aws-s3';
import * as sns from '../../../aws-sns';
import * as sqs from '../../../aws-sqs';
import * as cdk from '../../../core';
import { Stack } from '../../../core';
Expand Down Expand Up @@ -705,6 +706,77 @@ test('throws when deploy role session tags are used', () => {
}).toThrow('Deployment of stack SampleStage-123456789012-us-east-1-SampleStack requires assuming the role arn:${AWS::Partition}:iam::123456789012:role/cdk-hnb659fds-deploy-role-123456789012-us-east-1 with session tags, but assuming roles with session tags is not supported by CodePipeline.');
});

test('test add external link for manual approval', () => {
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });

const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', {
crossAccountKeys: true,
});

const stage = new TwoStackApp(app, 'TheApp', { withDependency: false });

const approval = new cdkp.ManualApprovalStep('Approval', {
comment: 'Please approve',
reviewUrl: 'https://approve-confirm.com',
});

pipeline.addStage(stage, {
pre: [approval],
});

const template = Template.fromStack(pipelineStack);
template.hasResourceProperties('AWS::CodePipeline::Pipeline', {
Stages: Match.arrayWith([{
Name: 'TheApp',
Actions: Match.arrayWith([
Match.objectLike({
Name: 'Approval',
RunOrder: 1,
Configuration: Match.objectLike({
ExternalEntityLink: 'https://approve-confirm.com',
}),
}),
]),
}]),
});
});

test('sns topic for manual approval', () => {
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', {
crossAccountKeys: true,
});
const topic = new sns.Topic(pipelineStack, 'Topic', {
topicName: 'MyTopic',
});
const approval = new cdkp.ManualApprovalStep('Approval', {
comment: 'Please approve',
notificationTopic: topic,
});
const stage = new TwoStackApp(app, 'TheApp', { withDependency: false });
pipeline.addStage(stage, {
pre: [approval],
});
const template = Template.fromStack(pipelineStack);
template.hasResourceProperties('AWS::CodePipeline::Pipeline', {
Stages: Match.arrayWith([{
Name: 'TheApp',
Actions: Match.arrayWith([
Match.objectLike({
Name: 'Approval',
RunOrder: 1,
Configuration: Match.objectLike({
NotificationArn: { Ref: 'TopicBFC7AF6E' },
}),
}),
]),
}]),
});
template.hasResourceProperties('AWS::SNS::Topic', {
TopicName: 'MyTopic',
});
});

interface ReuseCodePipelineStackProps extends cdk.StackProps {
reuseCrossRegionSupportStacks?: boolean;
reuseStageProps?: ReuseStageProps;
Expand Down
Loading