Skip to content

Commit

Permalink
New modules: iam_instance_profile(_info) (#1614)
Browse files Browse the repository at this point in the history
New modules: iam_instance_profile(_info)

SUMMARY
New modules for listing/managing IAM Instance Profiles
ISSUE TYPE

New Module Pull Request

COMPONENT NAME
iam_instance_profile
iam_instance_profile_info
ADDITIONAL INFORMATION
Fixes: ansible-collections/community.aws#1842

Reviewed-by: Alina Buzachis
Reviewed-by: Mark Chappell
(cherry picked from commit 6a1f875)
  • Loading branch information
tremble authored and patchback[bot] committed Jun 27, 2023
1 parent 5fc207b commit ff9e7ce
Show file tree
Hide file tree
Showing 11 changed files with 1,520 additions and 3 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/1843-iam_instance_profile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
trivial:
- new modules - iam_instance_profile, iam_instance_profile_info
2 changes: 2 additions & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ action_groups:
- elb_application_lb_info
- elb_classic_lb
- execute_lambda
- iam_instance_profile
- iam_instance_profile_info
- iam_policy
- iam_policy_info
- iam_user
Expand Down
210 changes: 207 additions & 3 deletions plugins/module_utils/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,73 @@
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from copy import deepcopy

try:
import botocore
except ImportError:
pass
pass # Modules are responsible for handling this.

from ansible.module_utils._text import to_native
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from .retries import AWSRetry
from .botocore import is_boto3_error_code
from .arn import parse_aws_arn
from .botocore import is_boto3_error_code
from .exceptions import AnsibleAWSError
from .retries import AWSRetry
from .tagging import ansible_dict_to_boto3_tag_list
from .tagging import boto3_tag_list_to_ansible_dict


class AnsibleIAMError(AnsibleAWSError):
pass


@AWSRetry.jittered_backoff()
def _tag_iam_instance_profile(client, **kwargs):
client.tag_instance_profile(**kwargs)


@AWSRetry.jittered_backoff()
def _untag_iam_instance_profile(client, **kwargs):
client.untag_instance_profile(**kwargs)


@AWSRetry.jittered_backoff()
def _get_iam_instance_profiles(client, **kwargs):
return client.get_instance_profile(**kwargs)["InstanceProfile"]


@AWSRetry.jittered_backoff()
def _list_iam_instance_profiles(client, **kwargs):
paginator = client.get_paginator("list_instance_profiles")
return paginator.paginate(**kwargs).build_full_result()["InstanceProfiles"]


@AWSRetry.jittered_backoff()
def _list_iam_instance_profiles_for_role(client, **kwargs):
paginator = client.get_paginator("list_instance_profiles_for_role")
return paginator.paginate(**kwargs).build_full_result()["InstanceProfiles"]


@AWSRetry.jittered_backoff()
def _create_instance_profile(client, **kwargs):
return client.create_instance_profile(**kwargs)


@AWSRetry.jittered_backoff()
def _delete_instance_profile(client, **kwargs):
client.delete_instance_profile(**kwargs)


@AWSRetry.jittered_backoff()
def _add_role_to_instance_profile(client, **kwargs):
client.add_role_to_instance_profile(**kwargs)


@AWSRetry.jittered_backoff()
def _remove_role_from_instance_profile(client, **kwargs):
client.remove_role_from_instance_profile(**kwargs)


def get_aws_account_id(module):
Expand Down Expand Up @@ -76,3 +133,150 @@ def get_aws_account_info(module):
)

return (to_native(account_id), to_native(partition))


def create_iam_instance_profile(client, name, path, tags):
boto3_tags = ansible_dict_to_boto3_tag_list(tags or {})
try:
result = _create_instance_profile(client, InstanceProfileName=name, Path=path, Tags=boto3_tags)
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(message="Unable to create instance profile", exception=e)
return result["InstanceProfile"]


def delete_iam_instance_profile(client, name):
try:
_delete_instance_profile(client, InstanceProfileName=name)
except is_boto3_error_code("NoSuchEntity"):
# Deletion already happened.
return False
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(message="Unable to delete instance profile", exception=e)
return True


def add_role_to_iam_instance_profile(client, profile_name, role_name):
try:
_add_role_to_instance_profile(client, InstanceProfileName=profile_name, RoleName=role_name)
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(
message="Unable to add role to instance profile",
exception=e,
profile_name=profile_name,
role_name=role_name,
)
return True


def remove_role_from_iam_instance_profile(client, profile_name, role_name):
try:
_remove_role_from_instance_profile(client, InstanceProfileName=profile_name, RoleName=role_name)
except is_boto3_error_code("NoSuchEntity"):
# Deletion already happened.
return False
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(
message="Unable to remove role from instance profile",
exception=e,
profile_name=profile_name,
role_name=role_name,
)
return True


def list_iam_instance_profiles(client, name=None, prefix=None, role=None):
"""
Returns a list of IAM instance profiles in boto3 format.
Profiles need to be converted to Ansible format using normalize_iam_instance_profile before being displayed.
See also: normalize_iam_instance_profile
"""
try:
if role:
return _list_iam_instance_profiles_for_role(client, RoleName=role)
if name:
# Unlike the others this returns a single result, make this a list with 1 element.
return [_get_iam_instance_profiles(client, InstanceProfileName=name)]
if prefix:
return _list_iam_instance_profiles(client, PathPrefix=prefix)
return _list_iam_instance_profiles(client)
except is_boto3_error_code("NoSuchEntity"):
return []
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(message="Unable to list instance profiles", exception=e)


def normalize_iam_instance_profile(profile):
"""
Converts a boto3 format IAM instance profile into "Ansible" format
"""

new_profile = camel_dict_to_snake_dict(deepcopy(profile))
if profile.get("Roles"):
new_profile["roles"] = [normalize_iam_role(role) for role in profile.get("Roles")]
if profile.get("Tags"):
new_profile["tags"] = boto3_tag_list_to_ansible_dict(profile.get("Tags"))
else:
new_profile["tags"] = {}
new_profile["original"] = profile
return new_profile


def normalize_iam_role(role):
"""
Converts a boto3 format IAM instance role into "Ansible" format
"""

new_role = camel_dict_to_snake_dict(deepcopy(role))
if role.get("InstanceProfiles"):
new_role["instance_profiles"] = [
normalize_iam_instance_profile(profile) for profile in role.get("InstanceProfiles")
]
if role.get("AssumeRolePolicyDocument"):
new_role["assume_role_policy_document"] = role.get("AssumeRolePolicyDocument")
if role.get("Tags"):
new_role["tags"] = boto3_tag_list_to_ansible_dict(role.get("Tags"))
else:
new_role["tags"] = {}
new_role["original"] = role
return new_role


def tag_iam_instance_profile(client, name, tags):
if not tags:
return
boto3_tags = ansible_dict_to_boto3_tag_list(tags or {})
try:
result = _tag_iam_instance_profile(client, InstanceProfileName=name, Tags=boto3_tags)
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(message="Unable to tag instance profile", exception=e)


def untag_iam_instance_profile(client, name, tags):
if not tags:
return
try:
result = _untag_iam_instance_profile(client, InstanceProfileName=name, TagKeys=tags)
except (
botocore.exceptions.BotoCoreError,
botocore.exceptions.ClientError,
) as e: # pylint: disable=duplicate-except
raise AnsibleIAMError(message="Unable to untag instance profile", exception=e)
Loading

0 comments on commit ff9e7ce

Please sign in to comment.