@@ -6,7 +6,7 @@ import nodeunit = require('nodeunit');
66import cloudformation = require('../lib');
77
88export = nodeunit.testCase({
9- CreateReplaceChangeSet: {
9+ ' CreateReplaceChangeSet' : {
1010 works(test: nodeunit.Test) {
1111 const stack = new cdk.Stack();
1212 const pipelineRole = new RoleDouble(stack, 'PipelineRole');
@@ -21,11 +21,7 @@ export = nodeunit.testCase({
2121
2222 _assertPermissionGranted(test, pipelineRole.statements, 'iam:PassRole', action.role.roleArn);
2323
24- const stackArn = cdk.ArnUtils.fromComponents({
25- service: 'cloudformation',
26- resource: 'stack',
27- resourceName: 'MyStack/*'
28- });
24+ const stackArn = _stackArn('MyStack');
2925 const changeSetCondition = { StringEquals: { 'cloudformation:ChangeSetName': 'MyChangeSet' } };
3026 _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DescribeStacks', stackArn);
3127 _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DescribeChangeSet', stackArn, changeSetCondition);
@@ -44,7 +40,7 @@ export = nodeunit.testCase({
4440 test.done();
4541 }
4642 },
47- ExecuteChangeSet: {
43+ ' ExecuteChangeSet' : {
4844 works(test: nodeunit.Test) {
4945 const stack = new cdk.Stack();
5046 const pipelineRole = new RoleDouble(stack, 'PipelineRole');
@@ -55,11 +51,7 @@ export = nodeunit.testCase({
5551 stackName: 'MyStack',
5652 });
5753
58- const stackArn = cdk.ArnUtils.fromComponents({
59- service: 'cloudformation',
60- resource: 'stack',
61- resourceName: 'MyStack/*'
62- });
54+ const stackArn = _stackArn('MyStack');
6355 _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:ExecuteChangeSet', stackArn,
6456 { StringEquals: { 'cloudformation:ChangeSetName': 'MyChangeSet' } });
6557
@@ -71,7 +63,44 @@ export = nodeunit.testCase({
7163
7264 test.done();
7365 }
74- }
66+ },
67+
68+ 'the CreateUpdateStack Action sets the DescribeStack*, Create/Update/DeleteStack & PassRole permissions'(test: nodeunit.Test) {
69+ const stack = new cdk.Stack();
70+ const pipelineRole = new RoleDouble(stack, 'PipelineRole');
71+ const action = new cloudformation.PipelineCreateUpdateStackAction(stack, 'Action', {
72+ stage: new StageDouble({ pipelineRole }),
73+ templatePath: new cpapi.Artifact(stack as any, 'TestArtifact').atPath('some/file'),
74+ stackName: 'MyStack',
75+ });
76+ const stackArn = _stackArn('MyStack');
77+
78+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DescribeStack*', stackArn);
79+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:CreateStack', stackArn);
80+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:UpdateStack', stackArn);
81+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DeleteStack', stackArn);
82+
83+ _assertPermissionGranted(test, pipelineRole.statements, 'iam:PassRole', action.role.roleArn);
84+
85+ test.done();
86+ },
87+
88+ 'the DeleteStack Action sets the DescribeStack*, DeleteStack & PassRole permissions'(test: nodeunit.Test) {
89+ const stack = new cdk.Stack();
90+ const pipelineRole = new RoleDouble(stack, 'PipelineRole');
91+ const action = new cloudformation.PipelineDeleteStackAction(stack, 'Action', {
92+ stage: new StageDouble({ pipelineRole }),
93+ stackName: 'MyStack',
94+ });
95+ const stackArn = _stackArn('MyStack');
96+
97+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DescribeStack*', stackArn);
98+ _assertPermissionGranted(test, pipelineRole.statements, 'cloudformation:DeleteStack', stackArn);
99+
100+ _assertPermissionGranted(test, pipelineRole.statements, 'iam:PassRole', action.role.roleArn);
101+
102+ test.done();
103+ },
75104});
76105
77106interface PolicyStatementJson {
@@ -121,7 +150,7 @@ function _assertPermissionGranted(test: nodeunit.Test, statements: PolicyStateme
121150 : '';
122151 const statementsStr = JSON.stringify(cdk.resolve(statements), null, 2);
123152 test.ok(_grantsPermission(statements, action, resource, conditions),
124- `Expected to find a statement granting ${action} on ${cdk.resolve(resource)}${conditionStr}, found:\n${statementsStr}`);
153+ `Expected to find a statement granting ${action} on ${JSON.stringify( cdk.resolve(resource) )}${conditionStr}, found:\n${statementsStr}`);
125154}
126155
127156function _grantsPermission(statements: PolicyStatementJson[], action: string, resource: string, conditions?: any) {
@@ -145,6 +174,14 @@ function _isOrContains(entity: string | string[], value: string): boolean {
145174 return false;
146175}
147176
177+ function _stackArn(stackName: string): string {
178+ return cdk.ArnUtils.fromComponents({
179+ service: 'cloudformation',
180+ resource: 'stack',
181+ resourceName: `${stackName}/*`,
182+ });
183+ }
184+
148185class StageDouble implements cpapi.IStage, cpapi.IInternalStage {
149186 public readonly name: string;
150187 public readonly pipelineArn: string;
0 commit comments