Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iam_role - makes EntityAlreadyExists error a warning and fixes bug when not creating a profile #2282

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading