Skip to content

Commit 8d23f24

Browse files
fix(custom-resources): deleting custom resource fails when using two or more (#10012)
---- Fixes #9840 *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 768d127 commit 8d23f24

File tree

6 files changed

+144
-83
lines changed

6 files changed

+144
-83
lines changed

packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json

+9-11
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@
7878
"InstallLatestAwsSdk": true
7979
},
8080
"UpdateReplacePolicy": "Delete",
81-
"DeletionPolicy": "Delete"
81+
"DeletionPolicy": "Delete",
82+
"DependsOn": [
83+
"UserPoolDomainCloudFrontDomainNameCustomResourcePolicy7DE54188"
84+
]
8285
},
8386
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": {
8487
"Type": "AWS::IAM::Role",
@@ -111,25 +114,21 @@
111114
]
112115
}
113116
},
114-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": {
117+
"UserPoolDomainCloudFrontDomainNameCustomResourcePolicy7DE54188": {
115118
"Type": "AWS::IAM::Policy",
116119
"Properties": {
117120
"PolicyDocument": {
118121
"Statement": [
119122
{
120-
"Action": "cognito-idp:DescribeUserPoolDomain",
121-
"Effect": "Allow",
123+
"Action":"cognito-idp:DescribeUserPoolDomain",
124+
"Effect":"Allow",
122125
"Resource": "*"
123126
}
124127
],
125128
"Version": "2012-10-17"
126129
},
127-
"PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
128-
"Roles": [
129-
{
130-
"Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
131-
}
132-
]
130+
"PolicyName": "UserPoolDomainCloudFrontDomainNameCustomResourcePolicy7DE54188",
131+
"Roles": [{"Ref":"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"}]
133132
}
134133
},
135134
"AWS679f53fac002430cb0da5b7982bd22872D164C4C": {
@@ -184,7 +183,6 @@
184183
"Timeout": 120
185184
},
186185
"DependsOn": [
187-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
188186
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
189187
]
190188
}

packages/@aws-cdk/aws-ecr/test/integ.imagescan.expected.json

+26-28
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,32 @@
7777
"InstallLatestAwsSdk": true
7878
},
7979
"UpdateReplacePolicy": "Delete",
80-
"DeletionPolicy": "Delete"
80+
"DeletionPolicy": "Delete",
81+
"DependsOn": [
82+
"RepoImageScanOnPushCustomResourcePolicy556E941E"
83+
]
84+
},
85+
"RepoImageScanOnPushCustomResourcePolicy556E941E": {
86+
"Type": "AWS::IAM::Policy",
87+
"Properties": {
88+
"PolicyDocument": {
89+
"Statement": [
90+
{
91+
"Action":"ecr:PutImageScanningConfiguration",
92+
"Effect":"Allow",
93+
"Resource": {
94+
"Fn::GetAtt": [
95+
"Repo02AC86CF",
96+
"Arn"
97+
]
98+
}
99+
}
100+
],
101+
"Version": "2012-10-17"
102+
},
103+
"PolicyName": "RepoImageScanOnPushCustomResourcePolicy556E941E",
104+
"Roles": [{"Ref":"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"}]
105+
}
81106
},
82107
"RepoImageScanComplete7BC71935": {
83108
"Type": "AWS::Events::Rule",
@@ -134,32 +159,6 @@
134159
]
135160
}
136161
},
137-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": {
138-
"Type": "AWS::IAM::Policy",
139-
"Properties": {
140-
"PolicyDocument": {
141-
"Statement": [
142-
{
143-
"Action": "ecr:PutImageScanningConfiguration",
144-
"Effect": "Allow",
145-
"Resource": {
146-
"Fn::GetAtt": [
147-
"Repo02AC86CF",
148-
"Arn"
149-
]
150-
}
151-
}
152-
],
153-
"Version": "2012-10-17"
154-
},
155-
"PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
156-
"Roles": [
157-
{
158-
"Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
159-
}
160-
]
161-
}
162-
},
163162
"AWS679f53fac002430cb0da5b7982bd22872D164C4C": {
164163
"Type": "AWS::Lambda::Function",
165164
"Properties": {
@@ -212,7 +211,6 @@
212211
"Timeout": 120
213212
},
214213
"DependsOn": [
215-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
216214
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
217215
]
218216
}

packages/@aws-cdk/aws-globalaccelerator/test/globalaccelerator-security-group.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ test('custom resource exists', () => {
5858
InstallLatestAwsSdk: true,
5959
},
6060
DependsOn: [
61+
'GlobalAcceleratorSGCustomResourceCustomResourcePolicyF3294553',
6162
'GroupC77FDACD',
6263
],
6364
}, ResourcePart.CompleteDefinition));

packages/@aws-cdk/custom-resources/lib/aws-custom-resource/aws-custom-resource.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -324,24 +324,31 @@ export class AwsCustomResource extends cdk.Construct implements iam.IGrantable {
324324
});
325325
this.grantPrincipal = provider.grantPrincipal;
326326

327+
// Create the policy statements for the custom resource function role, or use the user-provided ones
328+
const statements = [];
327329
if (props.policy.statements.length !== 0) {
328330
// Use custom statements provided by the user
329331
for (const statement of props.policy.statements) {
330-
provider.addToRolePolicy(statement);
332+
statements.push(statement);
331333
}
332334
} else {
333335
// Derive statements from AWS SDK calls
334336
for (const call of [props.onCreate, props.onUpdate, props.onDelete]) {
335337
if (call) {
336-
provider.addToRolePolicy(new iam.PolicyStatement({
338+
const statement = new iam.PolicyStatement({
337339
actions: [awsSdkToIamAction(call.service, call.action)],
338340
resources: props.policy.resources,
339-
}));
341+
});
342+
statements.push(statement);
340343
}
341344
}
342-
343345
}
344-
346+
const policy = new iam.Policy(this, 'CustomResourcePolicy', {
347+
statements: statements,
348+
});
349+
if (provider.role !== undefined) {
350+
policy.attachToRole(provider.role);
351+
}
345352
const create = props.onCreate || props.onUpdate;
346353
this.customResource = new cdk.CustomResource(this, 'Resource', {
347354
resourceType: props.resourceType || 'Custom::AWS',
@@ -354,6 +361,10 @@ export class AwsCustomResource extends cdk.Construct implements iam.IGrantable {
354361
installLatestAwsSdk: props.installLatestAwsSdk ?? true,
355362
},
356363
});
364+
365+
// If the policy was deleted first, then the function might lose permissions to delete the custom resource
366+
// This is here so that the policy doesn't get removed before onDelete is called
367+
this.customResource.node.addDependency(policy);
357368
}
358369

359370
/**

packages/@aws-cdk/custom-resources/test/aws-custom-resource/aws-custom-resource.test.ts

+49-5
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,6 @@ test('implements IGrantable', () => {
286286
expect(stack).toHaveResource('AWS::IAM::Policy', {
287287
PolicyDocument: {
288288
Statement: [
289-
{
290-
Action: 'service:Action',
291-
Effect: 'Allow',
292-
Resource: '*',
293-
},
294289
{
295290
Action: 'iam:PassRole',
296291
Effect: 'Allow',
@@ -564,3 +559,52 @@ test('can specify function name', () => {
564559
FunctionName: 'my-cool-function',
565560
});
566561
});
562+
563+
test('separate policies per custom resource', () => {
564+
// GIVEN
565+
const stack = new cdk.Stack();
566+
567+
// WHEN
568+
new AwsCustomResource(stack, 'Custom1', {
569+
onCreate: {
570+
service: 'service1',
571+
action: 'action1',
572+
physicalResourceId: PhysicalResourceId.of('id1'),
573+
},
574+
policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
575+
});
576+
new AwsCustomResource(stack, 'Custom2', {
577+
onCreate: {
578+
service: 'service2',
579+
action: 'action2',
580+
physicalResourceId: PhysicalResourceId.of('id2'),
581+
},
582+
policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
583+
});
584+
585+
// THEN
586+
expect(stack).toHaveResource('AWS::IAM::Policy', {
587+
PolicyDocument: {
588+
Statement: [
589+
{
590+
Action: 'service1:Action1',
591+
Effect: 'Allow',
592+
Resource: '*',
593+
},
594+
],
595+
Version: '2012-10-17',
596+
},
597+
});
598+
expect(stack).toHaveResource('AWS::IAM::Policy', {
599+
PolicyDocument: {
600+
Statement: [
601+
{
602+
Action: 'service2:Action2',
603+
Effect: 'Allow',
604+
Resource: '*',
605+
},
606+
],
607+
Version: '2012-10-17',
608+
},
609+
});
610+
});

packages/@aws-cdk/custom-resources/test/aws-custom-resource/integ.aws-custom-resource.expected.json

+43-34
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
"InstallLatestAwsSdk": true
4646
},
4747
"UpdateReplacePolicy": "Delete",
48-
"DeletionPolicy": "Delete"
48+
"DeletionPolicy": "Delete",
49+
"DependsOn": [
50+
"PublishCustomResourcePolicyDF696FCA"
51+
]
4952
},
5053
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2": {
5154
"Type": "AWS::IAM::Role",
@@ -78,37 +81,6 @@
7881
]
7982
}
8083
},
81-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E": {
82-
"Type": "AWS::IAM::Policy",
83-
"Properties": {
84-
"PolicyDocument": {
85-
"Statement": [
86-
{
87-
"Action": "sns:Publish",
88-
"Effect": "Allow",
89-
"Resource": "*"
90-
},
91-
{
92-
"Action": "sns:ListTopics",
93-
"Effect": "Allow",
94-
"Resource": "*"
95-
},
96-
{
97-
"Action": "ssm:GetParameter",
98-
"Effect": "Allow",
99-
"Resource": "*"
100-
}
101-
],
102-
"Version": "2012-10-17"
103-
},
104-
"PolicyName": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
105-
"Roles": [
106-
{
107-
"Ref": "AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
108-
}
109-
]
110-
}
111-
},
11284
"AWS679f53fac002430cb0da5b7982bd22872D164C4C": {
11385
"Type": "AWS::Lambda::Function",
11486
"Properties": {
@@ -161,7 +133,6 @@
161133
"Timeout": 120
162134
},
163135
"DependsOn": [
164-
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleDefaultPolicyD28E1A5E",
165136
"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"
166137
]
167138
},
@@ -191,6 +162,7 @@
191162
"InstallLatestAwsSdk": true
192163
},
193164
"DependsOn": [
165+
"ListTopicsCustomResourcePolicy31A8396A",
194166
"TopicBFC7AF6E"
195167
],
196168
"UpdateReplacePolicy": "Delete",
@@ -241,7 +213,44 @@
241213
"InstallLatestAwsSdk": true
242214
},
243215
"UpdateReplacePolicy": "Delete",
244-
"DeletionPolicy": "Delete"
216+
"DeletionPolicy": "Delete",
217+
"DependsOn": [
218+
"GetParameterCustomResourcePolicyD8E5D455"
219+
]
220+
},
221+
"PublishCustomResourcePolicyDF696FCA": {
222+
"Type": "AWS::IAM::Policy",
223+
"Properties": {
224+
"PolicyDocument": {
225+
"Statement": [{"Action":"sns:Publish","Effect":"Allow","Resource":"*"}],
226+
"Version": "2012-10-17"
227+
},
228+
"PolicyName": "PublishCustomResourcePolicyDF696FCA",
229+
"Roles": [{"Ref":"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"}]
230+
}
231+
},
232+
"ListTopicsCustomResourcePolicy31A8396A": {
233+
"Type": "AWS::IAM::Policy",
234+
"Properties": {
235+
"PolicyDocument": {
236+
"Statement": [{"Action":"sns:ListTopics","Effect":"Allow","Resource":"*"}],
237+
"Version": "2012-10-17"
238+
},
239+
"PolicyName": "ListTopicsCustomResourcePolicy31A8396A",
240+
"Roles": [{"Ref":"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"}]
241+
},
242+
"DependsOn": ["TopicBFC7AF6E"]
243+
},
244+
"GetParameterCustomResourcePolicyD8E5D455": {
245+
"Type": "AWS::IAM::Policy",
246+
"Properties": {
247+
"PolicyDocument": {
248+
"Statement": [{"Action":"ssm:GetParameter","Effect":"Allow","Resource":"*"}],
249+
"Version": "2012-10-17"
250+
},
251+
"PolicyName": "GetParameterCustomResourcePolicyD8E5D455",
252+
"Roles": [{"Ref":"AWS679f53fac002430cb0da5b7982bd2287ServiceRoleC1EA0FF2"}]
253+
}
245254
}
246255
},
247256
"Parameters": {

0 commit comments

Comments
 (0)