def lambda_handler()

in src/securityhub_enabler.py [0:0]


def lambda_handler(event, context):
    LOGGER.info(f"REQUEST RECEIVED: {json.dumps(event, default=str)}")
    partition = context.invoked_function_arn.split(":")[1]
    admin_account_id = os.environ['sh_admin_account']
    admin_session = assume_role(admin_account_id, os.environ['assume_role'])
    # Regions to Deploy
    if os.environ['region_filter'] == 'SecurityHub':
        securityhub_regions = get_enabled_regions(
            session, session.get_available_regions('securityhub'))
    else:
        securityhub_regions = get_ct_regions(session)
    # Check for Custom Resource Call
    if 'RequestType' in event and (
            event['RequestType'] == "Delete" or
            event['RequestType'] == "Create" or
            event['RequestType'] == "Update"):
        action = event['RequestType']
        if action == "Create":
            enable_admin(admin_session, securityhub_regions, partition)
        if action == "Delete":
            disable_admin(
                admin_session,
                os.environ['assume_role'],
                securityhub_regions,
                partition)
        LOGGER.info(f"Sending Custom Resource Response")
        response_data = {}
        send(event, context, "SUCCESS", response_data)
        if action == "Delete":
            # Exit on delete so it doesn't re-enable existing accounts
            raise SystemExit()
    else:
        action = 'Create'
    LOGGER.info(f"Enabling SecurityHub in Regions: {securityhub_regions}")
    aws_account_dict = dict()
    # Checks if Function was called by SNS
    if 'Records' in event:
        message = event['Records'][0]['Sns']['Message']
        json_message = json.loads(message)
        LOGGER.info(f"SNS message: {json.dumps(json_message, default=str)}")
        accountid = json_message['AccountId']
        email = json_message['Email']
        aws_account_dict.update({accountid: email})
        action = json_message['Action']
    # Checks if function triggered by Control Tower Lifecycle Event,
    # testing in multiple steps to ensure invalid values
    # short-circuit it instead of failing
    elif ('detail' in event) and (
        'eventName' in event['detail']) and (
            event['detail']['eventName'] == 'CreateManagedAccount'):
        service_detail = event['detail']['serviceEventDetails']
        status = service_detail['createManagedAccountStatus']
        LOGGER.info(f"Control Tower Event: CreateManagedAccount {status}")
        accountid = status['account']['accountId']
        email = session.client('organizations').describe_account(
            AccountId=accountid)['Account']['Email']
        aws_account_dict.update({accountid: email})
    else:
        # Not called by SNS or CloudFormation event, iterates through list of
        # accounts and recursively calls the function itself via SNS. SNS is
        # used to fan out the requests to avoid function timeout if too many
        # accounts
        aws_account_dict = get_account_list()
        sns_client = session.client('sns', region_name=os.environ['AWS_REGION'])
        for accountid, email in aws_account_dict.items():
            sns_message = {
                'AccountId': accountid,
                'Email': email,
                'Action': action
            }
            LOGGER.info(f"Publishing to configure Account {accountid}")
            sns_client.publish(
                TopicArn=os.environ['topic'], Message=json.dumps(sns_message))
        return
    # Ensure the Security Hub Admin is still enabled
    enable_admin(admin_session, securityhub_regions, partition)
    # Processing Accounts
    LOGGER.info(f"Processing: {json.dumps(aws_account_dict)}")
    for account in aws_account_dict.keys():
        email_address = aws_account_dict[account]
        if account == admin_account_id:
            LOGGER.info(f"Account {account} cannot become a member of itself")
            continue
        LOGGER.debug(f"Working on SecurityHub on Account {account} in \
                     regions %{securityhub_regions}")
        failed_invitations = []
        member_session = assume_role(account, os.environ['assume_role'])
        # Process Regions
        for aws_region in securityhub_regions:
            sh_member_client = member_session.client(
                'securityhub',
                endpoint_url=f"https://securityhub.{aws_region}.amazonaws.com",
                region_name=aws_region
                )
            sh_admin_client = admin_session.client(
                'securityhub',
                endpoint_url=f"https://securityhub.{aws_region}.amazonaws.com",
                region_name=aws_region
                )
            admin_members = get_admin_members(admin_session, aws_region)
            LOGGER.info(f"Beginning {aws_region} in Account {account}")
            if account in admin_members:
                if admin_members[account] == 'Associated':
                    LOGGER.info(f"Account {account} is already associated "
                                f"with Admin Account {admin_account_id} in "
                                f"{aws_region}")
                    if action == 'Delete':
                        try:
                            sh_admin_client.disassociate_members(
                                AccountIds=[account])
                        except Exception as e:
                            continue
                        try:
                            sh_admin_client.delete_members(
                                AccountIds=[account])
                        except Exception as e:
                            continue
                else:
                    LOGGER.warning(f"Account {account} exists, but not "
                                   f"associated to Admin Account "
                                   f"{admin_account_id} in {aws_region}")
                    LOGGER.info(f"Disassociating Account {account} from "
                                f"Admin Account {admin_account_id} in "
                                f"{aws_region}")
                    try:
                        sh_admin_client.disassociate_members(
                            AccountIds=[account])
                    except Exception as e:
                        continue
                    try:
                        sh_admin_client.delete_members(
                            AccountIds=[account])
                    except Exception as e:
                        continue
            try:
                sh_member_client.get_findings()
            except Exception as e:
                LOGGER.debug(str(e))
                LOGGER.info(f"SecurityHub not currently Enabled on Account "
                            f"{account} in {aws_region}")
                if action != 'Delete':
                    LOGGER.info(f"Enabled SecurityHub on Account {account} "
                                f"in {aws_region}")
                    sh_member_client.enable_security_hub()
            else:
                # Security Hub already enabled
                if action != 'Delete':
                    LOGGER.info(f"SecurityHub already Enabled in Account "
                                f"{account} in {aws_region}")
                else:
                    LOGGER.info(f"Disabled SecurityHub in Account "
                                f"{account} in {aws_region}")
                    try:
                        sh_member_client.disable_security_hub()
                    except Exception as e:
                        continue
            if action != 'Delete':
                process_security_standards(sh_member_client, partition,
                                           aws_region, account)
                LOGGER.info(f"Creating member for Account {account} and "
                            f"Email, {email_address} in {aws_region}")
                member_response = sh_admin_client.create_members(
                    AccountDetails=[{
                        'AccountId': account,
                        'Email': email_address
                    }])
                if len(member_response['UnprocessedAccounts']) > 0:
                    LOGGER.warning(f"Could not create member Account "
                                   f"{account} in {aws_region}")
                    failed_invitations.append({
                        'AccountId': account, 'Region': aws_region
                    })
                    continue
                LOGGER.info(f"Inviting Account {account} in {aws_region}")
                sh_admin_client.invite_members(AccountIds=[account])
            # go through each invitation (hopefully only 1)
            # and pull the one matching the Security Admin Account ID
            try:
                paginator = sh_member_client.get_paginator(
                    'list_invitations')
                invitation_iterator = paginator.paginate()
                for invitation in invitation_iterator:
                    admin_invitation = next(
                        item for item in invitation['Invitations'] if
                        item["AccountId"] == admin_account_id)
                LOGGER.info(f"Accepting invitation on Account {account} "
                            f"from Admin Account {admin_account_id} in "
                            f"{aws_region}")
                sh_member_client.accept_administrator_invitation(
                    AdministratorId=admin_account_id,
                    InvitationId=admin_invitation['InvitationId'])
            except Exception as e:
                LOGGER.warning(f"Account {account} could not accept "
                               f"invitation from Admin Account "
                               f"{admin_account_id} in {aws_region}")
                LOGGER.warning(e)

        if len(failed_invitations) > 0:
            failed_accounts = json.dumps(failed_invitations,
                                         sort_keys=True, default=str)
            LOGGER.warning(f"Error Processing the following Accounts: "
                           f"{failed_accounts}")