Skip to content

Commit

Permalink
adding some unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
abikouo committed Sep 18, 2023
1 parent a788868 commit bee50d8
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 71 deletions.
127 changes: 70 additions & 57 deletions plugins/modules/iam_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
DOCUMENTATION = r"""
---
module: iam_role
version_added: 1.0.0
version_added: 7.0.0
short_description: Manage AWS IAM roles
description:
- Manage AWS IAM roles.
Expand Down Expand Up @@ -180,7 +180,6 @@
description: the policy that grants an entity permission to assume the role
type: dict
returned: always
version_added: 5.3.0
sample: {
'Statement': [
{
Expand Down Expand Up @@ -238,6 +237,17 @@ def _list_policies(client):
return paginator.paginate().build_full_result()["Policies"]


def _wait_iam_role(client, role_name, wait_timeout):
delay = min(wait_timeout, 5)
max_attempts = wait_timeout // delay

waiter = client.get_waiter("role_exists")
waiter.wait(
WaiterConfig={"Delay": delay, "MaxAttempts": max_attempts},
RoleName=role_name,
)


def wait_iam_exists(module, client):
if module.check_mode:
return
Expand All @@ -247,15 +257,8 @@ def wait_iam_exists(module, client):
role_name = module.params.get("name")
wait_timeout = module.params.get("wait_timeout")

delay = min(wait_timeout, 5)
max_attempts = wait_timeout // delay

try:
waiter = client.get_waiter("role_exists")
waiter.wait(
WaiterConfig={"Delay": delay, "MaxAttempts": max_attempts},
RoleName=role_name,
)
_wait_iam_role(client, role_name, wait_timeout)
except botocore.exceptions.WaiterError as e:
module.fail_json_aws(e, msg="Timeout while waiting on IAM role creation")
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
Expand Down Expand Up @@ -532,25 +535,35 @@ def create_or_update_role(module, client):
module.exit_json(changed=changed, iam_role=camel_role)


def create_instance_profiles(module, client, role_name, path):
# Fetch existing Profiles
def list_instance_profiles_for_role(module, client, name):
try:
instance_profiles = client.list_instance_profiles_for_role(RoleName=role_name, aws_retry=True)[
"InstanceProfiles"
]
return client.list_instance_profiles_for_role(RoleName=name, aws_retry=True)["InstanceProfiles"]
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg=f"Unable to list instance profiles for role {role_name}")
module.fail_json_aws(e, msg=f"Unable to list instance profiles for role {name}")

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

if module.check_mode:
return True
def delete_instance_profile(module, client, name):
try:
client.delete_instance_profile(InstanceProfileName=name, aws_retry=True)
except is_boto3_error_code("NoSuchEntityException"):
pass
except (
botocore.exceptions.ClientError,
botocore.exceptions.BotoCoreError,
) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg=f"Unable to remove instance profile {name}")

# Make sure an instance profile is created

def remove_role_from_instance_profile(module, client, role_name, profile_name):
try:
client.remove_role_from_instance_profile(aws_retry=True, InstanceProfileName=profile_name, RoleName=role_name)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg=f"Unable to remove role {role_name} from instance profile {profile_name}")


def create_instance_profile(module, client, name, path):
try:
client.create_instance_profile(InstanceProfileName=role_name, Path=path, aws_retry=True)
client.create_instance_profile(InstanceProfileName=name, Path=path, aws_retry=True)
except is_boto3_error_code("EntityAlreadyExists"):
# If the profile already exists, no problem, move on.
# Implies someone's changing things at the same time...
Expand All @@ -559,48 +572,48 @@ def create_instance_profiles(module, client, role_name, path):
botocore.exceptions.ClientError,
botocore.exceptions.BotoCoreError,
) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg=f"Unable to create instance profile for role {role_name}")
module.fail_json_aws(e, msg=f"Unable to create instance profile for role {name}")

# And attach the role to the profile

def add_role_to_instance_profile(module, client, name):
try:
client.add_role_to_instance_profile(InstanceProfileName=role_name, RoleName=role_name, aws_retry=True)
client.add_role_to_instance_profile(InstanceProfileName=name, RoleName=name, aws_retry=True)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg=f"Unable to attach role {role_name} to instance profile {role_name}")
module.fail_json_aws(e, msg=f"Unable to attach role {name} to instance profile {name}")

return True

def create_instance_profiles(module, client, role_name, path):
# Fetch existing Profiles
instance_profiles = list_instance_profiles_for_role(module, client, role_name)

def remove_instance_profiles(module, client, role_name):
delete_profiles = module.params.get("delete_instance_profile")
# Profile already exists
if any(p["InstanceProfileName"] == role_name for p in instance_profiles):
return False

try:
instance_profiles = client.list_instance_profiles_for_role(aws_retry=True, RoleName=role_name)[
"InstanceProfiles"
]
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg=f"Unable to list instance profiles for role {role_name}")
if module.check_mode:
return True

# Remove the role from the instance profile(s)
for profile in instance_profiles:
profile_name = profile["InstanceProfileName"]
try:
if not module.check_mode:
client.remove_role_from_instance_profile(
aws_retry=True, InstanceProfileName=profile_name, RoleName=role_name
)
if profile_name == role_name:
if delete_profiles:
try:
client.delete_instance_profile(InstanceProfileName=profile_name, aws_retry=True)
except is_boto3_error_code("NoSuchEntityException"):
pass
except (
botocore.exceptions.ClientError,
botocore.exceptions.BotoCoreError,
) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg=f"Unable to remove instance profile {profile_name}")
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg=f"Unable to remove role {role_name} from instance profile {profile_name}")
# Make sure an instance profile is created
create_instance_profile(module, client, role_name, path)

# And attach the role to the profile
add_role_to_instance_profile(module, client, role_name)

return True


def remove_instance_profiles(module, client, role_name):
if not module.check_mode:
delete_profiles = module.params.get("delete_instance_profile")
instance_profiles = list_instance_profiles_for_role(module, client, role_name)

# Remove the role from the instance profile(s)
for profile in instance_profiles:
profile_name = profile["InstanceProfileName"]
remove_role_from_instance_profile(module, client, role_name, profile_name)
if profile_name == role_name:
if delete_profiles:
delete_instance_profile(module, client, profile_name)


def destroy_role(module, client):
Expand Down
33 changes: 19 additions & 14 deletions plugins/modules/iam_role_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
DOCUMENTATION = r"""
---
module: iam_role_info
version_added: 1.0.0
version_added: 7.0.0
short_description: Gather information on IAM roles
description:
- Gathers information about IAM roles.
Expand Down Expand Up @@ -66,7 +66,6 @@
description: The policy document describing what can assume the role.
returned: always
type: dict
version_added: 5.3.0
create_date:
description: Date IAM role was created.
returned: always
Expand Down Expand Up @@ -187,6 +186,18 @@ def list_iam_instance_profiles_for_role_with_backoff(client, role_name):
return paginator.paginate(RoleName=role_name).build_full_result()["InstanceProfiles"]


def get_role(module, client, name):
try:
return [client.get_role(RoleName=name, aws_retry=True)["Role"]]
except is_boto3_error_code("NoSuchEntity"):
return []
except (
botocore.exceptions.ClientError,
botocore.exceptions.BotoCoreError,
) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg=f"Couldn't get IAM role {name}")


def describe_iam_role(module, client, role):
name = role["RoleName"]
try:
Expand All @@ -213,15 +224,7 @@ def describe_iam_roles(module, client):
name = module.params["name"]
path_prefix = module.params["path_prefix"]
if name:
try:
roles = [client.get_role(RoleName=name, aws_retry=True)["Role"]]
except is_boto3_error_code("NoSuchEntity"):
return []
except (
botocore.exceptions.ClientError,
botocore.exceptions.BotoCoreError,
) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg=f"Couldn't get IAM role {name}")
roles = get_role(module, client, name)
else:
params = dict()
if path_prefix:
Expand All @@ -240,16 +243,18 @@ def describe_iam_roles(module, client):
def normalize_profile(profile):
new_profile = camel_dict_to_snake_dict(profile)
if profile.get("Roles"):
profile["roles"] = [normalize_role(role) for role in profile.get("Roles")]
new_profile["roles"] = [normalize_role(role) for role in profile.get("Roles")]
del new_profile["Roles"]
return new_profile


def normalize_role(role):
new_role = camel_dict_to_snake_dict(role, ignore_list=["tags", "AssumeRolePolicyDocument"])
new_role["assume_role_policy_document"] = role.get("AssumeRolePolicyDocument", {})
new_role["assume_role_policy_document"] = role.pop("AssumeRolePolicyDocument", {})
new_role["assume_role_policy_document_raw"] = new_role["assume_role_policy_document"]
if role.get("InstanceProfiles"):
role["instance_profiles"] = [normalize_profile(profile) for profile in role.get("InstanceProfiles")]
new_role["instance_profiles"] = [normalize_profile(profile) for profile in role.get("InstanceProfiles")]
del new_role["InstanceProfiles"]
return new_role


Expand Down
Loading

0 comments on commit bee50d8

Please sign in to comment.