From ea27514cd53ba5ae5b37a43d6544d5125ace0c35 Mon Sep 17 00:00:00 2001 From: Alex Jones Date: Mon, 6 Mar 2023 17:49:27 +0000 Subject: [PATCH] fix(rds): add clusterResourceIdentifier property to database cluster (#23605) **Summary** - Exposed the [`DBClusterResourceId`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html#aws-resource-rds-dbcluster-return-values) on the database cluster as `clusterResourceIdentifier`. - This is needed to [create an IAM policy for IAM database access](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html). --- packages/@aws-cdk/aws-rds/lib/cluster-ref.ts | 16 ++ packages/@aws-cdk/aws-rds/lib/cluster.ts | 20 +++ .../@aws-cdk/aws-rds/test/cluster.test.ts | 5 + .../aws-cdk-rds-integ.assets.json | 6 +- .../aws-cdk-rds-integ.template.json | 66 ++++++- .../test/integ.cluster.js.snapshot/cdk.out | 2 +- .../test/integ.cluster.js.snapshot/integ.json | 2 +- .../integ.cluster.js.snapshot/manifest.json | 28 ++- .../test/integ.cluster.js.snapshot/tree.json | 164 +++++++++++++++--- .../@aws-cdk/aws-rds/test/integ.cluster.ts | 17 ++ 10 files changed, 292 insertions(+), 34 deletions(-) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts b/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts index de1f89bd3dafa..c5f5a0155dd2e 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-ref.ts @@ -14,6 +14,13 @@ export interface IDatabaseCluster extends IResource, ec2.IConnectable, secretsma */ readonly clusterIdentifier: string; + /** + * The immutable identifier for the cluster; for example: cluster-ABCD1234EFGH5678IJKL90MNOP. + * + * This AWS Region-unique identifier is used in things like IAM authentication policies. + */ + readonly clusterResourceIdentifier: string; + /** * Identifiers of the replicas */ @@ -57,6 +64,15 @@ export interface DatabaseClusterAttributes { */ readonly clusterIdentifier: string; + /** + * The immutable identifier for the cluster; for example: cluster-ABCD1234EFGH5678IJKL90MNOP. + * + * This AWS Region-unique identifier is used to grant access to the cluster. + * + * @default none + */ + readonly clusterResourceIdentifier?: string; + /** * The database port * diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 16f72e6193b24..af6b42629f803 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -321,6 +321,13 @@ export abstract class DatabaseClusterBase extends Resource implements IDatabaseC */ public abstract readonly clusterIdentifier: string; + /** + * The immutable identifier for the cluster; for example: cluster-ABCD1234EFGH5678IJKL90MNOP. + * + * This AWS Region-unique identifier is used in things like IAM authentication policies. + */ + public abstract readonly clusterResourceIdentifier: string; + /** * Identifiers of the replicas */ @@ -557,6 +564,7 @@ class ImportedDatabaseCluster extends DatabaseClusterBase implements IDatabaseCl public readonly connections: ec2.Connections; public readonly engine?: IClusterEngine; + private readonly _clusterResourceIdentifier?: string; private readonly _clusterEndpoint?: Endpoint; private readonly _clusterReadEndpoint?: Endpoint; private readonly _instanceIdentifiers?: string[]; @@ -566,6 +574,7 @@ class ImportedDatabaseCluster extends DatabaseClusterBase implements IDatabaseCl super(scope, id); this.clusterIdentifier = attrs.clusterIdentifier; + this._clusterResourceIdentifier = attrs.clusterResourceIdentifier; const defaultPort = attrs.port ? ec2.Port.tcp(attrs.port) : undefined; this.connections = new ec2.Connections({ @@ -582,6 +591,13 @@ class ImportedDatabaseCluster extends DatabaseClusterBase implements IDatabaseCl : undefined; } + public get clusterResourceIdentifier() { + if (!this._clusterResourceIdentifier) { + throw new Error('Cannot access `clusterResourceIdentifier` of an imported cluster without a clusterResourceIdentifier'); + } + return this._clusterResourceIdentifier; + } + public get clusterEndpoint() { if (!this._clusterEndpoint) { throw new Error('Cannot access `clusterEndpoint` of an imported cluster without an endpoint address and port'); @@ -637,6 +653,7 @@ export class DatabaseCluster extends DatabaseClusterNew { } public readonly clusterIdentifier: string; + public readonly clusterResourceIdentifier: string; public readonly clusterEndpoint: Endpoint; public readonly clusterReadEndpoint: Endpoint; public readonly connections: ec2.Connections; @@ -662,6 +679,7 @@ export class DatabaseCluster extends DatabaseClusterNew { }); this.clusterIdentifier = cluster.ref; + this.clusterResourceIdentifier = cluster.attrDbClusterResourceId; if (secret) { this.secret = secret.attach(this); @@ -729,6 +747,7 @@ export interface DatabaseClusterFromSnapshotProps extends DatabaseClusterBasePro */ export class DatabaseClusterFromSnapshot extends DatabaseClusterNew { public readonly clusterIdentifier: string; + public readonly clusterResourceIdentifier: string; public readonly clusterEndpoint: Endpoint; public readonly clusterReadEndpoint: Endpoint; public readonly connections: ec2.Connections; @@ -774,6 +793,7 @@ export class DatabaseClusterFromSnapshot extends DatabaseClusterNew { }); this.clusterIdentifier = cluster.ref; + this.clusterResourceIdentifier = cluster.attrDbClusterResourceId; if (secret) { this.secret = secret.attach(this); diff --git a/packages/@aws-cdk/aws-rds/test/cluster.test.ts b/packages/@aws-cdk/aws-rds/test/cluster.test.ts index 880a3745efb09..fd60567e0eb44 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/cluster.test.ts @@ -97,6 +97,7 @@ describe('cluster', () => { VpcSecurityGroupIds: [{ 'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId'] }], }); + expect(stack.resolve(cluster.clusterResourceIdentifier)).toEqual({ 'Fn::GetAtt': ['DatabaseB269D8BB', 'DBClusterResourceId'] }); expect(cluster.instanceIdentifiers).toHaveLength(1); expect(stack.resolve(cluster.instanceIdentifiers[0])).toEqual({ Ref: 'DatabaseInstance1844F58FD', @@ -738,6 +739,7 @@ describe('cluster', () => { clusterIdentifier: 'identifier', }); + expect(() => cluster.clusterResourceIdentifier).toThrow(/Cannot access `clusterResourceIdentifier` of an imported cluster/); expect(() => cluster.clusterEndpoint).toThrow(/Cannot access `clusterEndpoint` of an imported cluster/); expect(() => cluster.clusterReadEndpoint).toThrow(/Cannot access `clusterReadEndpoint` of an imported cluster/); expect(() => cluster.instanceIdentifiers).toThrow(/Cannot access `instanceIdentifiers` of an imported cluster/); @@ -750,6 +752,7 @@ describe('cluster', () => { const cluster = DatabaseCluster.fromDatabaseClusterAttributes(stack, 'Database', { clusterEndpointAddress: 'addr', clusterIdentifier: 'identifier', + clusterResourceIdentifier: 'identifier', instanceEndpointAddresses: ['instance-addr'], instanceIdentifiers: ['identifier'], port: 3306, @@ -759,6 +762,7 @@ describe('cluster', () => { })], }); + expect(cluster.clusterResourceIdentifier).toEqual('identifier'); expect(cluster.clusterEndpoint.socketAddress).toEqual('addr:3306'); expect(cluster.clusterReadEndpoint.socketAddress).toEqual('reader-address:3306'); expect(cluster.instanceIdentifiers).toEqual(['identifier']); @@ -2091,6 +2095,7 @@ describe('cluster', () => { Template.fromStack(stack).resourceCountIs('AWS::RDS::DBInstance', 2); + expect(stack.resolve(cluster.clusterResourceIdentifier)).toEqual({ 'Fn::GetAtt': ['DatabaseB269D8BB', 'DBClusterResourceId'] }); expect(cluster.instanceIdentifiers).toHaveLength(2); expect(stack.resolve(cluster.instanceIdentifiers[0])).toEqual({ Ref: 'DatabaseInstance1844F58FD', diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.assets.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.assets.json index 98ea3b7c06a81..4d6cbf25c5121 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.assets.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.assets.json @@ -1,7 +1,7 @@ { - "version": "20.0.0", + "version": "29.0.0", "files": { - "0b11338b1fdbc41b36beb545451369de9d176a04762e559c87a3afbe35c7316d": { + "b62ee60990b0a14a4b10381d4d26d42ce50c164305fa81b2d9729e116480af0f": { "source": { "path": "aws-cdk-rds-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "0b11338b1fdbc41b36beb545451369de9d176a04762e559c87a3afbe35c7316d.json", + "objectKey": "b62ee60990b0a14a4b10381d4d26d42ce50c164305fa81b2d9729e116480af0f.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.template.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.template.json index 5addaf6c1826d..072548b1fa549 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.template.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/aws-cdk-rds-integ.template.json @@ -495,7 +495,6 @@ "DatabaseB269D8BB": { "Type": "AWS::RDS::DBCluster", "Properties": { - "Engine": "aurora", "CopyTagsToSnapshot": true, "DBClusterParameterGroupName": { "Ref": "ParamsA8366201" @@ -503,6 +502,7 @@ "DBSubnetGroupName": { "Ref": "DatabaseSubnets56F17B9A" }, + "Engine": "aurora", "KmsKeyId": { "Fn::GetAtt": [ "DbSecurity381C2C15", @@ -567,6 +567,70 @@ ], "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" + }, + "ClusterIamAccess93AC3DF3": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ClusterIamAccessDefaultPolicyA088E4DA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "rds-db:connect", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":rds-db:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":dbuser:", + { + "Fn::GetAtt": [ + "DatabaseB269D8BB", + "DBClusterResourceId" + ] + }, + "/db_user" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ClusterIamAccessDefaultPolicyA088E4DA", + "Roles": [ + { + "Ref": "ClusterIamAccess93AC3DF3" + } + ] + } } }, "Parameters": { diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/cdk.out b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/cdk.out index 588d7b269d34f..d8b441d447f8a 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"20.0.0"} \ No newline at end of file +{"version":"29.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/integ.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/integ.json index b32b0de432db8..553cd276a4bd3 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/integ.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "20.0.0", + "version": "29.0.0", "testCases": { "integ.cluster": { "stacks": [ diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/manifest.json index b3f4afaedc9b0..0641f1a9bb191 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/manifest.json @@ -1,12 +1,6 @@ { - "version": "20.0.0", + "version": "29.0.0", "artifacts": { - "Tree": { - "type": "cdk:tree", - "properties": { - "file": "tree.json" - } - }, "aws-cdk-rds-integ.assets": { "type": "cdk:asset-manifest", "properties": { @@ -23,7 +17,7 @@ "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/0b11338b1fdbc41b36beb545451369de9d176a04762e559c87a3afbe35c7316d.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b62ee60990b0a14a4b10381d4d26d42ce50c164305fa81b2d9729e116480af0f.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -225,6 +219,18 @@ "data": "DatabaseInstance2AA380DEE" } ], + "/aws-cdk-rds-integ/ClusterIamAccess/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterIamAccess93AC3DF3" + } + ], + "/aws-cdk-rds-integ/ClusterIamAccess/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ClusterIamAccessDefaultPolicyA088E4DA" + } + ], "/aws-cdk-rds-integ/BootstrapVersion": [ { "type": "aws:cdk:logicalId", @@ -239,6 +245,12 @@ ] }, "displayName": "aws-cdk-rds-integ" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/tree.json index c8ebaa8dc181b..1b5a2ae9b6bec 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.js.snapshot/tree.json @@ -4,14 +4,6 @@ "id": "App", "path": "", "children": { - "Tree": { - "id": "Tree", - "path": "Tree", - "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" - } - }, "aws-cdk-rds-integ": { "id": "aws-cdk-rds-integ", "path": "aws-cdk-rds-integ", @@ -91,8 +83,8 @@ "id": "Acl", "path": "aws-cdk-rds-integ/VPC/PublicSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -258,8 +250,8 @@ "id": "Acl", "path": "aws-cdk-rds-integ/VPC/PublicSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -425,8 +417,8 @@ "id": "Acl", "path": "aws-cdk-rds-integ/VPC/PrivateSubnet1/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -544,8 +536,8 @@ "id": "Acl", "path": "aws-cdk-rds-integ/VPC/PrivateSubnet2/Acl", "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" } }, "RouteTable": { @@ -848,7 +840,6 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::RDS::DBCluster", "aws:cdk:cloudformation:props": { - "engine": "aurora", "copyTagsToSnapshot": true, "dbClusterParameterGroupName": { "Ref": "ParamsA8366201" @@ -856,6 +847,7 @@ "dbSubnetGroupName": { "Ref": "DatabaseSubnets56F17B9A" }, + "engine": "aurora", "kmsKeyId": { "Fn::GetAtt": [ "DbSecurity381C2C15", @@ -929,17 +921,149 @@ "fqn": "@aws-cdk/aws-rds.DatabaseCluster", "version": "0.0.0" } + }, + "ClusterIamAccess": { + "id": "ClusterIamAccess", + "path": "aws-cdk-rds-integ/ClusterIamAccess", + "children": { + "ImportClusterIamAccess": { + "id": "ImportClusterIamAccess", + "path": "aws-cdk-rds-integ/ClusterIamAccess/ImportClusterIamAccess", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-integ/ClusterIamAccess/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-rds-integ/ClusterIamAccess/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-rds-integ/ClusterIamAccess/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "rds-db:connect", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":rds-db:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":dbuser:", + { + "Fn::GetAtt": [ + "DatabaseB269D8BB", + "DBClusterResourceId" + ] + }, + "/db_user" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "ClusterIamAccessDefaultPolicyA088E4DA", + "roles": [ + { + "Ref": "ClusterIamAccess93AC3DF3" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-rds-integ/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-rds-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } } }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.1.85" + "version": "10.1.216" } } }, "constructInfo": { - "fqn": "constructs.Construct", - "version": "10.1.85" + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.cluster.ts b/packages/@aws-cdk/aws-rds/test/integ.cluster.ts index 09c74af16fecd..1b136ea59a55c 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.cluster.ts @@ -1,4 +1,5 @@ import * as ec2 from '@aws-cdk/aws-ec2'; +import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as cdk from '@aws-cdk/core'; import { Credentials, DatabaseCluster, DatabaseClusterEngine, ParameterGroup } from '../lib'; @@ -32,4 +33,20 @@ const cluster = new DatabaseCluster(stack, 'Database', { cluster.connections.allowDefaultPortFromAnyIpv4('Open to the world'); +const role = new iam.Role(stack, 'ClusterIamAccess', { + assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'), +}); +const clusterIamAuthArn = stack.formatArn({ + service: 'rds-db', + resource: `dbuser:${cluster.clusterResourceIdentifier}`, + resourceName: 'db_user', +}); +role.addToPolicy( + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['rds-db:connect'], + resources: [clusterIamAuthArn], + }), +); + app.synth();