Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/aws-cdk-lib/aws-rds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,24 @@ new rds.DatabaseCluster(this, 'Cluster', {
});
```

## Importing existing DatabaseInstance

### Lookup DatabaseInstance by instanceIdentifier

You can lookup an existing DatabaseInstance by its instanceIdentifier using `DatabaseInstance.fromLookup()`. This method returns an `IDatabaseInstance`.

Here's how `DatabaseInstance.fromLookup()` can be used:

```ts
declare const myUserRole: iam.Role;

const dbFromLookup = rds.DatabaseInstance.fromLookup(this, 'dbFromLookup', {
instanceIdentifier: 'instanceId',
});

// Grant a connection
dbFromLookup.grantConnect(myUserRole, 'my-user-id');

## Limitless Database Cluster

Amazon Aurora [PostgreSQL Limitless Database](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/limitless.html) provides automated horizontal scaling to process millions of write transactions per second and manages petabytes of data while maintaining the simplicity of operating inside a single database.
Expand Down
67 changes: 66 additions & 1 deletion packages/aws-cdk-lib/aws-rds/lib/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import * as kms from '../../aws-kms';
import * as logs from '../../aws-logs';
import * as s3 from '../../aws-s3';
import * as secretsmanager from '../../aws-secretsmanager';
import { ArnComponents, ArnFormat, Duration, FeatureFlags, IResource, Lazy, RemovalPolicy, Resource, Stack, Token, Tokenization } from '../../core';
import * as cxschema from '../../cloud-assembly-schema';
import { ArnComponents, ArnFormat, ContextProvider, Duration, FeatureFlags, IResource, Lazy, RemovalPolicy, Resource, Stack, Token, Tokenization } from '../../core';
import { ValidationError } from '../../core/lib/errors';
import { addConstructMetadata } from '../../core/lib/metadata-resource';
import * as cxapi from '../../cx-api';
Expand Down Expand Up @@ -134,6 +135,60 @@ export interface DatabaseInstanceAttributes {
* A new or imported database instance.
*/
export abstract class DatabaseInstanceBase extends Resource implements IDatabaseInstance {
/**
* Lookup an existing DatabaseInstance using instanceIdentifier.
*/
public static fromLookup(scope: Construct, id: string, options: DatabaseInstanceLookupOptions): IDatabaseInstance {
const response: {[key: string]: any}[] = ContextProvider.getValue(scope, {
provider: cxschema.ContextProvider.CC_API_PROVIDER,
props: {
typeName: 'AWS::RDS::DBInstance',
exactIdentifier: options.instanceIdentifier,
propertiesToReturn: [
'DBInstanceArn',
'Endpoint.Address',
'Endpoint.Port',
'DbiResourceId',
'DBSecurityGroups',
],
} as cxschema.CcApiContextQuery,
dummyValue: [
{
'Identifier': 'TEST',
'DBInstanceArn': 'TESTARN',
'Endpoint.Address': 'TESTADDRESS',
'Endpoint.Port': '5432',
'DbiResourceId': 'TESTID',
'DBSecurityGroups': [],
},
],
}).value;

// getValue returns a list of result objects. We are expecting 1 result or Error.
const instance = response[0];

// Get ISecurityGroup from securityGroupId
let securityGroups: ec2.ISecurityGroup[] = [];
const dbsg: string[] = instance.DBSecurityGroups;
if (dbsg) {
securityGroups = dbsg.map(securityGroupId => {
return ec2.SecurityGroup.fromSecurityGroupId(
scope,
`LSG-${securityGroupId}`,
securityGroupId,
);
});
}

return this.fromDatabaseInstanceAttributes(scope, id, {
instanceEndpointAddress: instance['Endpoint.Address'],
port: instance['Endpoint.Port'],
instanceIdentifier: options.instanceIdentifier,
securityGroups: securityGroups,
instanceResourceId: instance.DbiResourceId,
});
}

/**
* Import an existing database instance.
*/
Expand Down Expand Up @@ -1142,6 +1197,16 @@ abstract class DatabaseInstanceSource extends DatabaseInstanceNew implements IDa
}
}

/**
* Properties for looking up an existing DatabaseInstance.
*/
export interface DatabaseInstanceLookupOptions {
/**
* The instance identifier of the DatabaseInstance
*/
readonly instanceIdentifier: string;
}

/**
* Construction properties for a DatabaseInstance.
*/
Expand Down
127 changes: 127 additions & 0 deletions packages/aws-cdk-lib/aws-rds/test/instance.from-lookup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as cxschema from '../../cloud-assembly-schema';
import { ContextProvider, Stack } from '../../core';
import * as rds from '../lib';

/* eslint-disable */
describe('DatabaseInstanceBase from lookup', () => {
test('return correct instance info', () => {
// GIVEN
const resultObjs = [
{
'DBInstanceArn': 'arn:aws:rds:us-east-1:123456789012:db:instance-1',
'Endpoint.Address': 'instance-1.testserver.us-east-1.rds.amazonaws.com',
'Endpoint.Port': '5432',
'DbiResourceId': 'db-ABCDEFGHI',
'DBSecurityGroups': [],
'Identifier': 'instance-1',
},
];
const value = {
value: resultObjs,
};
const mock = jest.spyOn(ContextProvider, 'getValue').mockReturnValue(value);

// WHEN
const stack = new Stack(undefined, undefined, { env: { region: 'us-east-1', account: '123456789012' } });
const instance = rds.DatabaseInstance.fromLookup(stack, 'MyInstance', {
instanceIdentifier: 'instance-1',
});

// THEN
expect(instance.instanceIdentifier).toEqual('instance-1');
expect(instance.dbInstanceEndpointAddress).toEqual('instance-1.testserver.us-east-1.rds.amazonaws.com');
expect(instance.dbInstanceEndpointPort).toEqual('5432');
expect(instance.instanceResourceId).toEqual('db-ABCDEFGHI');
expect(instance.connections.securityGroups.length).toEqual(0);

expect(mock).toHaveBeenCalledWith(stack, {
provider: cxschema.ContextProvider.CC_API_PROVIDER,
props: {
typeName: 'AWS::RDS::DBInstance',
exactIdentifier: 'instance-1',
propertiesToReturn: [
'DBInstanceArn',
'Endpoint.Address',
'Endpoint.Port',
'DbiResourceId',
'DBSecurityGroups',
],
} as cxschema.CcApiContextQuery,
dummyValue: [
{
'Identifier': 'TEST',
'DBInstanceArn': 'TESTARN',
'Endpoint.Address': 'TESTADDRESS',
'Endpoint.Port': '5432',
'DbiResourceId': 'TESTID',
'DBSecurityGroups': [],
},
],
});

mock.mockRestore();
});
});

describe('DatabaseInstanceBase from lookup with DBSG', () => {
test('return correct instance info', () => {
// GIVEN
const resultObjs = [
{
'DBInstanceArn': 'arn:aws:rds:us-east-1:123456789012:db:instance-1',
'Endpoint.Address': 'instance-1.testserver.us-east-1.rds.amazonaws.com',
'Endpoint.Port': '5432',
'DbiResourceId': 'db-ABCDEFGHI',
'DBSecurityGroups': ['dbsg-1', 'dbsg-2'],
'Identifier': 'instance-1',
},
];
const value = {
value: resultObjs,
};
const mock = jest.spyOn(ContextProvider, 'getValue').mockReturnValue(value);

// WHEN
const stack = new Stack(undefined, undefined, { env: { region: 'us-east-1', account: '123456789012' } });
const instance = rds.DatabaseInstance.fromLookup(stack, 'MyInstance', {
instanceIdentifier: 'instance-1',
});

// THEN
expect(instance.instanceIdentifier).toEqual('instance-1');
expect(instance.dbInstanceEndpointAddress).toEqual('instance-1.testserver.us-east-1.rds.amazonaws.com');
expect(instance.dbInstanceEndpointPort).toEqual('5432');
expect(instance.instanceResourceId).toEqual('db-ABCDEFGHI');
expect(instance.connections.securityGroups.length).toEqual(2);
expect(instance.connections.securityGroups[0].securityGroupId).toEqual('dbsg-1');
expect(instance.connections.securityGroups[1].securityGroupId).toEqual('dbsg-2');

expect(mock).toHaveBeenCalledWith(stack, {
provider: cxschema.ContextProvider.CC_API_PROVIDER,
props: {
typeName: 'AWS::RDS::DBInstance',
exactIdentifier: 'instance-1',
propertiesToReturn: [
'DBInstanceArn',
'Endpoint.Address',
'Endpoint.Port',
'DbiResourceId',
'DBSecurityGroups',
],
} as cxschema.CcApiContextQuery,
dummyValue: [
{
'Identifier': 'TEST',
'DBInstanceArn': 'TESTARN',
'Endpoint.Address': 'TESTADDRESS',
'Endpoint.Port': '5432',
'DbiResourceId': 'TESTID',
'DBSecurityGroups': [],
},
],
});

mock.mockRestore();
});
});
/* eslint-enable */