Skip to content

Commit

Permalink
fix(rds): add clusterResourceIdentifier property to database cluster (#…
Browse files Browse the repository at this point in the history
…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).
  • Loading branch information
alexedwardjones committed Mar 6, 2023
1 parent d7a1c34 commit 6bda4e5
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 34 deletions.
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-rds/lib/cluster-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
*
Expand Down
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-rds/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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[];
Expand All @@ -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({
Expand All @@ -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');
Expand Down Expand Up @@ -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;
Expand All @@ -662,6 +679,7 @@ export class DatabaseCluster extends DatabaseClusterNew {
});

this.clusterIdentifier = cluster.ref;
this.clusterResourceIdentifier = cluster.attrDbClusterResourceId;

if (secret) {
this.secret = secret.attach(this);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -774,6 +793,7 @@ export class DatabaseClusterFromSnapshot extends DatabaseClusterNew {
});

this.clusterIdentifier = cluster.ref;
this.clusterResourceIdentifier = cluster.attrDbClusterResourceId;

if (secret) {
this.secret = secret.attach(this);
Expand Down
5 changes: 5 additions & 0 deletions packages/@aws-cdk/aws-rds/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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/);
Expand All @@ -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,
Expand All @@ -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']);
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "20.0.0",
"version": "29.0.0",
"files": {
"0b11338b1fdbc41b36beb545451369de9d176a04762e559c87a3afbe35c7316d": {
"b62ee60990b0a14a4b10381d4d26d42ce50c164305fa81b2d9729e116480af0f": {
"source": {
"path": "aws-cdk-rds-integ.template.json",
"packaging": "file"
},
"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}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,14 +495,14 @@
"DatabaseB269D8BB": {
"Type": "AWS::RDS::DBCluster",
"Properties": {
"Engine": "aurora",
"CopyTagsToSnapshot": true,
"DBClusterParameterGroupName": {
"Ref": "ParamsA8366201"
},
"DBSubnetGroupName": {
"Ref": "DatabaseSubnets56F17B9A"
},
"Engine": "aurora",
"KmsKeyId": {
"Fn::GetAtt": [
"DbSecurity381C2C15",
Expand Down Expand Up @@ -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": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"20.0.0"}
{"version":"29.0.0"}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "20.0.0",
"version": "29.0.0",
"testCases": {
"integ.cluster": {
"stacks": [
Expand Down
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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": [
Expand Down Expand Up @@ -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",
Expand All @@ -239,6 +245,12 @@
]
},
"displayName": "aws-cdk-rds-integ"
},
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
}
}
}
Loading

0 comments on commit 6bda4e5

Please sign in to comment.