# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

import boto3
import json
import os
import logging

logging.basicConfig()

logger = logging.getLogger("lambda:dashboard_connection")
logger.setLevel(os.getenv("LOGLEVEL", "INFO"))


ssm_client = boto3.client("ssm")
event_client = boto3.client("events")


def flatten_parameters(parameters):
    """This takes a hierarchy of parameters as obtained from the SSM parameter store,
    and flattens it in simple key/value pairs {"AccountID1": "ProjectName1", ...}

    Args:
        parameters (dict): the parameters obtained from the SSM

    Returns:
        [dict]: a flat dictionary with account ids and project names
    """

    accounts = {}

    for p in parameters:
        p_account = p["Value"]
        accounts[p_account] = {}
        # separator for values is ;
        p_name = p["Name"].split("/")[2]
        accounts[p_account] = p_name

    return accounts


def allow_event_puts(account_id):
    """allows event puts from the account_id

    Args:
        account_id (str): the AWS account id
    """

    return event_client.put_permission(
        EventBusName="default",
        Action="events:PutEvents",
        Principal=account_id,
        StatementId=account_id,
    )


def forward_events(account_id, pattern, name_tag):
    """This creates an EventBridge rule to forward events to the target account id

    Args:
        account_id (str): The target AWS account id
    """

    put_rule_response = event_client.put_rule(
        Name=f"forwardTo{account_id}{name_tag}",
        EventPattern=json.dumps(pattern),
        State="ENABLED",
        Description=f"forwardTo{account_id}{name_tag}",
    )

    if put_rule_response["ResponseMetadata"]["HTTPStatusCode"] not in [200, 204]:
        return put_rule_response

    logger.debug("Response from PutRule:")
    logger.debug(put_rule_response)

    bus_arn = f'arn:aws:events:{os.getenv("AWS_REGION", "eu-west-1")}:{account_id}:event-bus/default'

    target = {"Arn": bus_arn, "Id": f"{account_id}-bus"}

    put_target_response = event_client.put_targets(
        Rule=f"forwardTo{account_id}{name_tag}", Targets=[target]
    )

    logger.debug("Response from PutTargets:")
    logger.debug(put_target_response)

    return put_target_response


def lambda_handler(event, context):
    """This is the main handler. It will be called with a payload
    specifying if it needs to configure the eventbus resource policy
    or if it has to create rolues to forward events

    Its task is to properly configure the source/destination of metrics-related
    events, using info in the parameter store.

    Example structure of the parameter store

    Name=/monitors/MonitorName Value=123456789123
    Name=/monitored_projects/ProjectName/CustomTag Value=987654321987

    CustomTag can be used when a project has more than one account (e.g. dev/int/prod)

    Args:
        event (dict): the payload received for this execution
        context: the execution context
    """

    logger.info("Starting invocation with payload:")
    logger.info(json.dumps(event))

    eb_put = event["action"] == "EBPut"

    monitors = ssm_client.get_parameters_by_path(Path="/monitors/", Recursive=True)[
        "Parameters"
    ]
    monitored_projects = ssm_client.get_parameters_by_path(
        Path="/monitored_projects/", Recursive=True
    )["Parameters"]

    monitors = flatten_parameters(monitors)
    monitored_projects = flatten_parameters(monitored_projects)

    # this means we are serving as monitored_project
    for account_id, project_name in monitors.items():

        logger.info(f"Connecting to monitor {project_name} in account {account_id}")

        event_pattern = {
            "source": ["metric_extractor"],
            "detail-type": ["metric_extractor"],
        }

        if eb_put:
            # allow monitor to send us events
            allow_event_puts(account_id)
        else:
            # forward our metrics to monitor
            forward_events(account_id, event_pattern, "MetricValues")

    # this means we are serving as monitor
    for account_id, project_name in monitored_projects.items():

        logger.info(
            f"Connecting to monitored project {project_name} in account {account_id}"
        )

        event_pattern = {
            "source": ["metric_fetch"],
            "detail-type": ["metric_fetch"],
        }

        if eb_put:
            # allow monitored_project to send us events
            allow_event_puts(account_id)
        else:
            # send requests to fetch new data to monitored_projects
            forward_events(account_id, event_pattern, "FetchData")

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

import boto3
import json
import os
import logging

logging.basicConfig()

logger = logging.getLogger("lambda:dashboard_connection")
logger.setLevel(os.getenv("LOGLEVEL", "INFO"))


ssm_client = boto3.client("ssm")
event_client = boto3.client("events")


def flatten_parameters(parameters):
    """This takes a hierarchy of parameters as obtained from the SSM parameter store,
    and flattens it in simple key/value pairs {"AccountID1": "ProjectName1", ...}

    Args:
        parameters (dict): the parameters obtained from the SSM

    Returns:
        [dict]: a flat dictionary with account ids and project names
    """

    accounts = {}

    for p in parameters:
        p_account = p["Value"]
        accounts[p_account] = {}
        # separator for values is ;
        p_name = p["Name"].split("/")[2]
        accounts[p_account] = p_name

    return accounts


def allow_event_puts(account_id):
    """allows event puts from the account_id

    Args:
        account_id (str): the AWS account id
    """

    return event_client.put_permission(
        EventBusName="default",
        Action="events:PutEvents",
        Principal=account_id,
        StatementId=account_id,
    )


def forward_events(account_id, pattern, name_tag):
    """This creates an EventBridge rule to forward events to the target account id

    Args:
        account_id (str): The target AWS account id
    """

    put_rule_response = event_client.put_rule(
        Name=f"forwardTo{account_id}{name_tag}",
        EventPattern=json.dumps(pattern),
        State="ENABLED",
        Description=f"forwardTo{account_id}{name_tag}",
    )

    if put_rule_response["ResponseMetadata"]["HTTPStatusCode"] not in [200, 204]:
        return put_rule_response

    logger.debug("Response from PutRule:")
    logger.debug(put_rule_response)

    bus_arn = f'arn:aws:events:{os.getenv("AWS_REGION", "eu-west-1")}:{account_id}:event-bus/default'

    target = {"Arn": bus_arn, "Id": f"{account_id}-bus"}

    put_target_response = event_client.put_targets(
        Rule=f"forwardTo{account_id}{name_tag}", Targets=[target]
    )

    logger.debug("Response from PutTargets:")
    logger.debug(put_target_response)

    return put_target_response


def lambda_handler(event, context):
    """This is the main handler. It will be called with a payload
    specifying if it needs to configure the eventbus resource policy
    or if it has to create rolues to forward events

    Its task is to properly configure the source/destination of metrics-related
    events, using info in the parameter store.

    Example structure of the parameter store

    Name=/monitors/MonitorName Value=123456789123
    Name=/monitored_projects/ProjectName/CustomTag Value=987654321987

    CustomTag can be used when a project has more than one account (e.g. dev/int/prod)

    Args:
        event (dict): the payload received for this execution
        context: the execution context
    """

    logger.info("Starting invocation with payload:")
    logger.info(json.dumps(event))

    eb_put = event["action"] == "EBPut"

    monitors = ssm_client.get_parameters_by_path(Path="/monitors/", Recursive=True)[
        "Parameters"
    ]
    monitored_projects = ssm_client.get_parameters_by_path(
        Path="/monitored_projects/", Recursive=True
    )["Parameters"]

    monitors = flatten_parameters(monitors)
    monitored_projects = flatten_parameters(monitored_projects)

    # this means we are serving as monitored_project
    for account_id, project_name in monitors.items():

        logger.info(f"Connecting to monitor {project_name} in account {account_id}")

        event_pattern = {
            "source": ["metric_extractor"],
            "detail-type": ["metric_extractor"],
        }

        if eb_put:
            # allow monitor to send us events
            allow_event_puts(account_id)
        else:
            # forward our metrics to monitor
            forward_events(account_id, event_pattern, "MetricValues")

    # this means we are serving as monitor
    for account_id, project_name in monitored_projects.items():

        logger.info(
            f"Connecting to monitored project {project_name} in account {account_id}"
        )

        event_pattern = {
            "source": ["metric_fetch"],
            "detail-type": ["metric_fetch"],
        }

        if eb_put:
            # allow monitored_project to send us events
            allow_event_puts(account_id)
        else:
            # send requests to fetch new data to monitored_projects
            forward_events(account_id, event_pattern, "FetchData")

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 This takes a hierarchy of parameters as obtained from the SSM parameter store,     and flattens it in simple key/value pairs {"AccountID1": "ProjectName1", ...}      Args:         parameters (dict): the parameters obtained from the SSM      Returns:         [dict]: a flat dictionary with account ids and project names # separator for values is ; allows event puts from the account_id      Args:         account_id (str): the AWS account id This creates an EventBridge rule to forward events to the target account id      Args:         account_id (str): The target AWS account id This is the main handler. It will be called with a payload     specifying if it needs to configure the eventbus resource policy     or if it has to create rolues to forward events      Its task is to properly configure the source/destination of metrics-related     events, using info in the parameter store.      Example structure of the parameter store      Name=/monitors/MonitorName Value=123456789123     Name=/monitored_projects/ProjectName/CustomTag Value=987654321987      CustomTag can be used when a project has more than one account (e.g. dev/int/prod)      Args:         event (dict): the payload received for this execution         context: the execution context # this means we are serving as monitored_project # allow monitor to send us events # forward our metrics to monitor # this means we are serving as monitor # allow monitored_project to send us events # send requests to fetch new data to monitored_projects