Skip to content

Commit

Permalink
iam_role - makes EntityAlreadyExists error a warning and fixes bug wh…
Browse files Browse the repository at this point in the history
…en not creating a profile (#2282)

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: Alina Buzachis
Reviewed-by: GomathiselviS
  • Loading branch information
tremble authored Sep 4, 2024
1 parent 26d7243 commit 4867e68
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 10 deletions.
4 changes: 4 additions & 0 deletions changelogs/fragments/2281-iam_role-support-not-creating.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
bugfixes:
- 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).
- 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).
31 changes: 22 additions & 9 deletions plugins/modules/iam_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,17 @@
type: str
create_instance_profile:
description:
- Creates an IAM instance profile along with the role.
- If no IAM instance profile with the same O(name) exists, setting O(create_instance_profile=True)
will create an IAM instance profile along with the role.
- This option has been deprecated and will be removed in a release after 2026-05-01. The
M(amazon.aws.iam_instance_profile) module can be used to manage instance profiles.
- Defaults to V(True)
type: bool
delete_instance_profile:
description:
- When O(delete_instance_profile=true) and O(state=absent) deleting a role will also delete the instance
profile created with the same O(name) as the role.
- When O(delete_instance_profile=true) and O(state=absent) deleting a role will also delete an
instance profile with the same O(name) as the role, but only if the instance profile is
associated with the role.
- Only applies when O(state=absent).
- This option has been deprecated and will be removed in a release after 2026-05-01. The
M(amazon.aws.iam_instance_profile) module can be used to manage instance profiles.
Expand Down Expand Up @@ -294,6 +296,10 @@
from ansible_collections.amazon.aws.plugins.module_utils.tagging import compare_aws_tags


class AnsibleIAMAlreadyExistsError(AnsibleIAMError):
pass


@IAMErrorHandler.common_error_handler("wait for role creation")
def wait_iam_exists(client, check_mode, role_name, wait, wait_timeout):
if check_mode or wait:
Expand Down Expand Up @@ -535,8 +541,11 @@ def create_or_update_role(module, client, role_name, create_instance_profile):
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)

if create_instance_profile:
changed |= create_instance_profiles(client, check_mode, role_name, path)
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
try:
changed |= create_instance_profiles(client, check_mode, role_name, path)
wait_iam_exists(client, check_mode, role_name, wait, wait_timeout)
except AnsibleIAMAlreadyExistsError as e:
module.warn(f"profile {role_name} already exists and will not be updated")

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

def create_instance_profiles(client, check_mode, role_name, path):
# Fetch existing Profiles
instance_profiles = list_iam_instance_profiles(client, role=role_name)

role_profiles = list_iam_instance_profiles(client, role=role_name)
# Profile already exists
if any(p["InstanceProfileName"] == role_name for p in instance_profiles):
if any(p["InstanceProfileName"] == role_name for p in role_profiles):
return False

named_profile = list_iam_instance_profiles(client, name=role_name)
if named_profile:
raise AnsibleIAMAlreadyExistsError(f"profile {role_name} already exists")

if check_mode:
return True

Expand Down Expand Up @@ -730,7 +742,8 @@ def main():
state = module.params.get("state")
role_name = module.params.get("name")

create_profile = module.params.get("create_instance_profile") or True
create_profile = module.params.get("create_instance_profile")
create_profile = True if create_profile is None else create_profile
delete_profile = module.params.get("delete_instance_profile") or False

try:
Expand Down
292 changes: 292 additions & 0 deletions tests/integration/targets/iam_role/tasks/instance_profile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
---

- block:
# Ensure profile doesn't already exist (from an old test)
- name: Delete Instance Profile
amazon.aws.iam_instance_profile:
state: absent
name: "{{ test_role }}"

##################################################################

# Profile doesn't exist, don't create
- name: Minimal IAM Role without instance profile (without existing profile)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: false
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile doesn't exist
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 0

# Profile doesn't exist, do delete
- name: Remove IAM Role and profile (with non-existent profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: true
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile doesn't exist
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 0

##################################################################

# Profile doesn't exist, do create
- name: Minimal IAM Role with instance profile (without existing profile)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: true
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1

# Profile does exist, don't delete
- name: Remove IAM Role and not profile (with existent profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: false
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1

##################################################################

# Profile does exist, do create
- name: Minimal IAM Role with instance profile (profile exists)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: true
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1


# Profile does exist, don't delete
- name: Remove IAM Role and don't delete profile (with existent profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: false
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1

##################################################################
# No create profile - profile already exists

# Profile does exist, don't create
- name: Minimal IAM Role without instance profile (profile exists)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: false
register: iam_role

- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile

- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1

- name: Attach Role to profile
amazon.aws.iam_instance_profile:
name: "{{ test_role }}"
role: "{{ test_role }}"
register: iam_instance_profile

- ansible.builtin.assert:
that:
- iam_instance_profile is changed

# Profile does exist, do delete
- name: Remove IAM Role and delete profile (with existent profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: true
register: iam_role

- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile

- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 0

##################################################################

# Profile doesn't exist, don't create
- name: Minimal IAM Role without instance profile (without existing profile)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: false
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile doesn't exist
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 0

# Profile doesn't exist, don't delete
- name: Remove IAM Role and profile (with non-existent profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: false
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile doesn't exist
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 0

##################################################################

# Profile doesn't exist, do create
- name: Minimal IAM Role with instance profile (profile does not exist)
amazon.aws.iam_role:
name: "{{ test_role }}"
create_instance_profile: true
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:CreateInstanceProfile" in iam_role.resource_actions'

- name: Decouple Instance Profile from role
amazon.aws.iam_instance_profile:
name: "{{ test_role }}"
role: ""
register: iam_instance_profile

- ansible.builtin.assert:
that:
- iam_instance_profile is changed

# Detached profile exists, we shouldn't delete it.
- name: Remove IAM Role and "delete" profile (with detached profile)
amazon.aws.iam_role:
state: absent
name: "{{ test_role }}"
delete_instance_profile: true
register: iam_role
- ansible.builtin.assert:
that:
- iam_role is changed
- '"iam:DeleteInstanceProfile" not in iam_role.resource_actions'

- name: Verify instance profile exists
amazon.aws.iam_instance_profile_info:
name: "{{ test_role }}"
register: iam_instance_profile
- ansible.builtin.assert:
that:
- iam_instance_profile.iam_instance_profiles | length == 1

##################################################################
# Delete profile

- name: Delete Instance Profile
amazon.aws.iam_instance_profile:
state: absent
name: "{{ test_role }}"
register: iam_instance_profile

- ansible.builtin.assert:
that:
- iam_instance_profile is changed

always:
- name: Delete Instance Profiles
amazon.aws.iam_instance_profile:
state: absent
name: "{{ test_role }}"
ignore_errors: true
2 changes: 1 addition & 1 deletion tests/integration/targets/iam_role/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
- create_managed_policy is succeeded

# ===================================================================
# Rapid Role Creation and deletion
- ansible.builtin.include_tasks: instance_profile.yml
- ansible.builtin.include_tasks: creation_deletion.yml
- ansible.builtin.include_tasks: max_session_update.yml
- ansible.builtin.include_tasks: description_update.yml
Expand Down

0 comments on commit 4867e68

Please sign in to comment.