Skip to content

Commit 39f1636

Browse files
authored
feat(cdk-pipeline): add url and sns topic for manual approval in cdk pipeline (re-create PR since original is auto-closed) (#34654)
### Issue #32809 Closes #12273 Original PR with approved changes: #34227 ### Reason for this change No concrete solution to output cdk diff in pipeline yet. Include ExternalEntityLink and SNS Topic to manual approval step for custom diff check solution. ### Description of changes Add url for review and SNS topic in ManualApprovalStepProps ### Description of how you validated changes Unit tested ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 993d2fd commit 39f1636

File tree

4 files changed

+112
-2
lines changed

4 files changed

+112
-2
lines changed

packages/aws-cdk-lib/pipelines/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,7 @@ pass in order to promote from the `PreProd` to the `Prod` environment:
565565
declare const pipeline: pipelines.CodePipeline;
566566
const preprod = new MyApplicationStage(this, 'PreProd');
567567
const prod = new MyApplicationStage(this, 'Prod');
568+
const topic = new sns.Topic(this, 'ChangeApprovalTopic');
568569

569570
pipeline.addStage(preprod, {
570571
post: [
@@ -574,7 +575,12 @@ pipeline.addStage(preprod, {
574575
],
575576
});
576577
pipeline.addStage(prod, {
577-
pre: [new pipelines.ManualApprovalStep('PromoteToProd')],
578+
pre: [new pipelines.ManualApprovalStep('PromoteToProd', {
579+
//All options below are optional
580+
comment: 'Please validate changes',
581+
reviewUrl: 'https://my.webservice.com/',
582+
notificationTopic: topic,
583+
})],
578584
});
579585
```
580586

packages/aws-cdk-lib/pipelines/lib/blueprint/manual-approval.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Step } from './step';
2-
2+
import { ITopic } from '../../../aws-sns';
33
/**
44
* Construction properties for a `ManualApprovalStep`
55
*/
@@ -10,6 +10,20 @@ export interface ManualApprovalStepProps {
1010
* @default - No comment
1111
*/
1212
readonly comment?: string;
13+
14+
/**
15+
* The URL for review associated with this manual approval
16+
*
17+
* @default - No URL
18+
*/
19+
readonly reviewUrl?: string;
20+
21+
/**
22+
* Optional SNS topic to send notifications to when an approval is pending
23+
*
24+
* @default - No notifications
25+
*/
26+
readonly notificationTopic?: ITopic;
1327
}
1428

1529
/**
@@ -29,10 +43,26 @@ export class ManualApprovalStep extends Step {
2943
*/
3044
public readonly comment?: string;
3145

46+
/**
47+
* The URL for review associated with this manual approval
48+
*
49+
* @default - No URL
50+
*/
51+
public readonly reviewUrl?: string;
52+
53+
/**
54+
* Optional SNS topic to send notifications
55+
*
56+
* @default - No notifications
57+
*/
58+
public readonly notificationTopic?: ITopic;
59+
3260
constructor(id: string, props: ManualApprovalStepProps = {}) {
3361
super(id);
3462

3563
this.comment = props.comment;
64+
this.reviewUrl = props.reviewUrl;
65+
this.notificationTopic = props.notificationTopic;
3666

3767
this.discoverReferencedOutputs(props.comment);
3868
}

packages/aws-cdk-lib/pipelines/lib/codepipeline/codepipeline.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,8 @@ export class CodePipeline extends PipelineBase {
714714
actionName: options.actionName,
715715
runOrder: options.runOrder,
716716
additionalInformation: step.comment,
717+
externalEntityLink: step.reviewUrl,
718+
notificationTopic: step.notificationTopic,
717719
}));
718720
return { runOrdersConsumed: 1 };
719721
},

packages/aws-cdk-lib/pipelines/test/codepipeline/codepipeline.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as ccommit from '../../../aws-codecommit';
44
import { Pipeline, PipelineType } from '../../../aws-codepipeline';
55
import * as iam from '../../../aws-iam';
66
import * as s3 from '../../../aws-s3';
7+
import * as sns from '../../../aws-sns';
78
import * as sqs from '../../../aws-sqs';
89
import * as cdk from '../../../core';
910
import { Stack } from '../../../core';
@@ -731,6 +732,77 @@ test('throws when deploy role session tags are used', () => {
731732
}).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.');
732733
});
733734

735+
test('test add external link for manual approval', () => {
736+
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
737+
738+
const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', {
739+
crossAccountKeys: true,
740+
});
741+
742+
const stage = new TwoStackApp(app, 'TheApp', { withDependency: false });
743+
744+
const approval = new cdkp.ManualApprovalStep('Approval', {
745+
comment: 'Please approve',
746+
reviewUrl: 'https://approve-confirm.com',
747+
});
748+
749+
pipeline.addStage(stage, {
750+
pre: [approval],
751+
});
752+
753+
const template = Template.fromStack(pipelineStack);
754+
template.hasResourceProperties('AWS::CodePipeline::Pipeline', {
755+
Stages: Match.arrayWith([{
756+
Name: 'TheApp',
757+
Actions: Match.arrayWith([
758+
Match.objectLike({
759+
Name: 'Approval',
760+
RunOrder: 1,
761+
Configuration: Match.objectLike({
762+
ExternalEntityLink: 'https://approve-confirm.com',
763+
}),
764+
}),
765+
]),
766+
}]),
767+
});
768+
});
769+
770+
test('sns topic for manual approval', () => {
771+
const pipelineStack = new cdk.Stack(app, 'PipelineStack', { env: PIPELINE_ENV });
772+
const pipeline = new ModernTestGitHubNpmPipeline(pipelineStack, 'Cdk', {
773+
crossAccountKeys: true,
774+
});
775+
const topic = new sns.Topic(pipelineStack, 'Topic', {
776+
topicName: 'MyTopic',
777+
});
778+
const approval = new cdkp.ManualApprovalStep('Approval', {
779+
comment: 'Please approve',
780+
notificationTopic: topic,
781+
});
782+
const stage = new TwoStackApp(app, 'TheApp', { withDependency: false });
783+
pipeline.addStage(stage, {
784+
pre: [approval],
785+
});
786+
const template = Template.fromStack(pipelineStack);
787+
template.hasResourceProperties('AWS::CodePipeline::Pipeline', {
788+
Stages: Match.arrayWith([{
789+
Name: 'TheApp',
790+
Actions: Match.arrayWith([
791+
Match.objectLike({
792+
Name: 'Approval',
793+
RunOrder: 1,
794+
Configuration: Match.objectLike({
795+
NotificationArn: { Ref: 'TopicBFC7AF6E' },
796+
}),
797+
}),
798+
]),
799+
}]),
800+
});
801+
template.hasResourceProperties('AWS::SNS::Topic', {
802+
TopicName: 'MyTopic',
803+
});
804+
});
805+
734806
interface ReuseCodePipelineStackProps extends cdk.StackProps {
735807
reuseCrossRegionSupportStacks?: boolean;
736808
reuseStageProps?: ReuseStageProps;

0 commit comments

Comments
 (0)