Post

Sharing CloudWatch metrics cross-account (part 1)

Using CDK CLI via Docker container as shown in the previous post.

Define stack in app.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import os

from aws_cdk import aws_iam
import aws_cdk


class CloudWatchSharingStack(aws_cdk.Stack):
    def __init__(self, trusted_account_ids: [str], **kwargs) -> None:
        super().__init__(**kwargs)
        aws_iam.Role(
            scope=self,
            id="Role",
            role_name='CloudWatch-CrossAccountSharingRole',
            assumed_by=aws_iam.CompositePrincipal(
                *(aws_iam.AccountPrincipal(account_id) for account_id in trusted_account_ids)
            ),
            description="A role for sharing CloudWatch metrics across accounts",
            managed_policies=[
                aws_iam.ManagedPolicy.from_aws_managed_policy_name(name) for name in (
                    'CloudWatchReadOnlyAccess',
                    'CloudWatchAutomaticDashboardsAccess',
                    'AWSXrayReadOnlyAccess',
                )
            ],
        )


app = aws_cdk.App()
CloudWatchSharingStack(
    scope=app,
    id="CloudWatchSharingStack",
    stack_name="CloudWatchSharingStack",
    env=aws_cdk.Environment(
        account=os.environ['CDK_DEFAULT_ACCOUNT'],
        region=os.environ['CDK_DEFAULT_REGION'],
    ),
    trusted_account_ids=['123456789012'],
)
app.synth()

and create a Makefile:

1
2
3
4
5
6
7
8
9
10
11
clean:
	rm -rf cdk.out
	rm -rf venv

venv:
	cdk.sh -c 'python -m venv venv && venv/bin/pip install -U pip'
	cdk.sh -c 'venv/bin/pip install  aws-cdk-lib  constructs'
	echo "$$(yq -Moj '.app = "venv/bin/python app.py"' cdk.json)" > cdk.json

synth: venv
	cdk synth | yq -P

The following cdk.json would also work in our case:

1
2
3
{
  "app": "venv/bin/python app.py"
}

Now synth the template:

1
make clean synth

Locking dependencies is a good idea for a more complex or regularly-updated stack, in requirements.txt:

aws-cdk-lib==2.131.0
constructs>=10.0.0,<11.0.0

To make inspections work in PyCharm, one could create a host venv and use it for interpreter

1
2
3
4
5
6
7
venv_host:
	python -m venv venv_host
	venv_host/bin/pip install -U pip
	venv_host/bin/pip install -r requirements.txt
	
...:
	cdk.sh -c 'venv/bin/pip install -r requirements.txt'

Using CDK context to pass a list of trusted accounts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from aws_cdk import aws_iam
import aws_cdk


class CloudWatchSharingStack(aws_cdk.Stack):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        aws_iam.Role(
            scope=self,
            id="Role",
            role_name='CloudWatch-CrossAccountSharingRole',
            assumed_by=aws_iam.CompositePrincipal(
                *(
                    aws_iam.AccountPrincipal(account_id)
                    for account_id in self.node.get_context("TrustedAccountIds").split(',')
                )
            ),
            description="A role for sharing CloudWatch metrics across accounts",
            managed_policies=[
                aws_iam.ManagedPolicy.from_aws_managed_policy_name(name) for name in (
                    'CloudWatchReadOnlyAccess',
                    'CloudWatchAutomaticDashboardsAccess',
                    'AWSXrayReadOnlyAccess',
                )
            ],
        )


app = aws_cdk.App()
CloudWatchSharingStack(
    scope=app,
    id="CloudWatchSharingStack",
    stack_name="CloudWatchSharingStack",
)
app.synth()

Example:

1
cdk synth CloudWatchSharingStack --context TrustedAccountIds="123456789012"

Now, make it deploy:

1
2
3
4
5
6
7
8
9
10
PROFILE = my-profile
TRUSTED = 123,456
CONTEXT = 'TrustedAccountIds=${TRUSTED}'

synth: venv
	rm -rf cdk.out
	cdk synth CloudWatchSharingStack --context ${CONTEXT}

deploy: venv
	aws-vault exec ${PROFILE} -- cdk deploy CloudWatchSharingStack --context ${CONTEXT}
This post is licensed under CC BY 4.0 by the author.