Skip to content

Commit

Permalink
feat(cli): notify option in deploy command to specify SNS Notificatio…
Browse files Browse the repository at this point in the history
…n ARNs (#4420)

* feat(cli): notify option in deploy command to specify SNS Notification ARNs

Adds an option `notify` that can take multiple (up to 5) SNS notification ARNs that
will receive CloudFormation stack events.

Fixes #2528

* moving the notify option under the deploy command

* notify is now notification-arns to align with API/CLI
  • Loading branch information
shivlaks authored and mergify[bot] committed Oct 10, 2019
1 parent 9740ed3 commit 7d6b474
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 3 deletions.
2 changes: 2 additions & 0 deletions packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ async function parseCommandLineArguments() {
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependencies' })
.option('require-approval', { type: 'string', choices: [RequireApproval.Never, RequireApproval.AnyChange, RequireApproval.Broadening], desc: 'What security-sensitive changes need manual approval' })
.option('ci', { type: 'boolean', desc: 'Force CI detection. Use --no-ci to disable CI autodetection.', default: process.env.CI !== undefined })
.option('notification-arns', {type: 'array', desc: 'ARNs of SNS topics that CloudFormation will notify with stack related events', nargs: 1, requiresArg: true})
.option('tags', { type: 'array', alias: 't', desc: 'Tags to add to the stack (KEY=VALUE)', nargs: 1, requiresArg: true }))
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only deploy requested stacks, don\'t include dependees' })
Expand Down Expand Up @@ -198,6 +199,7 @@ async function initCommandLine() {
exclusively: args.exclusively,
toolkitStackName,
roleArn: args.roleArn,
notificationArns: args.notificationArns,
requireApproval: configuration.settings.get(['requireApproval']),
ci: args.ci,
reuseAssets: args['build-exclude'],
Expand Down
2 changes: 2 additions & 0 deletions packages/aws-cdk/lib/api/deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface DeployStackOptions {
sdk: ISDK;
toolkitInfo?: ToolkitInfo;
roleArn?: string;
notificationArns?: string[];
deployName?: string;
quiet?: boolean;
ci?: boolean;
Expand Down Expand Up @@ -78,6 +79,7 @@ export async function deployStack(options: DeployStackOptions): Promise<DeploySt
TemplateURL: bodyParameter.TemplateURL,
Parameters: params,
RoleARN: options.roleArn,
NotificationARNs: options.notificationArns,
Capabilities: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND' ],
Tags: options.tags
}).promise();
Expand Down
2 changes: 2 additions & 0 deletions packages/aws-cdk/lib/api/deployment-target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface IDeploymentTarget {
export interface DeployStackOptions {
stack: CloudFormationStackArtifact;
roleArn?: string;
notificationArns?: string[];
deployName?: string;
quiet?: boolean;
ci?: boolean;
Expand Down Expand Up @@ -68,6 +69,7 @@ export class CloudFormationDeploymentTarget implements IDeploymentTarget {
stack: options.stack,
deployName: options.deployName,
roleArn: options.roleArn,
notificationArns: options.notificationArns,
quiet: options.quiet,
sdk: this.aws,
ci: options.ci,
Expand Down
8 changes: 7 additions & 1 deletion packages/aws-cdk/lib/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export class CdkToolkit {
ci: options.ci,
toolkitStackName: options.toolkitStackName,
reuseAssets: options.reuseAssets,
notificationArns: options.notificationArns,
tags
});

Expand Down Expand Up @@ -273,6 +274,11 @@ export interface DeployOptions {
*/
roleArn?: string;

/**
* ARNs of SNS topics that CloudFormation will notify with stack related events
*/
notificationArns?: string[];

/**
* What kind of security changes require approval
*
Expand All @@ -281,7 +287,7 @@ export interface DeployOptions {
requireApproval?: RequireApproval;

/**
* Wheter we're in CI mode
* Whether we're in CI mode
*
* @default false
*/
Expand Down
31 changes: 29 additions & 2 deletions packages/aws-cdk/test/test.cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ export = nodeunit.testCase({
// WHEN
toolkit.deploy({ stackNames: ['Test-Stack-A', 'Test-Stack-B'], sdk: new SDK() });

// THEN
test.done();
},
'with sns notification arns'(test: nodeunit.Test) {
// GIVEN
const notificationArns = ['arn:aws:sns:::cfn-notifications', 'arn:aws:sns:::my-cool-topic'];
const toolkit = new CdkToolkit({
appStacks: new TestAppStacks(test),
provisioner: new TestProvisioner(test, {
'Test-Stack-A': { Foo: 'Bar' },
'Test-Stack-B': { Baz: 'Zinga!' },
}, notificationArns),
});

// WHEN
toolkit.deploy({
stackNames: ['Test-Stack-A', 'Test-Stack-B'],
notificationArns,
sdk: new SDK()
});

// THEN
test.done();
},
Expand Down Expand Up @@ -89,17 +110,22 @@ class TestAppStacks extends AppStacks {
}

class TestProvisioner implements IDeploymentTarget {
private readonly expectedTags: { [sytackName: string]: Tag[] } = {};
private readonly expectedTags: { [stackName: string]: Tag[] } = {};
private readonly expectedNotificationArns: string[];

constructor(
private readonly test: nodeunit.Test,
expectedTags: { [sytackName: string]: { [kay: string]: string } } = {},
expectedTags: { [stackName: string]: { [key: string]: string } } = {},
expectedNotificationArns?: string[],
) {
for (const [stackName, tags] of Object.entries(expectedTags)) {
this.expectedTags[stackName] =
Object.entries(tags).map(([Key, Value]) => ({ Key, Value }))
.sort((l, r) => l.Key.localeCompare(r.Key));
}
if (expectedNotificationArns) {
this.expectedNotificationArns = expectedNotificationArns;
}
}

public deployStack(options: DeployStackOptions): Promise<DeployStackResult> {
Expand All @@ -108,6 +134,7 @@ class TestProvisioner implements IDeploymentTarget {
`Not an expected mock stack: ${options.stack.name}`
);
this.test.deepEqual(options.tags, this.expectedTags[options.stack.name]);
this.test.deepEqual(options.notificationArns, this.expectedNotificationArns);
return Promise.resolve({
stackArn: `arn:aws:cloudformation:::stack/${options.stack.name}/MockedOut`,
noOp: false,
Expand Down

0 comments on commit 7d6b474

Please sign in to comment.