Source code for idem_aws.states.aws.iam.role_policy_attachment

"""State module for managing IAM Role Policy Attachments."""
import copy
from typing import Any
from typing import Dict

__contracts__ = ["resource"]


[docs]async def present( hub, ctx, name: str, role_name: str, policy_arn: str, resource_id: str = None ) -> Dict[str, Any]: """Attaches the specified managed policy to the specified IAM role. When you attach a managed policy to a role, the managed policy becomes part of the role's permission (access) policy. Args: name(str): A name to represent the operation. This name is only for logging purpose. It is not used to attach a policy to a role. role_name(str): The name (friendly name, not ARN) of the role to attach a policy. This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@- policy_arn(str): The Amazon Resource Name (ARN) of the IAM policy you want to attach. resource_id(str, Optional): The identifier for this object Request Syntax: .. code-block:: sls [iam-attach-role-policy-name]: aws.iam.role_policy_attachment.present: - resource_id: 'string' - role_name: 'string' - policy_arn: 'string' Returns: Dict[str, Any] Examples: .. code-block:: sls idem-test-policy-temp-name: aws.iam.role_policy_attachment.present: - role_name: idem-test-role-name - policy_arn: arn:aws:iam::aws:policy/ReadOnlyAccess """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) try: before_ret = ( await hub.tool.aws.iam.role_policy_attachment.is_role_policy_attached( ctx, role_name=role_name, policy_arn=policy_arn ) ) except hub.tool.boto3.exception.ClientError as e: result["comment"] += [f"{e.__class__.__name__}: {e}"] result["result"] = False return result # If before_ret has False result and has an error message in comment, then immediately return with the error, # If role does not exists - NoSuchEntityException is in the comment, do not fail in 'test' mode. if before_ret["result"] is False and before_ret["comment"]: if "NoSuchEntityException" in before_ret["comment"][0] and ctx.get( "test", False ): result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state( enforced_state={}, desired_state={ "role_name": role_name, "policy_arn": policy_arn, }, ) result["comment"] = ( f"Would create aws.iam.role_policy_attachment. \ Role {role_name}, policy {policy_arn}.", ) return result result["result"] = False result["comment"] = before_ret["comment"] return result if before_ret["result"]: result[ "old_state" ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_policy_attachment_to_present( role_name, policy_arn ) result["new_state"] = copy.deepcopy(result["old_state"]) result["comment"] += [f"aws.iam.role_policy_attachment '{name}' already exists"] else: if ctx.get("test", False): result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state( enforced_state={}, desired_state={ "role_name": role_name, "policy_arn": policy_arn, }, ) result["comment"] = ( f"Would create aws.iam.role_policy_attachment. \ Role {role_name}, policy {policy_arn}.", ) return result try: ret = await hub.exec.boto3.client.iam.attach_role_policy( ctx, RoleName=role_name, PolicyArn=policy_arn, ) result["result"] = ret["result"] if not result["result"]: result["comment"] = ret["comment"] return result result["comment"] += [f"Created aws.iam.role_policy_attachment '{name}'"] result[ "new_state" ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_policy_attachment_to_present( role_name, policy_arn ) except hub.tool.boto3.exception.ClientError as e: result["comment"] += [f"{e.__class__.__name__}: {e}"] result["result"] = False return result
[docs]async def absent( hub, ctx, name: str, role_name: str = None, policy_arn: str = None ) -> Dict[str, Any]: """Removes the specified managed policy from the specified role. Args: name(str): The name of the AWS IAM role policy. role_name(str, Optional): The name (friendly name, not ARN) of the role to attach a policy. This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: _+=,.@- policy_arn(str, Optional): The Amazon Resource Name (ARN) of the IAM policy you want to attach. Request Syntax: .. code-block:: sls [rpa-resource-id]: aws.iam.role_policy_attachment.absent: - name: "string" - role_name: "string" - policy_arn: "string" Returns: Dict[str, Any] Examples: .. code-block:: sls resource_is_absent: aws.iam.role_policy_attachment.absent: - name: value - role_name: value - policy_arn: value """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) if not role_name and not policy_arn: result["comment"] = hub.tool.aws.comment_utils.already_absent_comment( resource_type="aws.iam.role_policy_attachment", name=name ) return result try: before_ret = ( await hub.tool.aws.iam.role_policy_attachment.is_role_policy_attached( ctx, role_name=role_name, policy_arn=policy_arn ) ) except hub.tool.boto3.exception.ClientError as e: result["comment"] += [f"{e.__class__.__name__}: {e}"] result["result"] = False return result if before_ret["result"] is False and before_ret["comment"]: if "NoSuchEntityException" in before_ret["comment"][0]: result["comment"] += [ f"aws.iam.role_policy_attachment '{name}' already absent" ] else: result["result"] = False result["comment"] = before_ret["comment"] return result if not before_ret["result"]: result["comment"] += [f"aws.iam.role_policy_attachment '{name}' already absent"] else: try: result[ "old_state" ] = hub.tool.aws.iam.conversion_utils.convert_raw_role_policy_attachment_to_present( role_name, policy_arn ) if ctx.get("test", False): result["comment"] = ( f"Would detach aws.iam.role_policy_attachment. Role '{role_name}', policy '{policy_arn}'", ) return result ret = await hub.exec.boto3.client.iam.detach_role_policy( ctx, RoleName=role_name, PolicyArn=policy_arn ) result["result"] = ret["result"] if not result["result"]: result["comment"] = ret["comment"] result["result"] = False return result result["comment"] = ( f"Detached aws.iam.role_policy_attachment. Role '{role_name}', policy '{policy_arn}'", ) except hub.tool.boto3.exception.ClientError as e: result["comment"] += [f"{e.__class__.__name__}: {e}"] return result
[docs]async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]: """Describe the resource in a way that can be recreated/managed with the corresponding "present" function Lists the names of the attached managed policies of all IAM roles. If there are no managed policies attached to the specified role, the operation returns an empty dict. Returns: Dict[str, Any] Examples: .. code-block:: bash $ idem describe aws.iam.role_policy_attachment """ result = {} # To describe all the attached role policies of all the roles, we first need to list all the roles, then get all the # attached policies ret_roles = await hub.exec.boto3.client.iam.list_roles(ctx) if not ret_roles["result"]: hub.log.warning(f"Could not describe role {ret_roles['comment']}") return {} role_name_list = [role.get("RoleName") for role in ret_roles["ret"]["Roles"]] for role_name in role_name_list: ret_attached_policies = ( await hub.exec.boto3.client.iam.list_attached_role_policies( ctx=ctx, RoleName=role_name ) ) if not ret_attached_policies["result"]: hub.log.warning( f"Could not get attached policy list with role {role_name} with error" f" {ret_attached_policies['comment']} . Describe will skip this role and continue." ) else: resources = ret_attached_policies["ret"].get("AttachedPolicies") for resource in resources: translated_resource = hub.tool.aws.iam.conversion_utils.convert_raw_role_policy_attachment_to_present( role_name, resource.get("PolicyArn") ) resource_id = f"{role_name}-{translated_resource['policy_arn']}" result[resource_id] = { "aws.iam.role_policy_attachment.present": [ {parameter_key: parameter_value} for parameter_key, parameter_value in translated_resource.items() ] } return result