Skip to content

Commit 766ff8b

Browse files
authored
feat(rds): grantConnect method enables iam auth to rds cluster (#28118)
This adds a `grantConnect` method to the `DatabaseCluster` which gives an IAM entity access to connect to the cluster using the specified database user. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent d19640b commit 766ff8b

File tree

4 files changed

+95
-3
lines changed

4 files changed

+95
-3
lines changed

packages/aws-cdk-lib/aws-rds/README.md

+24-2
Original file line numberDiff line numberDiff line change
@@ -744,10 +744,10 @@ You can also authenticate to a database instance using AWS Identity and Access M
744744
See <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html> for more information
745745
and a list of supported versions and limitations.
746746

747-
**Note**: `grantConnect()` does not currently work - see [this GitHub issue](https://github.com/aws/aws-cdk/issues/11851).
748-
749747
The following example shows enabling IAM authentication for a database instance and granting connection access to an IAM role.
750748

749+
### Instance
750+
751751
```ts
752752
declare const vpc: ec2.Vpc;
753753
const instance = new rds.DatabaseInstance(this, 'Instance', {
@@ -759,6 +759,8 @@ const role = new iam.Role(this, 'DBRole', { assumedBy: new iam.AccountPrincipal(
759759
instance.grantConnect(role); // Grant the role connection access to the DB.
760760
```
761761

762+
### Proxy
763+
762764
The following example shows granting connection access for RDS Proxy to an IAM role.
763765

764766
```ts
@@ -784,6 +786,26 @@ proxy.grantConnect(role, 'admin'); // Grant the role connection access to the DB
784786
**Note**: In addition to the setup above, a database user will need to be created to support IAM auth.
785787
See <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html> for setup instructions.
786788

789+
### Cluster
790+
791+
The following example shows granting connection access for an IAM role to an Aurora Cluster.
792+
793+
```ts
794+
declare const vpc: ec2.Vpc;
795+
const cluster = new rds.DatabaseCluster(this, 'Database', {
796+
engine: rds.DatabaseClusterEngine.auroraMysql({
797+
version: rds.AuroraMysqlEngineVersion.VER_3_03_0,
798+
}),
799+
writer: rds.ClusterInstance.provisioned('writer'),
800+
vpc,
801+
});
802+
const role = new iam.Role(this, 'AppRole', { assumedBy: new iam.ServicePrincipal('someservice.amazonaws.com') });
803+
cluster.grantConnect(role, 'somedbuser');
804+
```
805+
806+
**Note**: In addition to the setup above, a database user will need to be created to support IAM auth.
807+
See <https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html> for setup instructions.
808+
787809
## Kerberos Authentication
788810

789811
You can also authenticate using Kerberos to a database instance using AWS Managed Microsoft AD for authentication;

packages/aws-cdk-lib/aws-rds/lib/cluster-ref.ts

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { IClusterEngine } from './cluster-engine';
22
import { Endpoint } from './endpoint';
33
import { DatabaseProxy, DatabaseProxyOptions } from './proxy';
44
import * as ec2 from '../../aws-ec2';
5+
import * as iam from '../../aws-iam';
56
import * as secretsmanager from '../../aws-secretsmanager';
67
import { IResource } from '../../core';
78

@@ -53,6 +54,15 @@ export interface IDatabaseCluster extends IResource, ec2.IConnectable, secretsma
5354
* Add a new db proxy to this cluster.
5455
*/
5556
addProxy(id: string, options: DatabaseProxyOptions): DatabaseProxy;
57+
58+
/**
59+
* Grant the given identity connection access to the Cluster.
60+
*
61+
* @param grantee the Principal to grant the permissions to
62+
* @param dbUser the name of the database user to allow connecting
63+
*
64+
*/
65+
grantConnect(grantee: iam.IGrantable, dbUser: string): iam.Grant;
5666
}
5767

5868
/**

packages/aws-cdk-lib/aws-rds/lib/cluster.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import { CfnDBCluster, CfnDBClusterProps, CfnDBInstance } from './rds.generated'
1212
import { ISubnetGroup, SubnetGroup } from './subnet-group';
1313
import * as cloudwatch from '../../aws-cloudwatch';
1414
import * as ec2 from '../../aws-ec2';
15+
import * as iam from '../../aws-iam';
1516
import { IRole, ManagedPolicy, Role, ServicePrincipal } from '../../aws-iam';
1617
import * as kms from '../../aws-kms';
1718
import * as logs from '../../aws-logs';
1819
import * as s3 from '../../aws-s3';
1920
import * as secretsmanager from '../../aws-secretsmanager';
20-
import { Annotations, Duration, FeatureFlags, Lazy, RemovalPolicy, Resource, Token } from '../../core';
21+
import { Annotations, ArnFormat, Duration, FeatureFlags, Lazy, RemovalPolicy, Resource, Stack, Token } from '../../core';
2122
import * as cxapi from '../../cx-api';
2223

2324
/**
@@ -457,6 +458,19 @@ export abstract class DatabaseClusterBase extends Resource implements IDatabaseC
457458
targetType: secretsmanager.AttachmentTargetType.RDS_DB_CLUSTER,
458459
};
459460
}
461+
462+
public grantConnect(grantee: iam.IGrantable, dbUser: string): iam.Grant {
463+
return iam.Grant.addToPrincipal({
464+
actions: ['rds-db:connect'],
465+
grantee,
466+
resourceArns: [Stack.of(this).formatArn({
467+
service: 'rds-db',
468+
resource: 'dbuser',
469+
resourceName: `${this.clusterResourceIdentifier}/${dbUser}`,
470+
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
471+
})],
472+
});
473+
}
460474
}
461475

462476
/**

packages/aws-cdk-lib/aws-rds/test/cluster.test.ts

+46
Original file line numberDiff line numberDiff line change
@@ -3855,6 +3855,52 @@ describe('cluster', () => {
38553855
EngineVersion: Match.absent(),
38563856
});
38573857
});
3858+
3859+
test('grantConnect', () => {
3860+
// GIVEN
3861+
const stack = testStack();
3862+
const vpc = new ec2.Vpc(stack, 'VPC');
3863+
const role = new Role(stack, 'Role', {
3864+
assumedBy: new ServicePrincipal('service.amazonaws.com'),
3865+
});
3866+
3867+
// WHEN
3868+
const cluster = new DatabaseCluster(stack, 'Database', {
3869+
engine: DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.VER_14_3 }),
3870+
instanceProps: { vpc },
3871+
});
3872+
cluster.grantConnect(role, 'someUser');
3873+
3874+
// THEN
3875+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
3876+
Roles: [{ Ref: 'Role1ABCC5F0' }],
3877+
PolicyDocument: {
3878+
Statement: [{
3879+
Action: 'rds-db:connect',
3880+
Effect: 'Allow',
3881+
Resource: {
3882+
'Fn::Join': [
3883+
'',
3884+
[
3885+
'arn:',
3886+
{
3887+
Ref: 'AWS::Partition',
3888+
},
3889+
':rds-db:us-test-1:12345:dbuser:',
3890+
{
3891+
'Fn::GetAtt': [
3892+
'DatabaseB269D8BB',
3893+
'DBClusterResourceId',
3894+
],
3895+
},
3896+
'/someUser',
3897+
],
3898+
],
3899+
},
3900+
}],
3901+
},
3902+
});
3903+
});
38583904
});
38593905

38603906
test.each([

0 commit comments

Comments
 (0)