Skip to content

Commit 689024f

Browse files
iam_role - makes EntityAlreadyExists error a warning and fixes bug when not creating a profile (#2282) (#2283)
This is a backport of PR #2282 as merged into main (4867e68). fixes: #2102 fixes: #2281 SUMMARY #2221 deprecation logic accidentally forced create_instance_profile to True The IAM refactor made iam_role sensitive to pre-existing instance profiles with the same name. ISSUE TYPE Bugfix Pull Request COMPONENT NAME iam_role ADDITIONAL INFORMATION Reviewed-by: Mark Chappell
1 parent 270de16 commit 689024f

File tree

4 files changed

+319
-10
lines changed

4 files changed

+319
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
bugfixes:
3+
- iam_role - fixes issue where IAM instance profiles were created when ``create_instance_profile`` was set to ``false`` (https://github.com/ansible-collections/amazon.aws/issues/2281).
4+
- iam_role - fixes ``EntityAlreadyExists`` exception when ``create_instance_profile`` was set to ``false`` and the instance profile already existed (https://github.com/ansible-collections/amazon.aws/issues/2102).

plugins/modules/iam_role.py

+22-9
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,17 @@
8080
type: str
8181
create_instance_profile:
8282
description:
83-
- Creates an IAM instance profile along with the role.
83+
- If no IAM instance profile with the same O(name) exists, setting O(create_instance_profile=True)
84+
will create an IAM instance profile along with the role.
8485
- This option has been deprecated and will be removed in a release after 2026-05-01. The
8586
M(amazon.aws.iam_instance_profile) module can be used to manage instance profiles.
8687
- Defaults to V(True)
8788
type: bool
8889
delete_instance_profile:
8990
description:
90-
- When O(delete_instance_profile=true) and O(state=absent) deleting a role will also delete the instance
91-
profile created with the same O(name) as the role.
91+
- When O(delete_instance_profile=true) and O(state=absent) deleting a role will also delete an
92+
instance profile with the same O(name) as the role, but only if the instance profile is
93+
associated with the role.
9294
- Only applies when O(state=absent).
9395
- This option has been deprecated and will be removed in a release after 2026-05-01. The
9496
M(amazon.aws.iam_instance_profile) module can be used to manage instance profiles.
@@ -294,6 +296,10 @@
294296
from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags
295297

296298

299+
class AnsibleIAMAlreadyExistsError(AnsibleIAMError):
300+
pass
301+
302+
297303
@IAMErrorHandler.common_error_handler("wait for role creation")
298304
def wait_iam_exists(client, check_mode, role_name, wait, wait_timeout):
299305
if check_mode or wait:
@@ -535,8 +541,11 @@ def create_or_update_role(module, client, role_name, create_instance_profile):
535541
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
536542

537543
if create_instance_profile:
538-
changed |= create_instance_profiles(client, check_mode, role_name, path)
539-
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
544+
try:
545+
changed |= create_instance_profiles(client, check_mode, role_name, path)
546+
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
547+
except AnsibleIAMAlreadyExistsError as e:
548+
module.warn(f"profile {role_name} already exists and will not be updated")
540549

541550
changed |= update_managed_policies(client, module.check_mode, role_name, managed_policies, purge_policies)
542551
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
@@ -551,12 +560,15 @@ def create_or_update_role(module, client, role_name, create_instance_profile):
551560

552561
def create_instance_profiles(client, check_mode, role_name, path):
553562
# Fetch existing Profiles
554-
instance_profiles = list_iam_instance_profiles(client, role=role_name)
555-
563+
role_profiles = list_iam_instance_profiles(client, role=role_name)
556564
# Profile already exists
557-
if any(p["InstanceProfileName"] == role_name for p in instance_profiles):
565+
if any(p["InstanceProfileName"] == role_name for p in role_profiles):
558566
return False
559567

568+
named_profile = list_iam_instance_profiles(client, name=role_name)
569+
if named_profile:
570+
raise AnsibleIAMAlreadyExistsError(f"profile {role_name} already exists")
571+
560572
if check_mode:
561573
return True
562574

@@ -730,7 +742,8 @@ def main():
730742
state = module.params.get("state")
731743
role_name = module.params.get("name")
732744

733-
create_profile = module.params.get("create_instance_profile") or True
745+
create_profile = module.params.get("create_instance_profile")
746+
create_profile = True if create_profile is None else create_profile
734747
delete_profile = module.params.get("delete_instance_profile") or False
735748

736749
try:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
---
2+
3+
- block:
4+
# Ensure profile doesn't already exist (from an old test)
5+
- name: Delete Instance Profile
6+
amazon.aws.iam_instance_profile:
7+
state: absent
8+
name: "{{ test_role }}"
9+
10+
##################################################################
11+
12+
# Profile doesn't exist, don't create
13+
- name: Minimal IAM Role without instance profile (without existing profile)
14+
amazon.aws.iam_role:
15+
name: "{{ test_role }}"
16+
create_instance_profile: false
17+
register: iam_role
18+
- ansible.builtin.assert:
19+
that:
20+
- iam_role is changed
21+
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'
22+
23+
- name: Verify instance profile doesn't exist
24+
amazon.aws.iam_instance_profile_info:
25+
name: "{{ test_role }}"
26+
register: iam_instance_profile
27+
- ansible.builtin.assert:
28+
that:
29+
- iam_instance_profile.iam_instance_profiles | length == 0
30+
31+
# Profile doesn't exist, do delete
32+
- name: Remove IAM Role and profile (with non-existent profile)
33+
amazon.aws.iam_role:
34+
state: absent
35+
name: "{{ test_role }}"
36+
delete_instance_profile: true
37+
register: iam_role
38+
- ansible.builtin.assert:
39+
that:
40+
- iam_role is changed
41+
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'
42+
43+
- name: Verify instance profile doesn't exist
44+
amazon.aws.iam_instance_profile_info:
45+
name: "{{ test_role }}"
46+
register: iam_instance_profile
47+
- ansible.builtin.assert:
48+
that:
49+
- iam_instance_profile.iam_instance_profiles | length == 0
50+
51+
##################################################################
52+
53+
# Profile doesn't exist, do create
54+
- name: Minimal IAM Role with instance profile (without existing profile)
55+
amazon.aws.iam_role:
56+
name: "{{ test_role }}"
57+
create_instance_profile: true
58+
register: iam_role
59+
- ansible.builtin.assert:
60+
that:
61+
- iam_role is changed
62+
- '"iam:CreateInstanceProfile" in iam_role.resource_actions'
63+
64+
- name: Verify instance profile exists
65+
amazon.aws.iam_instance_profile_info:
66+
name: "{{ test_role }}"
67+
register: iam_instance_profile
68+
- ansible.builtin.assert:
69+
that:
70+
- iam_instance_profile.iam_instance_profiles | length == 1
71+
72+
# Profile does exist, don't delete
73+
- name: Remove IAM Role and not profile (with existent profile)
74+
amazon.aws.iam_role:
75+
state: absent
76+
name: "{{ test_role }}"
77+
delete_instance_profile: false
78+
register: iam_role
79+
- ansible.builtin.assert:
80+
that:
81+
- iam_role is changed
82+
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'
83+
84+
- name: Verify instance profile exists
85+
amazon.aws.iam_instance_profile_info:
86+
name: "{{ test_role }}"
87+
register: iam_instance_profile
88+
- ansible.builtin.assert:
89+
that:
90+
- iam_instance_profile.iam_instance_profiles | length == 1
91+
92+
##################################################################
93+
94+
# Profile does exist, do create
95+
- name: Minimal IAM Role with instance profile (profile exists)
96+
amazon.aws.iam_role:
97+
name: "{{ test_role }}"
98+
create_instance_profile: true
99+
register: iam_role
100+
- ansible.builtin.assert:
101+
that:
102+
- iam_role is changed
103+
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'
104+
105+
- name: Verify instance profile exists
106+
amazon.aws.iam_instance_profile_info:
107+
name: "{{ test_role }}"
108+
register: iam_instance_profile
109+
- ansible.builtin.assert:
110+
that:
111+
- iam_instance_profile.iam_instance_profiles | length == 1
112+
113+
114+
# Profile does exist, don't delete
115+
- name: Remove IAM Role and don't delete profile (with existent profile)
116+
amazon.aws.iam_role:
117+
state: absent
118+
name: "{{ test_role }}"
119+
delete_instance_profile: false
120+
register: iam_role
121+
- ansible.builtin.assert:
122+
that:
123+
- iam_role is changed
124+
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'
125+
126+
- name: Verify instance profile exists
127+
amazon.aws.iam_instance_profile_info:
128+
name: "{{ test_role }}"
129+
register: iam_instance_profile
130+
- ansible.builtin.assert:
131+
that:
132+
- iam_instance_profile.iam_instance_profiles | length == 1
133+
134+
##################################################################
135+
# No create profile - profile already exists
136+
137+
# Profile does exist, don't create
138+
- name: Minimal IAM Role without instance profile (profile exists)
139+
amazon.aws.iam_role:
140+
name: "{{ test_role }}"
141+
create_instance_profile: false
142+
register: iam_role
143+
144+
- ansible.builtin.assert:
145+
that:
146+
- iam_role is changed
147+
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'
148+
149+
- name: Verify instance profile exists
150+
amazon.aws.iam_instance_profile_info:
151+
name: "{{ test_role }}"
152+
register: iam_instance_profile
153+
154+
- ansible.builtin.assert:
155+
that:
156+
- iam_instance_profile.iam_instance_profiles | length == 1
157+
158+
- name: Attach Role to profile
159+
amazon.aws.iam_instance_profile:
160+
name: "{{ test_role }}"
161+
role: "{{ test_role }}"
162+
register: iam_instance_profile
163+
164+
- ansible.builtin.assert:
165+
that:
166+
- iam_instance_profile is changed
167+
168+
# Profile does exist, do delete
169+
- name: Remove IAM Role and delete profile (with existent profile)
170+
amazon.aws.iam_role:
171+
state: absent
172+
name: "{{ test_role }}"
173+
delete_instance_profile: true
174+
register: iam_role
175+
176+
- ansible.builtin.assert:
177+
that:
178+
- iam_role is changed
179+
- '"iam:DeleteInstanceProfile" in iam_role.resource_actions'
180+
181+
- name: Verify instance profile exists
182+
amazon.aws.iam_instance_profile_info:
183+
name: "{{ test_role }}"
184+
register: iam_instance_profile
185+
186+
- ansible.builtin.assert:
187+
that:
188+
- iam_instance_profile.iam_instance_profiles | length == 0
189+
190+
##################################################################
191+
192+
# Profile doesn't exist, don't create
193+
- name: Minimal IAM Role without instance profile (without existing profile)
194+
amazon.aws.iam_role:
195+
name: "{{ test_role }}"
196+
create_instance_profile: false
197+
register: iam_role
198+
- ansible.builtin.assert:
199+
that:
200+
- iam_role is changed
201+
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'
202+
203+
- name: Verify instance profile doesn't exist
204+
amazon.aws.iam_instance_profile_info:
205+
name: "{{ test_role }}"
206+
register: iam_instance_profile
207+
- ansible.builtin.assert:
208+
that:
209+
- iam_instance_profile.iam_instance_profiles | length == 0
210+
211+
# Profile doesn't exist, don't delete
212+
- name: Remove IAM Role and profile (with non-existent profile)
213+
amazon.aws.iam_role:
214+
state: absent
215+
name: "{{ test_role }}"
216+
delete_instance_profile: false
217+
register: iam_role
218+
- ansible.builtin.assert:
219+
that:
220+
- iam_role is changed
221+
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'
222+
223+
- name: Verify instance profile doesn't exist
224+
amazon.aws.iam_instance_profile_info:
225+
name: "{{ test_role }}"
226+
register: iam_instance_profile
227+
- ansible.builtin.assert:
228+
that:
229+
- iam_instance_profile.iam_instance_profiles | length == 0
230+
231+
##################################################################
232+
233+
# Profile doesn't exist, do create
234+
- name: Minimal IAM Role with instance profile (profile does not exist)
235+
amazon.aws.iam_role:
236+
name: "{{ test_role }}"
237+
create_instance_profile: true
238+
register: iam_role
239+
- ansible.builtin.assert:
240+
that:
241+
- iam_role is changed
242+
- '"iam:CreateInstanceProfile" in iam_role.resource_actions'
243+
244+
- name: Decouple Instance Profile from role
245+
amazon.aws.iam_instance_profile:
246+
name: "{{ test_role }}"
247+
role: ""
248+
register: iam_instance_profile
249+
250+
- ansible.builtin.assert:
251+
that:
252+
- iam_instance_profile is changed
253+
254+
# Detached profile exists, we shouldn't delete it.
255+
- name: Remove IAM Role and "delete" profile (with detached profile)
256+
amazon.aws.iam_role:
257+
state: absent
258+
name: "{{ test_role }}"
259+
delete_instance_profile: true
260+
register: iam_role
261+
- ansible.builtin.assert:
262+
that:
263+
- iam_role is changed
264+
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'
265+
266+
- name: Verify instance profile exists
267+
amazon.aws.iam_instance_profile_info:
268+
name: "{{ test_role }}"
269+
register: iam_instance_profile
270+
- ansible.builtin.assert:
271+
that:
272+
- iam_instance_profile.iam_instance_profiles | length == 1
273+
274+
##################################################################
275+
# Delete profile
276+
277+
- name: Delete Instance Profile
278+
amazon.aws.iam_instance_profile:
279+
state: absent
280+
name: "{{ test_role }}"
281+
register: iam_instance_profile
282+
283+
- ansible.builtin.assert:
284+
that:
285+
- iam_instance_profile is changed
286+
287+
always:
288+
- name: Delete Instance Profiles
289+
amazon.aws.iam_instance_profile:
290+
state: absent
291+
name: "{{ test_role }}"
292+
ignore_errors: true

tests/integration/targets/iam_role/tasks/main.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
- create_managed_policy is succeeded
4646

4747
# ===================================================================
48-
# Rapid Role Creation and deletion
48+
- ansible.builtin.include_tasks: instance_profile.yml
4949
- ansible.builtin.include_tasks: creation_deletion.yml
5050
- ansible.builtin.include_tasks: max_session_update.yml
5151
- ansible.builtin.include_tasks: description_update.yml

0 commit comments

Comments
 (0)