Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(redshift): supports excludeCharacters settings for DatabaseSecret #30563

Merged
merged 6 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
38 changes: 31 additions & 7 deletions packages/@aws-cdk/aws-redshift-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ const cluster = new Cluster(this, 'Redshift', {
```

By default, the master password will be generated and stored in AWS Secrets Manager.
You can specify characters to not include in generated passwords by setting `excludeCharacters` property.

```ts
import * as ec2 from 'aws-cdk-lib/aws-ec2';

const vpc = new ec2.Vpc(this, 'Vpc');
const cluster = new Cluster(this, 'Redshift', {
masterUser: {
masterUsername: 'admin',
excludeCharacters: '"@/\\\ \'`',
},
vpc
});
```

A default database named `default_db` will be created in the cluster. To change the name of this database set the `defaultDatabaseName` attribute in the constructor properties.

Expand Down Expand Up @@ -152,6 +166,16 @@ plaintext for the password will never be present in the CDK application; instead
Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html)
will be used wherever the password value is required.

You can specify characters to not include in generated passwords by setting `excludeCharacters` property.

```ts fixture=cluster
new User(this, 'User', {
cluster: cluster,
databaseName: 'databaseName',
excludeCharacters: '"@/\\\ \'`',
});
```

### Creating Tables

Create a table within a Redshift cluster database by instantiating a `Table`
Expand Down Expand Up @@ -211,7 +235,7 @@ Tables and their respective columns can be configured to contain comments:
```ts fixture=cluster
new Table(this, 'Table', {
tableColumns: [
{ name: 'col1', dataType: 'varchar(4)', comment: 'This is a column comment' },
{ name: 'col1', dataType: 'varchar(4)', comment: 'This is a column comment' },
{ name: 'col2', dataType: 'float', comment: 'This is a another column comment' }
],
cluster: cluster,
Expand Down Expand Up @@ -242,7 +266,7 @@ Table columns can also contain an `id` attribute, which can allow table columns
```ts fixture=cluster
new Table(this, 'Table', {
tableColumns: [
{ id: 'col1', name: 'col1', dataType: 'varchar(4)' },
{ id: 'col1', name: 'col1', dataType: 'varchar(4)' },
{ id: 'col2', name: 'col2', dataType: 'float' }
],
cluster: cluster,
Expand Down Expand Up @@ -580,11 +604,11 @@ new redshift.Cluster(stack, 'Cluster', {

## Resizing

As your data warehousing needs change, it's possible to resize your Redshift cluster. If the cluster was deployed via CDK,
it's important to resize it via CDK so the change is registered in the AWS CloudFormation template.
As your data warehousing needs change, it's possible to resize your Redshift cluster. If the cluster was deployed via CDK,
it's important to resize it via CDK so the change is registered in the AWS CloudFormation template.
There are two types of resize operations:

* Elastic resize - Number of nodes and node type can be changed, but not at the same time. Elastic resize is the default behavior,
* Elastic resize - Number of nodes and node type can be changed, but not at the same time. Elastic resize is the default behavior,
as it's a fast operation and typically completes in minutes. Elastic resize is only supported on clusters of the following types:
* dc1.large (if your cluster is in a VPC)
* dc1.8xlarge (if your cluster is in a VPC)
Expand All @@ -596,9 +620,9 @@ as it's a fast operation and typically completes in minutes. Elastic resize is o
* ra3.4xlarge
* ra3.16xlarge

* Classic resize - Number of nodes, node type, or both, can be changed. This operation takes longer to complete,
* Classic resize - Number of nodes, node type, or both, can be changed. This operation takes longer to complete,
but is useful when the resize operation doesn't meet the criteria of an elastic resize. If you prefer classic resizing,
you can set the `classicResizing` flag when creating the cluster.

There are other constraints to be aware of, for example, elastic resizing does not support single-node clusters and there are
There are other constraints to be aware of, for example, elastic resizing does not support single-node clusters and there are
limits on the number of nodes you can add to a cluster. See the [AWS Redshift Documentation](https://docs.aws.amazon.com/redshift/latest/mgmt/managing-cluster-operations.html#rs-resize-tutorial) and [AWS API Documentation](https://docs.aws.amazon.com/redshift/latest/APIReference/API_ResizeCluster.html) for more details.
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ export interface Login {
* @default - default master key
*/
readonly encryptionKey?: kms.IKey;

/**
* Characters to not include in the generated password.
*
* @default '"@/\\\ \''
*/
readonly excludeCharacters?: string;
}

/**
Expand Down Expand Up @@ -527,6 +534,7 @@ export class Cluster extends ClusterBase {
secret = new DatabaseSecret(this, 'Secret', {
username: props.masterUser.masterUsername,
encryptionKey: props.masterUser.encryptionKey,
excludeCharacters: props.masterUser.excludeCharacters,
});
}

Expand Down
9 changes: 8 additions & 1 deletion packages/@aws-cdk/aws-redshift-alpha/lib/database-secret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export interface DatabaseSecretProps {
* @default default master key
*/
readonly encryptionKey?: kms.IKey;

/**
* Characters to not include in the generated password.
*
* @default '"@/\\\ \''
*/
readonly excludeCharacters?: string;
}

/**
Expand All @@ -32,7 +39,7 @@ export class DatabaseSecret extends secretsmanager.Secret {
passwordLength: 30, // Redshift password could be up to 64 characters
secretStringTemplate: JSON.stringify({ username: props.username }),
generateStringKey: 'password',
excludeCharacters: '"@/\\\ \'',
excludeCharacters: props.excludeCharacters ?? '"@/\\\ \'',
},
});
}
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-redshift-alpha/lib/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ export interface UserProps extends DatabaseOptions {
*/
readonly encryptionKey?: kms.IKey;

/**
* Characters to not include in the generated password.
*
* @default '"@/\\\ \''
*/
readonly excludeCharacters?: string;

/**
* The policy to apply when this resource is removed from the application.
*
Expand Down Expand Up @@ -153,6 +160,7 @@ export class User extends UserBase {
const secret = new DatabaseSecret(this, 'Secret', {
username,
encryptionKey: props.encryptionKey,
excludeCharacters: props.excludeCharacters,
});
const attachedSecret = secret.attach(props.cluster);
this.password = attachedSecret.secretValueFromJson('password');
Expand Down
48 changes: 48 additions & 0 deletions packages/@aws-cdk/aws-redshift-alpha/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,54 @@ test('creates a secret when master credentials are not specified', () => {
});
});

test('creates a secret with a custom excludeCharacters', () => {
// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
excludeCharacters: '"@/\\\ \'`',
},
vpc,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Redshift::Cluster', {
MasterUsername: {
'Fn::Join': [
'',
[
'{{resolve:secretsmanager:',
{
Ref: 'RedshiftSecretA08D42D6',
},
':SecretString:username::}}',
],
],
},
MasterUserPassword: {
'Fn::Join': [
'',
[
'{{resolve:secretsmanager:',
{
Ref: 'RedshiftSecretA08D42D6',
},
':SecretString:password::}}',
],
],
},
});

Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::Secret', {
GenerateSecretString: {
ExcludeCharacters: '"@/\\\ \'`',
GenerateStringKey: 'password',
PasswordLength: 30,
SecretStringTemplate: '{"username":"admin"}',
},
});
});

describe('node count', () => {

test('Single Node Clusters do not define node count', () => {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading