private createInstanceProfile()

in source/lib/msk-client.ts [84:157]


    private createInstanceProfile(clusterName: string): iam.CfnInstanceProfile {
        const role = new iam.Role(this, 'Role', {
            assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com')
        });

        const ssmPolicy = new iam.Policy(this, 'SessionManagerPolicy', {
            statements: [new iam.PolicyStatement({
                resources: ['*'],
                actions: [
                    'ssm:UpdateInstanceInformation',
                    'ssmmessages:CreateControlChannel',
                    'ssmmessages:CreateDataChannel',
                    'ssmmessages:OpenControlChannel',
                    'ssmmessages:OpenDataChannel'
                ]
            })]
        });

        this.addW12Suppression(ssmPolicy, 'Session Manager actions do not support resource level permissions');
        ssmPolicy.attachToRole(role);

        const mskPolicy = new iam.Policy(this, 'MskPolicy', {
            statements: [
                new iam.PolicyStatement({
                    sid: 'ClusterMetadata',
                    resources: ['*'],
                    actions: ['kafka:DescribeCluster', 'kafka:GetBootstrapBrokers']
                }),

                // For topics and groups, the resources contain "/*/*":
                // - The first one refers to the cluster UUID
                // - The second one refers to the topic / group name
                // Since this is meant to be a generic client, we use "*" so that this instance can create any topics or groups.
                new iam.PolicyStatement({
                    sid: 'ClusterAPIs',
                    resources: [
                        // The cluster ARN in MSK contains an UUID, which is only available after the cluster is created.
                        // Example: arn:${Partition}:kafka:${Region}:${Account}:cluster/${ClusterName}/${UUID}
                        `arn:${cdk.Aws.PARTITION}:kafka:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:cluster/${clusterName}/*`,
                        `arn:${cdk.Aws.PARTITION}:kafka:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:group/${clusterName}/*/*`
                    ],
                    actions: [
                        'kafka-cluster:Connect',
                        'kafka-cluster:DescribeCluster',
                        'kafka-cluster:AlterGroup',
                        'kafka-cluster:DescribeGroup',
                        'kafka-cluster:DeleteGroup'
                    ]
                }),
                new iam.PolicyStatement({
                    sid: 'ProducerAPIs',
                    resources: [
                        `arn:${cdk.Aws.PARTITION}:kafka:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:topic/${clusterName}/*/*`
                    ],
                    actions: ['kafka-cluster:*Topic*', 'kafka-cluster:WriteData']
                }),
                new iam.PolicyStatement({
                    sid: 'ConsumerAPIs',
                    resources: [
                        `arn:${cdk.Aws.PARTITION}:kafka:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:topic/${clusterName}/*/*`
                    ],
                    actions: ['kafka-cluster:*Topic*', 'kafka-cluster:ReadData']
                }),
            ]
        });

        this.addW12Suppression(mskPolicy, 'MSK actions do not support resource level permissions');
        mskPolicy.attachToRole(role);

        const cfnRole = role.node.defaultChild as iam.CfnRole;
        return new iam.CfnInstanceProfile(this, 'InstanceProfile', {
            roles: [cfnRole.ref]
        });
    }