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

[PR #1614/6a1f875e backport][stable-6] New modules: iam_instance_profile(_info) #1630

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
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