Skip to content

Commit 368cd56

Browse files
authored
aws - iam-role add ability to force on delete action (cloud-custodian#6446)
1 parent f855016 commit 368cd56

17 files changed

+274
-15
lines changed

c7n/resources/iam.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,17 @@ def detach_all_policies(self, client, resource):
11061106
class RoleDelete(BaseAction):
11071107
"""Delete an IAM Role.
11081108
1109+
To delete IAM Role you must first delete the policies
1110+
that are associated with the role. Also, you need to remove
1111+
the role from all instance profiles that the role is in.
1112+
1113+
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_manage_delete.html
1114+
1115+
For this case option 'force' is used. If you set it as 'true',
1116+
policies that are associated with the role would be detached
1117+
(inline policies would be removed) and all instance profiles
1118+
the role is in would be removed as well as the role.
1119+
11091120
For example, if you want to automatically delete an unused IAM role.
11101121
11111122
:example:
@@ -1124,7 +1135,7 @@ class RoleDelete(BaseAction):
11241135
11251136
"""
11261137
schema = type_schema('delete', force={'type': 'boolean'})
1127-
permissions = ('iam:DeleteRole',)
1138+
permissions = ('iam:DeleteRole', 'iam:DeleteInstanceProfile',)
11281139

11291140
def detach_inline_policies(self, client, r):
11301141
policies = (self.manager.retry(
@@ -1136,6 +1147,26 @@ def detach_inline_policies(self, client, r):
11361147
RoleName=r['RoleName'], PolicyName=p,
11371148
ignore_err_codes=('NoSuchEntityException',))
11381149

1150+
def delete_instance_profiles(self, client, r):
1151+
# An instance profile can contain only one IAM role,
1152+
# although a role can be included in multiple instance profiles
1153+
profile_names = []
1154+
profiles = self.manager.retry(
1155+
client.list_instance_profiles_for_role,
1156+
RoleName=r['RoleName'],
1157+
ignore_err_codes=('NoSuchEntityException',))
1158+
if profiles:
1159+
profile_names = [p.get('InstanceProfileName') for p in profiles['InstanceProfiles']]
1160+
for p in profile_names:
1161+
self.manager.retry(
1162+
client.remove_role_from_instance_profile,
1163+
RoleName=r['RoleName'], InstanceProfileName=p,
1164+
ignore_err_codes=('NoSuchEntityException',))
1165+
self.manager.retry(
1166+
client.delete_instance_profile,
1167+
InstanceProfileName=p,
1168+
ignore_err_codes=('NoSuchEntityException',))
1169+
11391170
def process(self, resources):
11401171
client = local_session(self.manager.session_factory).client('iam')
11411172
error = None
@@ -1147,12 +1178,13 @@ def process(self, resources):
11471178
for r in resources:
11481179
if self.data.get('force', False):
11491180
self.detach_inline_policies(client, r)
1181+
self.delete_instance_profiles(client, r)
11501182
try:
11511183
client.delete_role(RoleName=r['RoleName'])
11521184
except client.exceptions.DeleteConflictException as e:
11531185
self.log.warning(
11541186
("Role:%s cannot be deleted, set force "
1155-
"to detach policy and delete, error: %s") % (
1187+
"to detach policy, instance profile and delete, error: %s") % (
11561188
r['Arn'], str(e)))
11571189
error = e
11581190
except (client.exceptions.NoSuchEntityException,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"ResponseMetadata": {}
5+
}
6+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"status_code": 404,
3+
"data": {
4+
"Error": {
5+
"Type": "Sender",
6+
"Code": "NoSuchEntity",
7+
"Message": "Instance Profile test cannot be found."
8+
},
9+
"ResponseMetadata": {
10+
"RequestId": "RequestId",
11+
"HTTPStatusCode": 404,
12+
"HTTPHeaders": {
13+
"x-amzn-requestid": "x-amzn-requestid",
14+
"content-type": "text/xml",
15+
"content-length": "282",
16+
"date": "Mon, 15 Feb 2021 11:16:24 GMT"
17+
},
18+
"RetryAttempts": 0
19+
}
20+
}
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"InstanceProfiles": [
5+
{
6+
"InstanceProfileName": "test_profile",
7+
"Roles": [
8+
{
9+
"RoleName": "test_role"
10+
}
11+
]
12+
}
13+
],
14+
"IsTruncated": false,
15+
"ResponseMetadata": {}
16+
}
17+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"InstanceProfiles": [],
5+
"IsTruncated": false,
6+
"ResponseMetadata": {}
7+
}
8+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"status_code": 404,
3+
"data": {
4+
"Error": {
5+
"Type": "Sender",
6+
"Code": "NoSuchEntity",
7+
"Message": "The role with name test cannot be found."
8+
},
9+
"ResponseMetadata": {
10+
"RequestId": "RequestId",
11+
"HTTPStatusCode": 404,
12+
"HTTPHeaders": {
13+
"x-amzn-requestid": "x-amzn-requestid",
14+
"content-type": "text/xml",
15+
"content-length": "284",
16+
"date": "Mon, 15 Feb 2021 11:05:26 GMT"
17+
},
18+
"RetryAttempts": 0
19+
}
20+
}
21+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"ResponseMetadata": {}
5+
}
6+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"status_code": 404,
3+
"data": {
4+
"Error": {
5+
"Type": "Sender",
6+
"Code": "NoSuchEntity",
7+
"Message": "The role with name test cannot be found."
8+
},
9+
"ResponseMetadata": {
10+
"RequestId": "RequestId",
11+
"HTTPStatusCode": 404,
12+
"HTTPHeaders": {
13+
"x-amzn-requestid": "x-amzn-requestid",
14+
"content-type": "text/xml",
15+
"content-length": "284",
16+
"date": "Mon, 15 Feb 2021 11:16:24 GMT"
17+
},
18+
"RetryAttempts": 0
19+
}
20+
}
21+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"ResponseMetadata": {}
5+
}
6+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"status_code": 404,
3+
"data": {
4+
"Error": {
5+
"Type": "Sender",
6+
"Code": "NoSuchEntity",
7+
"Message": "Instance Profile test cannot be found."
8+
},
9+
"ResponseMetadata": {
10+
"RequestId": "RequestId",
11+
"HTTPStatusCode": 404,
12+
"HTTPHeaders": {
13+
"x-amzn-requestid": "x-amzn-requestid",
14+
"content-type": "text/xml",
15+
"content-length": "282",
16+
"date": "Mon, 15 Feb 2021 11:16:24 GMT"
17+
},
18+
"RetryAttempts": 0
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)