Skip to content

Commit 54f8ea9

Browse files
Jimmy Gaussenmergify[bot]
authored andcommitted
fix(kms): allow multiple addAlias calls on single key (#3596)
* fix(kms): allow multiple addAlias calls on single key * Add prefix+constructor feature, fix tests
1 parent 4681d01 commit 54f8ea9

File tree

7 files changed

+182
-31
lines changed

7 files changed

+182
-31
lines changed

packages/@aws-cdk/aws-kms/lib/alias.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,28 @@ export class Alias extends AliasBase {
8181
constructor(scope: Construct, id: string, props: AliasProps) {
8282
super(scope, id);
8383

84-
if (!Token.isUnresolved(props.aliasName)) {
85-
if (!props.aliasName.startsWith(REQUIRED_ALIAS_PREFIX)) {
86-
throw new Error(`Alias must start with the prefix "${REQUIRED_ALIAS_PREFIX}": ${props.aliasName}`);
84+
let aliasName = props.aliasName;
85+
86+
if (!Token.isUnresolved(aliasName)) {
87+
if (!aliasName.startsWith(REQUIRED_ALIAS_PREFIX)) {
88+
aliasName = REQUIRED_ALIAS_PREFIX + aliasName;
8789
}
8890

89-
if (props.aliasName === REQUIRED_ALIAS_PREFIX) {
90-
throw new Error(`Alias must include a value after "${REQUIRED_ALIAS_PREFIX}": ${props.aliasName}`);
91+
if (aliasName === REQUIRED_ALIAS_PREFIX) {
92+
throw new Error(`Alias must include a value after "${REQUIRED_ALIAS_PREFIX}": ${aliasName}`);
9193
}
9294

93-
if (props.aliasName.startsWith(DISALLOWED_PREFIX)) {
94-
throw new Error(`Alias cannot start with ${DISALLOWED_PREFIX}: ${props.aliasName}`);
95+
if (aliasName.startsWith(DISALLOWED_PREFIX)) {
96+
throw new Error(`Alias cannot start with ${DISALLOWED_PREFIX}: ${aliasName}`);
9597
}
9698

97-
if (!props.aliasName.match(/^[a-zA-Z0-9:/_-]{1,256}$/)) {
99+
if (!aliasName.match(/^[a-zA-Z0-9:/_-]{1,256}$/)) {
98100
throw new Error(`Alias name must be between 1 and 256 characters in a-zA-Z0-9:/_-`);
99101
}
100102
}
101103

102104
const resource = new CfnAlias(this, 'Resource', {
103-
aliasName: props.aliasName,
105+
aliasName,
104106
targetKeyId: props.targetKey.keyArn
105107
});
106108

packages/@aws-cdk/aws-kms/lib/key.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ abstract class KeyBase extends Resource implements IKey {
6868
* Defines a new alias for the key.
6969
*/
7070
public addAlias(alias: string): Alias {
71-
return new Alias(this, 'Alias', { aliasName: alias, targetKey: this });
71+
return new Alias(this, `Alias${alias}`, { aliasName: alias, targetKey: this });
7272
}
7373

7474
/**
@@ -151,6 +151,15 @@ export interface KeyProps {
151151
*/
152152
readonly description?: string;
153153

154+
/**
155+
* Initial alias to add to the key
156+
*
157+
* More aliases can be added later by calling `addAlias`.
158+
*
159+
* @default - No alias is added for the key.
160+
*/
161+
readonly alias?: string;
162+
154163
/**
155164
* Indicates whether AWS KMS rotates the key.
156165
*
@@ -226,6 +235,10 @@ export class Key extends KeyBase {
226235

227236
this.keyArn = resource.attrArn;
228237
resource.applyRemovalPolicy(props.removalPolicy);
238+
239+
if (props.alias !== undefined) {
240+
this.addAlias(props.alias);
241+
}
229242
}
230243

231244
/**

packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.expected.json

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,35 @@
4747
"Version": "2012-10-17"
4848
}
4949
},
50-
"DeletionPolicy": "Delete",
51-
"UpdateReplacePolicy": "Delete"
52-
},
53-
"MyKeyAlias1B45D9DA": {
50+
"UpdateReplacePolicy": "Delete",
51+
"DeletionPolicy": "Delete"
52+
}
53+
},
54+
"Outputs": {
55+
"ExportsOutputFnGetAttMyKey6AB29FA6Arn4FA82736": {
56+
"Value": {
57+
"Fn::GetAtt": [
58+
"MyKey6AB29FA6",
59+
"Arn"
60+
]
61+
},
62+
"Export": {
63+
"Name": "KeyStack:ExportsOutputFnGetAttMyKey6AB29FA6Arn4FA82736"
64+
}
65+
}
66+
}
67+
},
68+
{
69+
"Resources": {
70+
"Alias325C5727": {
5471
"Type": "AWS::KMS::Alias",
5572
"Properties": {
5673
"AliasName": "alias/foo",
5774
"TargetKeyId": {
58-
"Fn::GetAtt": [
59-
"MyKey6AB29FA6",
60-
"Arn"
61-
]
75+
"Fn::ImportValue": "KeyStack:ExportsOutputFnGetAttMyKey6AB29FA6Arn4FA82736"
6276
}
6377
}
6478
}
6579
}
66-
},
67-
{}
68-
]
80+
}
81+
]

packages/@aws-cdk/aws-kms/test/integ.key-sharing.lit.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ class UseStack extends cdk.Stack {
3131
super(scope, id, props);
3232

3333
// Use the IKey object here.
34-
props.key.addAlias('alias/foo');
34+
new kms.Alias(this, 'Alias', {
35+
aliasName: 'alias/foo',
36+
targetKey: props.key
37+
});
3538
}
3639
}
3740

packages/@aws-cdk/aws-kms/test/integ.key.expected.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@
5656
"Version": "2012-10-17"
5757
}
5858
},
59-
"DeletionPolicy": "Delete",
60-
"UpdateReplacePolicy": "Delete"
59+
"UpdateReplacePolicy": "Delete",
60+
"DeletionPolicy": "Delete"
6161
},
62-
"MyKeyAlias1B45D9DA": {
62+
"MyKeyAliasaliasbarD0D50DB8": {
6363
"Type": "AWS::KMS::Alias",
6464
"Properties": {
6565
"AliasName": "alias/bar",
@@ -72,4 +72,4 @@
7272
}
7373
}
7474
}
75-
}
75+
}

packages/@aws-cdk/aws-kms/test/test.alias.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,41 @@ export = {
2020
test.done();
2121
},
2222

23-
'fails if alias name does\'t start with "alias/"'(test: Test) {
23+
'add "alias/" prefix if not given.'(test: Test) {
2424
const app = new App();
2525
const stack = new Stack(app, 'Test');
2626

27-
const key = new Key(stack, 'MyKey', {
27+
const key = new Key(stack, 'Key', {
2828
enableKeyRotation: true,
2929
enabled: false
3030
});
3131

32-
test.throws(() => new Alias(stack, 'Alias', {
32+
new Alias(stack, 'Alias', {
3333
aliasName: 'foo',
3434
targetKey: key
35+
});
36+
37+
expect(stack).to(haveResource('AWS::KMS::Alias', {
38+
AliasName: 'alias/foo',
39+
TargetKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', 'Arn' ] }
40+
}));
41+
42+
test.done();
43+
},
44+
45+
'can create alias directly while creating the key'(test: Test) {
46+
const app = new App();
47+
const stack = new Stack(app, 'Test');
48+
49+
new Key(stack, 'Key', {
50+
enableKeyRotation: true,
51+
enabled: false,
52+
alias: 'foo',
53+
});
54+
55+
expect(stack).to(haveResource('AWS::KMS::Alias', {
56+
AliasName: 'alias/foo',
57+
TargetKeyId: { 'Fn::GetAtt': [ 'Key961B73FD', 'Arn' ] }
3558
}));
3659

3760
test.done();

packages/@aws-cdk/aws-kms/test/test.key.ts

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ export = {
311311
DeletionPolicy: "Retain",
312312
UpdateReplacePolicy: "Retain",
313313
},
314-
MyKeyAlias1B45D9DA: {
314+
MyKeyAliasaliasxoo9464EB1C: {
315315
Type: "AWS::KMS::Alias",
316316
Properties: {
317317
AliasName: "alias/xoo",
@@ -329,6 +329,103 @@ export = {
329329
test.done();
330330
},
331331

332+
'can run multiple addAlias'(test: Test) {
333+
const app = new App();
334+
const stack = new Stack(app, 'Test');
335+
336+
const key = new Key(stack, 'MyKey', {
337+
enableKeyRotation: true,
338+
enabled: false
339+
});
340+
341+
const alias1 = key.addAlias('alias/alias1');
342+
const alias2 = key.addAlias('alias/alias2');
343+
test.ok(alias1.aliasName);
344+
test.ok(alias2.aliasName);
345+
346+
expect(stack).toMatch({
347+
Resources: {
348+
MyKey6AB29FA6: {
349+
Type: "AWS::KMS::Key",
350+
Properties: {
351+
EnableKeyRotation: true,
352+
Enabled: false,
353+
KeyPolicy: {
354+
Statement: [
355+
{
356+
Action: [
357+
"kms:Create*",
358+
"kms:Describe*",
359+
"kms:Enable*",
360+
"kms:List*",
361+
"kms:Put*",
362+
"kms:Update*",
363+
"kms:Revoke*",
364+
"kms:Disable*",
365+
"kms:Get*",
366+
"kms:Delete*",
367+
"kms:ScheduleKeyDeletion",
368+
"kms:CancelKeyDeletion",
369+
"kms:GenerateDataKey"
370+
],
371+
Effect: "Allow",
372+
Principal: {
373+
AWS: {
374+
"Fn::Join": [
375+
"",
376+
[
377+
"arn:",
378+
{
379+
Ref: "AWS::Partition"
380+
},
381+
":iam::",
382+
{
383+
Ref: "AWS::AccountId"
384+
},
385+
":root"
386+
]
387+
]
388+
}
389+
},
390+
Resource: "*"
391+
}
392+
],
393+
Version: "2012-10-17"
394+
}
395+
},
396+
DeletionPolicy: "Retain",
397+
UpdateReplacePolicy: "Retain",
398+
},
399+
MyKeyAliasaliasalias1668D31D7: {
400+
Type: "AWS::KMS::Alias",
401+
Properties: {
402+
AliasName: "alias/alias1",
403+
TargetKeyId: {
404+
"Fn::GetAtt": [
405+
"MyKey6AB29FA6",
406+
"Arn"
407+
]
408+
}
409+
}
410+
},
411+
MyKeyAliasaliasalias2EC56BD3E: {
412+
Type: "AWS::KMS::Alias",
413+
Properties: {
414+
AliasName: "alias/alias2",
415+
TargetKeyId: {
416+
"Fn::GetAtt": [
417+
"MyKey6AB29FA6",
418+
"Arn"
419+
]
420+
}
421+
}
422+
}
423+
}
424+
});
425+
426+
test.done();
427+
},
428+
332429
'grant decrypt on a key'(test: Test) {
333430
// GIVEN
334431
const stack = new Stack();
@@ -387,7 +484,7 @@ export = {
387484

388485
expect(stack2).toMatch({
389486
Resources: {
390-
MyKeyImportedAliasB1C5269F: {
487+
MyKeyImportedAliasaliashelloD41FEB6C: {
391488
Type: "AWS::KMS::Alias",
392489
Properties: {
393490
AliasName: "alias/hello",

0 commit comments

Comments
 (0)