Skip to content

Commit

Permalink
feat(redshift): supports excludeCharacters settings for DatabaseSecret (
Browse files Browse the repository at this point in the history
#30563)

### Issue # (if applicable)

Closes #26847.

### Reason for this change
In the case of passwords generated by [DatabaseSecret](https://docs.aws.amazon.com/cdk/api/v2/docs/@aws-cdk_aws-redshift-alpha.DatabaseSecret.html), there may be a need to exclude certain characters. 

The original issue was to exclude the backtick character from passwords. 
However, the current default value of `excludeCharacters`, `'"@/\ ''`, matches the characters that are not supported in Redshift ([docs](https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_USER.html#r_CREATE_USER-parameters)).

> It can use any ASCII characters with ASCII codes 33–126, except ' (single quotation mark), " (double quotation mark), \, /, or @.

Instead of including the backtick in the default value of `excludeCharacters`, it was considered appropriate to make it configurable.



### Description of changes
Add `excludeCharacters` property to specify characters to not include in generated passwords.



### Description of how you validated changes
Add unit tests and integ tests.


### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https:/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https:/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
mazyu36 authored Sep 26, 2024
1 parent 00c2efb commit a1c46cf
Show file tree
Hide file tree
Showing 28 changed files with 3,865 additions and 8 deletions.
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

0 comments on commit a1c46cf

Please sign in to comment.