Skip to content

Commit

Permalink
fix(redshift): add Redshift SLR creation (#725)
Browse files Browse the repository at this point in the history
* add Redshift service linked role creation in RedshiftServerlessNamespace
* add service linked role factory in RedshiftServerlessNamespace props
  • Loading branch information
vgkowski authored Sep 10, 2024
1 parent 3880167 commit 29c85e0
Show file tree
Hide file tree
Showing 10 changed files with 405 additions and 5 deletions.
355 changes: 355 additions & 0 deletions framework/API.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions framework/src/consumption/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ A [Redshift Serverless Workgroup](https://docs.aws.amazon.com/redshift/latest/mg
- Provide helper methods for running SQL commands via the Redshift Data API. Commands can be custom or predefined for common administration tasks like creating and granting roles.
- Initialize a Glue Data Catalog integration with auto crawling via Glue Crawlers. This would allow tables in Redshift Serverless to appear in the [Glue Data Catalog](https://docs.aws.amazon.com/glue/latest/dg/catalog-and-crawler.html) for the purposes of discovery and integration.

:::warning
The default VPC created by the construct follows the [standard implementation of the VPC L2 CDK Construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#control-over-availability-zones). As a result, if no account ID and no region are configured in the CDK Stack, the VPC will only contain 2 AZ and the [Redshift Serverless Workgroup will fail to deploy](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-usage-considerations.html). Be sure to configure the account ID and region to create a 3 AZ VPC.
:::

## Usage

[example default usage](./examples/redshift-serverless-workgroup-default.lit.ts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { RemovalPolicy } from 'aws-cdk-lib';
import { IRole } from 'aws-cdk-lib/aws-iam';
import { Key } from 'aws-cdk-lib/aws-kms';
import { CreateServiceLinkedRole } from '../../../../utils';

/**
* Namespace log export types
Expand Down Expand Up @@ -83,4 +84,10 @@ export interface RedshiftServerlessNamespaceProps {
* @default Indefinite final snapshot retention
*/
readonly finalSnapshotRetentionPeriod?: number;

/**
* The Factory for creating Redshift service linked role
* @default - A factory is created
*/
readonly serviceLinkedRoleFactory?: CreateServiceLinkedRole;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { ILogGroup } from 'aws-cdk-lib/aws-logs';
import { ISecret, Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { Construct } from 'constructs';
import { RedshiftServerlessNamespaceProps } from './redshift-serverless-namespace-props';
import { Context, TrackedConstruct, TrackedConstructProps, Utils } from '../../../../utils';
import { Context, CreateServiceLinkedRole, TrackedConstruct, TrackedConstructProps, Utils } from '../../../../utils';
import { DsfProvider } from '../../../../utils/lib/dsf-provider';
import { ServiceLinkedRoleService } from '../../../../utils/lib/service-linked-role-service';

/**
* Create a Redshift Serverless Namespace with the admin credentials stored in Secrets Manager
Expand Down Expand Up @@ -113,8 +114,14 @@ export class RedshiftServerlessNamespace extends TrackedConstruct {
this.roles[props.defaultIAMRole.roleArn] = props.defaultIAMRole;
}

this.dbName = props.dbName;
this.removalPolicy = Context.revertRemovalPolicy(scope, props.removalPolicy);

const slr = props.serviceLinkedRoleFactory || new CreateServiceLinkedRole(this, 'CreateSLR', {
removalPolicy: this.removalPolicy,
});
slr.create(ServiceLinkedRoleService.REDSHIFT);

this.dbName = props.dbName;
const logExports: string[] = props.logExports || [];
this.namespaceName = `${props.name}-${Utils.generateUniqueHash(this)}`;
this.dataKey = props.dataKey ?? new Key(this, 'DefaultNamespaceKey', { enableKeyRotation: true, removalPolicy: this.removalPolicy });
Expand Down
3 changes: 1 addition & 2 deletions framework/src/utils/lib/create-service-linked-role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ import { DsfProvider } from './dsf-provider';
import { ServiceLinkedRoleService } from './service-linked-role-service';

/**
* @internal
* Create service linked role for the indicated service if it doesn't exists
*
* @example
* const slr = new dsf.utils.CreateServiceLinkedRole(this, 'CreateSLR')
* slr.create('redshift.amazonaws.com')
* slr.create(ServiceLinkedRoleService.REDSHIFT)
*/
export class CreateServiceLinkedRole extends Construct {

Expand Down
2 changes: 2 additions & 0 deletions framework/src/utils/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ export * from './s3-data-copy-props';
export * from './architecture';
export * from './client-vpn-endpoint-props';
export * from './create-service-linked-role';
export * from './create-service-linked-role-props';
export * from './service-linked-role-service';


Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ NagSuppressions.addResourceSuppressionsByPath(stack, [
],
true);

NagSuppressions.addResourceSuppressionsByPath(stack,
'Stack/DefaultNamespace/CreateSLR',
[
{ id: 'AwsSolutions-IAM5', reason: 'Inherited from another DSF construct, not in the scope of this test' },
{ id: 'AwsSolutions-IAM4', reason: 'Inherited from another DSF construct, not in the scope of this test' },
{ id: 'AwsSolutions-L1', reason: 'Inherited from another DSF construct, not in the scope of this test' },
],
true,
);

test('No unsuppressed Warnings', () => {
const warnings = Annotations.fromStack(stack).findWarning('*', Match.stringLikeRegexp('AwsSolutions-.*'));
console.log(warnings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ NagSuppressions.addResourceSuppressionsByPath(stack,
],
);

NagSuppressions.addResourceSuppressionsByPath(stack,
'Stack/DefaultNamespace/CreateSLR',
[
{ id: 'AwsSolutions-IAM5', reason: 'Inherited from another DSF construct, not in the scope of this test' },
{ id: 'AwsSolutions-IAM4', reason: 'Inherited from another DSF construct, not in the scope of this test' },
{ id: 'AwsSolutions-L1', reason: 'Inherited from another DSF construct, not in the scope of this test' },
],
true,
);

NagSuppressions.addResourceSuppressionsByPath(stack, [
'Stack/RedshiftWorkgroup/DataApiCheckAccessData',
'Stack/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ A [Redshift Serverless Workgroup](https://docs.aws.amazon.com/redshift/latest/mg
- Provide helper methods for running SQL commands via the Redshift Data API. Commands can be custom or predefined for common administration tasks like creating and granting roles.
- Initialize a Glue Data Catalog integration with auto crawling via Glue Crawlers. This would allow tables in Redshift Serverless to appear in the [Glue Data Catalog](https://docs.aws.amazon.com/glue/latest/dg/catalog-and-crawler.html) for the purposes of discovery and integration.

:::warning
The default VPC created by the construct follows the [standard implementation of the VPC L2 CDK Construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#control-over-availability-zones). As a result, if no account ID and no region are configured in the CDK Stack, the VPC will only contain 2 AZ and the [Redshift Serverless Workgroup will fail to deploy](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-usage-considerations.html). Be sure to configure the account ID and region to create a 3 AZ VPC.
:::

## Usage

<Tabs>
Expand Down
4 changes: 3 additions & 1 deletion website/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import TabItem from '@theme/TabItem';

# Quick start

> :heavy_exclamation_mark: If you're new to AWS CDK, we recommend going through a [few basic examples first](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).
:::warning
If you're new to AWS CDK, we recommend going through a [few basic examples first](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).
:::

The DSF on AWS library is available in Typescript or Python, select the right tab for code examples in your preferred language.

Expand Down

0 comments on commit 29c85e0

Please sign in to comment.