コード例 #1
0
class Policy(Construct):
    def __init__(self, scope: Construct, id: str) -> None:
        super().__init__(scope, id)

        policy = ManagedPolicy(self, "Policy")
        # (de)registerTaskDefinitions doesn't support specific resources
        ecs_task = PolicyStatement(
            actions=[
                "ecs:DeregisterTaskDefinition",
                "ecs:RegisterTaskDefinition",
            ],
            resources=["*"],
        )
        # ListTagsForResource cannot be set for a service only, but has to be
        # on the cluster (despite it only looking at the tags for the service).
        # We make a separate statement for this, to avoid giving other polcies
        # more rights than required, as they can be per service.
        self._cluster_statement = PolicyStatement(actions=[
            "ecs:ListTagsForResource",
        ], )
        # All other actions can be combined, as they don't collide. As policies
        # have a maximum amount of bytes they can consume, this spares a few of
        # them.
        self._statement = PolicyStatement(actions=[
            "iam:PassRole",
            "ssm:GetParameter",
            "ssm:GetParameters",
            "ssm:PutParameter",
            "ecs:UpdateService",
            "ecs:DescribeServices",
            "cloudformation:UpdateStack",
            "cloudformation:DescribeStacks",
        ], )

        policy.add_statements(ecs_task)
        policy.add_statements(self._cluster_statement)
        policy.add_statements(self._statement)

    def add_role(self, role: IRole) -> None:
        self._statement.add_resources(role.role_arn)

    def add_parameter(self, parameter: IParameter) -> None:
        self._statement.add_resources(parameter.parameter_arn)

    def add_service(self, service: IService) -> None:
        self._statement.add_resources(service.service_arn)

    def add_cluster(self, cluster: ICluster) -> None:
        self._cluster_statement.add_resources(cluster.cluster_arn)

    def add_stack(self, stack: Stack) -> None:
        self._statement.add_resources(stack.stack_id)
コード例 #2
0
    def create_event_handling(
        self,
        secrets: List[secretsmanager.Secret],
        slack_host_ssm_name: str,
        slack_webhook_ssm_name: str,
    ) -> lambda_.Function:
        """

        Args:
            secrets: a list of secrets that we will track for events
            slack_host_ssm_name: the SSM parameter name for the slack host
            slack_webhook_ssm_name: the SSM parameter name for the slack webhook id

        Returns:
            a lambda event handler
        """
        dirname = os.path.dirname(__file__)
        filename = os.path.join(dirname, "runtime/notify_slack")

        env = {
            # for the moment we don't parametrise at the CDK level.. only needed if this is liable to change
            "SLACK_HOST_SSM_NAME": slack_host_ssm_name,
            "SLACK_WEBHOOK_SSM_NAME": slack_webhook_ssm_name,
        }

        notifier = lambda_.Function(
            self,
            "NotifySlack",
            runtime=lambda_.Runtime.PYTHON_3_8,
            code=lambda_.AssetCode(filename),
            handler="lambda_entrypoint.main",
            timeout=Duration.minutes(1),
            environment=env,
        )

        get_ssm_policy = PolicyStatement()

        # there is some weirdness around SSM parameter ARN formation and leading slashes.. can't be bothered
        # looking into right now - as the ones we want to use do a have a leading slash
        # but put in this exception in case
        if not slack_webhook_ssm_name.startswith(
                "/") or not slack_host_ssm_name.startswith("/"):
            raise Exception(
                "SSM parameters need to start with a leading slash")

        # see here - the *required* slash between parameter and the actual name uses the leading slash from the actual
        # name itself.. which is wrong..
        get_ssm_policy.add_resources(
            f"arn:aws:ssm:*:*:parameter{slack_host_ssm_name}")
        get_ssm_policy.add_resources(
            f"arn:aws:ssm:*:*:parameter{slack_webhook_ssm_name}")
        get_ssm_policy.add_actions("ssm:GetParameter")

        notifier.add_to_role_policy(get_ssm_policy)

        # we want a rule that traps all the rotation failures for our JWT secrets
        rule = Rule(
            self,
            "NotifySlackRule",
        )

        rule.add_event_pattern(
            source=["aws.secretsmanager"],
            detail={
                # at the moment only interested in these - add extra events into this array if wanting more
                "eventName": ["RotationFailed", "RotationSucceeded"],
                "additionalEventData": {
                    "SecretId": list(map(lambda s: s.secret_arn, secrets))
                },
            },
        )

        rule.add_target(LambdaFunction(notifier))

        return notifier