Skip to content

Commit 8e92185

Browse files
authored
chore(kinesisfirehose-alpha): refactor encryption property to combine encryptionKey (#31430)
### Reason for this change The previous `encryption` and `encryptionKey` properties required error handling to enforce when an `encryptionKey` could be specified and when it was invalid (only valid when using `CUSTOMER_MANAGED_KEY`). The properties should be combined to make this user experience more straightforward and only allow a KMS key to be passed in when using a customer-managed key. ### Description of changes BREAKING CHANGE: `encryptionKey` property is removed and `encryption` property type has changed from the `StreamEncryption` enum to the `StreamEncryption` class. To pass in a KMS key for the customer managed key case, use `StreamEncryption.customerManagedKey(key)` #### Details Replaced `encryption` and `encryptionKey` properties with a single property `encryption` of type `StreamEncryption` and is used by calling one of the 3 methods: ```ts SreamEncryption.unencrypted() StreamEncryption.awsOwnedKey() StreamEncryption.customerManagedKey(key?: IKey) ``` This makes it so it's not longer possible to pass in a key when the encryption type is AWS owned or unencrypted. The `key` is an optional parameter in `StreamEncryption.customerManagedKey(key?: IKey)` so following the previous behaviour, if a key is provided it will be used, otherwise a key will be created for the user. ### Description of how you validated changes Generated templates do not change so behaviour remains the same. Updated integ/unit tests. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/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*
1 parent 7ee183d commit 8e92185

File tree

6 files changed

+67
-50
lines changed

6 files changed

+67
-50
lines changed

packages/@aws-cdk/aws-kinesisfirehose-alpha/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,18 @@ declare const destination: firehose.IDestination;
153153

154154
// SSE with an AWS-owned key
155155
new firehose.DeliveryStream(this, 'Delivery Stream AWS Owned', {
156-
encryption: firehose.StreamEncryption.AWS_OWNED,
156+
encryption: firehose.StreamEncryption.awsOwnedKey(),
157157
destinations: [destination],
158158
});
159159
// SSE with an customer-managed key that is created automatically by the CDK
160160
new firehose.DeliveryStream(this, 'Delivery Stream Implicit Customer Managed', {
161-
encryption: firehose.StreamEncryption.CUSTOMER_MANAGED,
161+
encryption: firehose.StreamEncryption.customerManagedKey(),
162162
destinations: [destination],
163163
});
164164
// SSE with an customer-managed key that is explicitly specified
165165
declare const key: kms.Key;
166166
new firehose.DeliveryStream(this, 'Delivery Stream Explicit Customer Managed', {
167-
encryptionKey: key,
167+
encryption: firehose.StreamEncryption.customerManagedKey(key),
168168
destinations: [destination],
169169
});
170170
```

packages/@aws-cdk/aws-kinesisfirehose-alpha/lib/delivery-stream.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Construct, Node } from 'constructs';
99
import { IDestination } from './destination';
1010
import { FirehoseMetrics } from 'aws-cdk-lib/aws-kinesisfirehose/lib/kinesisfirehose-canned-metrics.generated';
1111
import { CfnDeliveryStream } from 'aws-cdk-lib/aws-kinesisfirehose';
12+
import { StreamEncryption } from './encryption';
1213

1314
const PUT_RECORD_ACTIONS = [
1415
'firehose:PutRecord',
@@ -162,7 +163,7 @@ abstract class DeliveryStreamBase extends cdk.Resource implements IDeliveryStrea
162163
/**
163164
* Options for server-side encryption of a delivery stream.
164165
*/
165-
export enum StreamEncryption {
166+
export enum StreamEncryptionType {
166167
/**
167168
* Data in the stream is stored unencrypted.
168169
*/
@@ -216,16 +217,9 @@ export interface DeliveryStreamProps {
216217
/**
217218
* Indicates the type of customer master key (CMK) to use for server-side encryption, if any.
218219
*
219-
* @default StreamEncryption.UNENCRYPTED - unless `encryptionKey` is provided, in which case this will be implicitly set to `StreamEncryption.CUSTOMER_MANAGED`
220+
* @default StreamEncryption.unencrypted()
220221
*/
221222
readonly encryption?: StreamEncryption;
222-
223-
/**
224-
* Customer managed key to server-side encrypt data in the stream.
225-
*
226-
* @default - no KMS key will be used; if `encryption` is set to `CUSTOMER_MANAGED`, a KMS key will be created for you
227-
*/
228-
readonly encryptionKey?: kms.IKey;
229223
}
230224

231225
/**
@@ -334,23 +328,20 @@ export class DeliveryStream extends DeliveryStreamBase {
334328
throw new Error(`Only one destination is allowed per delivery stream, given ${props.destinations.length}`);
335329
}
336330

337-
if (props.encryptionKey || props.sourceStream) {
331+
if (props.encryption?.encryptionKey || props.sourceStream) {
338332
this._role = this._role ?? new iam.Role(this, 'Service Role', {
339333
assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'),
340334
});
341335
}
342336

343337
if (
344338
props.sourceStream &&
345-
(props.encryption === StreamEncryption.AWS_OWNED || props.encryption === StreamEncryption.CUSTOMER_MANAGED || props.encryptionKey)
339+
(props.encryption?.type === StreamEncryptionType.AWS_OWNED || props.encryption?.type === StreamEncryptionType.CUSTOMER_MANAGED)
346340
) {
347341
throw new Error('Requested server-side encryption but delivery stream source is a Kinesis data stream. Specify server-side encryption on the data stream instead.');
348342
}
349-
if ((props.encryption === StreamEncryption.AWS_OWNED || props.encryption === StreamEncryption.UNENCRYPTED) && props.encryptionKey) {
350-
throw new Error(`Specified stream encryption as ${StreamEncryption[props.encryption]} but provided a customer-managed key`);
351-
}
352-
const encryptionKey = props.encryptionKey ?? (props.encryption === StreamEncryption.CUSTOMER_MANAGED ? new kms.Key(this, 'Key') : undefined);
353-
const encryptionConfig = (encryptionKey || (props.encryption === StreamEncryption.AWS_OWNED)) ? {
343+
const encryptionKey = props.encryption?.encryptionKey ?? (props.encryption?.type === StreamEncryptionType.CUSTOMER_MANAGED ? new kms.Key(this, 'Key') : undefined);
344+
const encryptionConfig = (encryptionKey || (props.encryption?.type === StreamEncryptionType.AWS_OWNED)) ? {
354345
keyArn: encryptionKey?.keyArn,
355346
keyType: encryptionKey ? 'CUSTOMER_MANAGED_CMK' : 'AWS_OWNED_CMK',
356347
} : undefined;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { StreamEncryptionType } from './delivery-stream';
2+
import { IKey } from 'aws-cdk-lib/aws-kms';
3+
4+
/**
5+
* Represents server-side encryption for a Kinesis Firehose Delivery Stream.
6+
*/
7+
export abstract class StreamEncryption {
8+
/**
9+
* No server-side encryption is configured.
10+
*/
11+
public static unencrypted(): StreamEncryption {
12+
return new (class extends StreamEncryption {
13+
}) (StreamEncryptionType.UNENCRYPTED);
14+
}
15+
16+
/**
17+
* Configure server-side encryption using an AWS owned key.
18+
*/
19+
public static awsOwnedKey(): StreamEncryption {
20+
return new (class extends StreamEncryption {
21+
}) (StreamEncryptionType.AWS_OWNED);
22+
}
23+
24+
/**
25+
* Configure server-side encryption using customer managed keys.
26+
*
27+
* @param encryptionKey the KMS key for the delivery stream.
28+
*/
29+
public static customerManagedKey(encryptionKey?: IKey): StreamEncryption {
30+
return new (class extends StreamEncryption {
31+
32+
}) (StreamEncryptionType.CUSTOMER_MANAGED, encryptionKey);
33+
}
34+
35+
/**
36+
* Constructor for StreamEncryption.
37+
*
38+
* @param type The type of server-side encryption for the Kinesis Firehose delivery stream.
39+
* @param encryptionKey Optional KMS key used for customer managed encryption.
40+
*/
41+
private constructor (
42+
public readonly type: StreamEncryptionType,
43+
public readonly encryptionKey?: IKey) {}
44+
}

packages/@aws-cdk/aws-kinesisfirehose-alpha/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './delivery-stream';
22
export * from './destination';
3+
export * from './encryption';
34
export * from './lambda-function-processor';
45
export * from './processor';
56

packages/@aws-cdk/aws-kinesisfirehose-alpha/test/delivery-stream.test.ts

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as targets from 'aws-cdk-lib/aws-events-targets';
99
import * as cdk from 'aws-cdk-lib';
1010
import { Construct, Node } from 'constructs';
1111
import * as firehose from '../lib';
12+
import { StreamEncryption } from '../lib';
1213

1314
describe('delivery stream', () => {
1415
let stack: cdk.Stack;
@@ -151,12 +152,12 @@ describe('delivery stream', () => {
151152
Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 2);
152153
});
153154

154-
test('not providing role but specifying encryptionKey creates two roles', () => {
155+
test('not providing role but using customerManagedKey encryption with a key creates two roles', () => {
155156
const key = new kms.Key(stack, 'Key');
156157

157158
new firehose.DeliveryStream(stack, 'Delivery Stream', {
158159
destinations: [mockS3Destination],
159-
encryptionKey: key,
160+
encryption: StreamEncryption.customerManagedKey(key),
160161
});
161162

162163
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
@@ -215,7 +216,7 @@ describe('delivery stream', () => {
215216
test('requesting customer-owned encryption creates key and configuration', () => {
216217
new firehose.DeliveryStream(stack, 'Delivery Stream', {
217218
destinations: [mockS3Destination],
218-
encryption: firehose.StreamEncryption.CUSTOMER_MANAGED,
219+
encryption: firehose.StreamEncryption.customerManagedKey(),
219220
role: deliveryStreamRole,
220221
});
221222

@@ -246,12 +247,12 @@ describe('delivery stream', () => {
246247
});
247248
});
248249

249-
test('providing encryption key creates configuration', () => {
250+
test('using customerManagedKey encryption with provided key creates configuration', () => {
250251
const key = new kms.Key(stack, 'Key');
251252

252253
new firehose.DeliveryStream(stack, 'Delivery Stream', {
253254
destinations: [mockS3Destination],
254-
encryptionKey: key,
255+
encryption: StreamEncryption.customerManagedKey(key),
255256
role: deliveryStreamRole,
256257
});
257258

@@ -281,7 +282,7 @@ describe('delivery stream', () => {
281282
test('requesting AWS-owned key does not create key and creates configuration', () => {
282283
new firehose.DeliveryStream(stack, 'Delivery Stream', {
283284
destinations: [mockS3Destination],
284-
encryption: firehose.StreamEncryption.AWS_OWNED,
285+
encryption: firehose.StreamEncryption.awsOwnedKey(),
285286
role: deliveryStreamRole,
286287
});
287288

@@ -299,7 +300,7 @@ describe('delivery stream', () => {
299300
test('requesting no encryption creates no configuration', () => {
300301
new firehose.DeliveryStream(stack, 'Delivery Stream', {
301302
destinations: [mockS3Destination],
302-
encryption: firehose.StreamEncryption.UNENCRYPTED,
303+
encryption: firehose.StreamEncryption.unencrypted(),
303304
role: deliveryStreamRole,
304305
});
305306

@@ -311,42 +312,22 @@ describe('delivery stream', () => {
311312
});
312313
});
313314

314-
test('requesting AWS-owned key and providing a key throws an error', () => {
315-
const key = new kms.Key(stack, 'Key');
316-
317-
expect(() => new firehose.DeliveryStream(stack, 'Delivery Stream', {
318-
destinations: [mockS3Destination],
319-
encryption: firehose.StreamEncryption.AWS_OWNED,
320-
encryptionKey: key,
321-
})).toThrowError('Specified stream encryption as AWS_OWNED but provided a customer-managed key');
322-
});
323-
324-
test('requesting no encryption and providing a key throws an error', () => {
325-
const key = new kms.Key(stack, 'Key');
326-
327-
expect(() => new firehose.DeliveryStream(stack, 'Delivery Stream', {
328-
destinations: [mockS3Destination],
329-
encryption: firehose.StreamEncryption.UNENCRYPTED,
330-
encryptionKey: key,
331-
})).toThrowError('Specified stream encryption as UNENCRYPTED but provided a customer-managed key');
332-
});
333-
334315
test('requesting encryption or providing a key when source is a stream throws an error', () => {
335316
const sourceStream = new kinesis.Stream(stack, 'Source Stream');
336317

337318
expect(() => new firehose.DeliveryStream(stack, 'Delivery Stream 1', {
338319
destinations: [mockS3Destination],
339-
encryption: firehose.StreamEncryption.AWS_OWNED,
320+
encryption: firehose.StreamEncryption.awsOwnedKey(),
340321
sourceStream,
341322
})).toThrowError('Requested server-side encryption but delivery stream source is a Kinesis data stream. Specify server-side encryption on the data stream instead.');
342323
expect(() => new firehose.DeliveryStream(stack, 'Delivery Stream 2', {
343324
destinations: [mockS3Destination],
344-
encryption: firehose.StreamEncryption.CUSTOMER_MANAGED,
325+
encryption: firehose.StreamEncryption.customerManagedKey(),
345326
sourceStream,
346327
})).toThrowError('Requested server-side encryption but delivery stream source is a Kinesis data stream. Specify server-side encryption on the data stream instead.');
347328
expect(() => new firehose.DeliveryStream(stack, 'Delivery Stream 3', {
348329
destinations: [mockS3Destination],
349-
encryptionKey: new kms.Key(stack, 'Key'),
330+
encryption: StreamEncryption.customerManagedKey(new kms.Key(stack, 'Key')),
350331
sourceStream,
351332
})).toThrowError('Requested server-side encryption but delivery stream source is a Kinesis data stream. Specify server-side encryption on the data stream instead.');
352333
});

packages/@aws-cdk/aws-kinesisfirehose-alpha/test/integ.delivery-stream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const key = new kms.Key(stack, 'Key', {
3737

3838
new firehose.DeliveryStream(stack, 'Delivery Stream', {
3939
destinations: [mockS3Destination],
40-
encryptionKey: key,
40+
encryption: firehose.StreamEncryption.customerManagedKey(key),
4141
});
4242

4343
new firehose.DeliveryStream(stack, 'Delivery Stream No Source Or Encryption Key', {

0 commit comments

Comments
 (0)