Source code for idem_aws.states.aws.guardduty.member

"""State module for adding/deleting members from Guardduty."""
import copy
from typing import Any
from typing import Dict

__contracts__ = ["resource"]

TREQ = {
    "absent": {
        "require": ["aws.guardduty.organization_admin_account.absent"],
    },
    "present": {
        "require": [
            "aws.guardduty.detector.present",
            "aws.guardduty.organization_admin_account.present",
        ],
    },
}


[docs]async def present( hub, ctx, name: str, detector_id: str, account_id: str, email: str, resource_id: str = None, ) -> Dict[str, Any]: r"""Add member to AWS Guardduty. Creates a member account under Guardduty by specifying Amazon Web Services account ID for the member account. This step is a prerequisite for managing the associated member accounts either by invitation or through an organization. When using Create Members as an organizations delegated administrator this action will enable GuardDuty in the added member accounts, with the exception of the organization delegated administrator account, which must enable GuardDuty prior to being added as a member. If you are adding accounts by invitation use this action after GuardDuty has been enabled in potential member accounts and before using Invite Members . Args: name(str): An Idem name of the resource. detector_id(str): AWS Guardduty detector ID account_id(str): The member account ID. email(str): The email address of the member account. resource_id(str, Optional): An identifier refers to an existing resource. The format is <detector_id>:<account_id> Request Syntax: .. code-block:: sls [idem_test_aws_guardduty_member]: aws.guardduty.member.present: - name: 'string' - detector_id: 'string' - account_id: 'string' - email: 'string' - resource_id: 'string' Returns: Dict[str, Any] Examples: .. code-block:: sls resource_is_present: aws.guardduty.member.present: - name: 'create_members' - detector_id: "68c25425ab84ea0dcae26311eddacd34" - resource_id: "68c25425ab84ea0dcae26311eddacd34:496603212238" - account_id: "496603212238" - email: "xyz@vmware.com" """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) update_needed = False revised_account_details = [] # pre-processing for create_members call entry = {"AccountId": account_id, "Email": email} revised_account_details.append(entry) before = await hub.exec.aws.guardduty.member.get( ctx, detector_id=detector_id, account_id=account_id, resource_id=resource_id, name=name, ) if not before["result"]: result["result"] = before["result"] result["comment"] = before["comment"] return result if not before["ret"]: # create new resource update_needed = True else: # check if existing member needs to be added member_details = before["ret"] if member_details.get("relationship_status") not in ("Created", "Enabled"): update_needed = True if update_needed: if ctx.get("test", False): result["new_state"] = hub.tool.aws.test_state_utils.generate_test_state( enforced_state={}, desired_state={ "name": name, "resource_id": resource_id, "detector_id": detector_id, "account_id": account_id, "email": email, }, ) result["comment"] = hub.tool.aws.comment_utils.would_create_comment( resource_type="aws.guardduty.member", name=name ) return result ret = await hub.exec.boto3.client.guardduty.create_members( ctx, DetectorId=detector_id, AccountDetails=revised_account_details ) result["result"] = ret["result"] if not result["result"]: result["comment"] = ret["comment"] return result result["comment"] = hub.tool.aws.comment_utils.create_comment( resource_type="aws.guardduty.member", name=name ) resource_id = detector_id + ":" + account_id else: result["old_state"] = before["ret"] result["comment"] = hub.tool.aws.comment_utils.already_exists_comment( resource_type="aws.guardduty.member", name=name ) if update_needed: after = await hub.exec.aws.guardduty.member.get( ctx, detector_id=detector_id, account_id=account_id, resource_id=resource_id, name=name, ) if not after["result"]: result["result"] = after["result"] result["comment"] += after["comment"] return result result["new_state"] = after["ret"] else: result["new_state"] = copy.deepcopy(result["old_state"]) return result
[docs]async def absent( hub, ctx, name: str, detector_id: str = None, account_id: str = None, resource_id: str = None, ) -> Dict[str, Any]: r"""Delete member from AWS Guardduty. Deletes GuardDuty member account specified by the account ID. Args: name(str): An Idem name of the resource. detector_id(str, Optional): AWS Guardduty Detector id account_id(str, Optional): AWS Guardduty member account id resource_id(str, Optional): An identifier refers to an existing resource. The format is <detector_id>:<account_id> Request Syntax: .. code-block:: sls [idem_test_aws_guardduty_member]: aws.guardduty.member.absent: - name: 'string' - detector_id: 'string' - account_id: 'string' - resource_id: 'string' Returns: Dict[str, Any] Examples: .. code-block:: sls resource_is_absent: aws.guardduty.member.absent: - name: test_delete_members - detector_id: '68c25425ab84ea0dcae26311eddacd34' - account_id: '106828723025' - resource_id: '68c25425ab84ea0dcae26311eddacd34:106828723025' """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) if not resource_id: result["comment"] = hub.tool.aws.comment_utils.already_absent_comment( resource_type="aws.guardduty.member", name=name ) return result if not detector_id or not account_id: result["comment"] = hub.tool.aws.comment_utils.missing_args_for_absent_comment( resource_type="aws.guardduty.member", name=name, args=["detector_id", "account_id"], ) result["result"] = False return result before = await hub.exec.aws.guardduty.member.get( ctx, detector_id=detector_id, account_id=account_id, resource_id=resource_id, name=name, ) if not before["result"]: result["result"] = before["result"] result["comment"] = before["comment"] return result if not before["ret"]: result["comment"] = hub.tool.aws.comment_utils.already_absent_comment( resource_type="aws.guardduty.member", name=name ) elif ctx.get("test", False): result["old_state"] = before["ret"] result["comment"] = hub.tool.aws.comment_utils.would_delete_comment( resource_type="aws.guardduty.member", name=name ) return result else: account_ids = [account_id] result["old_state"] = before["ret"] try: ret = await hub.exec.boto3.client.guardduty.delete_members( ctx, DetectorId=detector_id, AccountIds=account_ids ) result["result"] = ret["result"] if not result["result"]: result["comment"] = ret["comment"] return result result["comment"] = hub.tool.aws.comment_utils.delete_comment( resource_type="aws.guardduty.member", name=name ) 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]]: r"""Describe the resource in a way that can be recreated/managed with the corresponding "present" function. List details about all member accounts for the current GuardDuty administrator account. Describe returns detector_id, master_id, relationship_status, invited_at, updated_at, administrator_id as additional params not used in present input Returns: Dict[str, Dict[str, Any]] Examples: .. code-block:: bash $ idem describe aws.guardduty.member """ result = {} ret = await hub.exec.boto3.client.guardduty.list_detectors(ctx) if not ret["ret"]["DetectorIds"]: hub.log.warning(f"Could not list detector {ret['comment']}") return {} for detector_id in ret["ret"]["DetectorIds"]: members = await hub.exec.aws.guardduty.member.list( ctx, detector_id=detector_id, name=f"Describe members for detector {detector_id}", ) if not members["result"]: hub.log.debug(f"Could not find members for detector {detector_id}") continue for member in members["ret"]: account_id = member.get("account_id") resource_id = detector_id + ":" + account_id # to avoid any other state overriding describe if just resource_id is the state_id resource_key = f"aws-guardduty-member-{resource_id}" result[resource_key] = { "aws.guardduty.member.present": [ {parameter_key: parameter_value} for parameter_key, parameter_value in member.items() ] } return result