diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/integ.json new file mode 100644 index 0000000000000..a023d9784bef3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "enableLookups": true, + "version": "38.0.1", + "testCases": { + "key-from-lookup-dummy-integ/DefaultTest": { + "stacks": [ + "key-from-lookup-dummy" + ], + "assertionStack": "key-from-lookup-dummy-integ/DefaultTest/DeployAssert", + "assertionStackName": "keyfromlookupdummyintegDefaultTestDeployAssert59B83056" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.assets.json new file mode 100644 index 0000000000000..1675ef6086d66 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.assets.json @@ -0,0 +1,20 @@ +{ + "version": "38.0.1", + "files": { + "108859781ef94bf4058c98d631d659c64aaa91ada86b02972458aeb561fc24da": { + "source": { + "path": "key-from-lookup-dummy.template.json", + "packaging": "file" + }, + "destinations": { + "12345678-test-region": { + "bucketName": "cdk-hnb659fds-assets-12345678-test-region", + "objectKey": "108859781ef94bf4058c98d631d659c64aaa91ada86b02972458aeb561fc24da.json", + "region": "test-region", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-test-region" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.template.json new file mode 100644 index 0000000000000..4e2669a9be1cf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/key-from-lookup-dummy.template.json @@ -0,0 +1,41 @@ +{ + "Outputs": { + "KeyId": { + "Value": "1234abcd-12ab-34cd-56ef-1234567890ab" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets.json new file mode 100644 index 0000000000000..063d18ecc9628 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/keyfromlookupdummyintegDefaultTestDeployAssert59B83056.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/manifest.json new file mode 100644 index 0000000000000..db3d05518cb56 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "38.0.1", + "artifacts": { + "key-from-lookup-dummy.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "key-from-lookup-dummy.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "key-from-lookup-dummy": { + "type": "aws:cloudformation:stack", + "environment": "aws://12345678/test-region", + "properties": { + "templateFile": "key-from-lookup-dummy.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-test-region", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-test-region", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-test-region/108859781ef94bf4058c98d631d659c64aaa91ada86b02972458aeb561fc24da.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "key-from-lookup-dummy.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-test-region", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "key-from-lookup-dummy.assets" + ], + "metadata": { + "/key-from-lookup-dummy/KeyId": [ + { + "type": "aws:cdk:logicalId", + "data": "KeyId" + } + ], + "/key-from-lookup-dummy/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/key-from-lookup-dummy/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "key-from-lookup-dummy" + }, + "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "keyfromlookupdummyintegDefaultTestDeployAssert59B83056": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "keyfromlookupdummyintegDefaultTestDeployAssert59B83056.assets" + ], + "metadata": { + "/key-from-lookup-dummy-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/key-from-lookup-dummy-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "key-from-lookup-dummy-integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + }, + "missing": [ + { + "key": "key-provider:account=12345678:aliasName=alias/foo:region=test-region", + "provider": "key-provider", + "props": { + "dummyValue": { + "keyId": "1234abcd-12ab-34cd-56ef-1234567890ab" + }, + "ignoreErrorOnMissingContext": true, + "account": "12345678", + "region": "test-region", + "aliasName": "alias/foo", + "lookupRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-test-region" + } + } + ] +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/tree.json new file mode 100644 index 0000000000000..d5f07caf4914b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.js.snapshot/tree.json @@ -0,0 +1,117 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "key-from-lookup-dummy": { + "id": "key-from-lookup-dummy", + "path": "key-from-lookup-dummy", + "children": { + "Key": { + "id": "Key", + "path": "key-from-lookup-dummy/Key", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "KeyId": { + "id": "KeyId", + "path": "key-from-lookup-dummy/KeyId", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "key-from-lookup-dummy/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "key-from-lookup-dummy/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "key-from-lookup-dummy-integ": { + "id": "key-from-lookup-dummy-integ", + "path": "key-from-lookup-dummy-integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "key-from-lookup-dummy-integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "key-from-lookup-dummy-integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "key-from-lookup-dummy-integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "key-from-lookup-dummy-integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "key-from-lookup-dummy-integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.ts new file mode 100644 index 0000000000000..dfa90c95fe79f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-kms/test/integ.key-from-lookup-dummy.ts @@ -0,0 +1,21 @@ +import * as cdk from 'aws-cdk-lib/core'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { Key } from 'aws-cdk-lib/aws-kms'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'key-from-lookup-dummy', { + env: { + account: process.env.CDK_INTEG_ACCOUNT ?? process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_INTEG_REGION ?? process.env.CDK_DEFAULT_REGION, + }, +}); + +const dummy = Key.fromLookup(stack, 'Key', { aliasName: 'alias/foo', returnDummyKeyOnMissing: true }); + +new cdk.CfnOutput(stack, 'KeyId', { value: dummy.keyId }); + +new integ.IntegTest(app, 'key-from-lookup-dummy-integ', { + enableLookups: true, + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-kms/README.md b/packages/aws-cdk-lib/aws-kms/README.md index ae78310e96917..a149a74b5ca79 100644 --- a/packages/aws-cdk-lib/aws-kms/README.md +++ b/packages/aws-cdk-lib/aws-kms/README.md @@ -123,6 +123,24 @@ Note that a call to `.addToResourcePolicy(statement)` on `myKeyLookup` will not an affect on the key's policy because it is not owned by your stack. The call will be a no-op. +If the target key is not found in your account, an error will be thrown. +To prevent the error in the case, you can receive a dummy key without the error +by setting `returnDummyKeyOnMissing` to `true`. The dummy key has a `keyId` of +`1234abcd-12ab-34cd-56ef-1234567890ab`. The value of the dummy key id can also be +referenced using the `Key.DEFAULT_DUMMY_KEY_ID` variable, and you can check if the +key is a dummy key by using the `Key.isLookupDummy()` method. + +```ts +const dummy = kms.Key.fromLookup(this, 'MyKeyLookup', { + aliasName: 'alias/NonExistentAlias', + returnDummyKeyOnMissing: true, +}); + +if (kms.Key.isLookupDummy(dummy)) { + // alternative process +} +``` + ## Key Policies Controlling access and usage of KMS Keys requires the use of key policies (resource-based policies attached to the key); diff --git a/packages/aws-cdk-lib/aws-kms/lib/key-lookup.ts b/packages/aws-cdk-lib/aws-kms/lib/key-lookup.ts index 0ac02b2185d49..0825742e3be46 100644 --- a/packages/aws-cdk-lib/aws-kms/lib/key-lookup.ts +++ b/packages/aws-cdk-lib/aws-kms/lib/key-lookup.ts @@ -8,4 +8,18 @@ export interface KeyLookupOptions { * Must be in the format `alias/`. */ readonly aliasName: string; + + /** + * Whether to return a dummy key if the key was not found. + * + * If it is set to `true` and the key was not found, a dummy + * key with a key id '1234abcd-12ab-34cd-56ef-1234567890ab' + * will be returned. The value of the dummy key id can also + * be referenced using the `Key.DEFAULT_DUMMY_KEY_ID` variable, + * and you can check if the key is a dummy key by using the + * `Key.isLookupDummy()` method. + * + * @default false + */ + readonly returnDummyKeyOnMissing?: boolean; } diff --git a/packages/aws-cdk-lib/aws-kms/lib/key.ts b/packages/aws-cdk-lib/aws-kms/lib/key.ts index 7cdd14fc546e0..059bee9794f76 100644 --- a/packages/aws-cdk-lib/aws-kms/lib/key.ts +++ b/packages/aws-cdk-lib/aws-kms/lib/key.ts @@ -554,6 +554,14 @@ export interface KeyProps { * @resource AWS::KMS::Key */ export class Key extends KeyBase { + /** + * The default key id of the dummy key. + * + * This value is used as a dummy key id if the key was not found + * by the `Key.fromLookup()` method. + */ + public static readonly DEFAULT_DUMMY_KEY_ID = '1234abcd-12ab-34cd-56ef-1234567890ab'; + /** * Import an externally defined KMS Key using its ARN. * @@ -651,6 +659,12 @@ export class Key extends KeyBase { * You can therefore not use any values that will only be available at * CloudFormation execution time (i.e., Tokens). * + * If you set `returnDummyKeyOnMissing` to `true` in `options` and the key was not found, + * this method will return a dummy key with a key id '1234abcd-12ab-34cd-56ef-1234567890ab'. + * The value of the dummy key id can also be referenced using the `Key.DEFAULT_DUMMY_KEY_ID` + * variable, and you can check if the key is a dummy key by using the `Key.isLookupDummy()` + * method. + * * The Key information will be cached in `cdk.context.json` and the same Key * will be used on future runs. To refresh the lookup, you will have to * evict the value from the cache using the `cdk context` command. See @@ -683,14 +697,26 @@ export class Key extends KeyBase { aliasName: options.aliasName, } as cxschema.KeyContextQuery, dummyValue: { - keyId: '1234abcd-12ab-34cd-56ef-1234567890ab', + keyId: Key.DEFAULT_DUMMY_KEY_ID, }, + ignoreErrorOnMissingContext: options.returnDummyKeyOnMissing, }).value; return new Import(attributes.keyId, Arn.format({ resource: 'key', service: 'kms', resourceName: attributes.keyId }, Stack.of(scope))); } + /** + * Checks if the key returned by the `Key.fromLookup()` method is a dummy key, + * i.e., a key that was not found. + * + * This method can only be used if the `returnDummyKeyOnMissing` option + * is set to `true` in the `options` for the `Key.fromLookup()` method. + */ + public static isLookupDummy(key: IKey): boolean { + return key.keyId === Key.DEFAULT_DUMMY_KEY_ID; + } + public readonly keyArn: string; public readonly keyId: string; protected readonly policy?: iam.PolicyDocument; diff --git a/packages/aws-cdk-lib/aws-kms/test/key.from-lookup.test.ts b/packages/aws-cdk-lib/aws-kms/test/key.from-lookup.test.ts index 34af7d0030d9f..aadb45daf7f2a 100644 --- a/packages/aws-cdk-lib/aws-kms/test/key.from-lookup.test.ts +++ b/packages/aws-cdk-lib/aws-kms/test/key.from-lookup.test.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import * as cxschema from '../../cloud-assembly-schema'; -import { ContextProvider, GetContextValueOptions, GetContextValueResult, Lazy, Stack } from '../../core'; +import { App, ContextProvider, GetContextValueOptions, GetContextValueResult, Lazy, Stack } from '../../core'; import * as cxapi from '../../cx-api'; import { Key } from '../lib'; @@ -39,6 +39,65 @@ test('return correct key', () => { restoreContextProvider(previous); }); +test('return dummy key if returnDummyKeyOnMissing is true', () => { + const app = new App(); + const stack = new Stack(app, 'MyStack', { env: { region: 'us-east-1', account: '123456789012' } }); + const key = Key.fromLookup(stack, 'Key', { + aliasName: 'alias/foo', + returnDummyKeyOnMissing: true, + }); + + expect(key.keyId).toEqual(Key.DEFAULT_DUMMY_KEY_ID); + expect(app.synth().manifest.missing).toEqual([ + { + key: 'key-provider:account=123456789012:aliasName=alias/foo:region=us-east-1', + props: { + account: '123456789012', + aliasName: 'alias/foo', + ignoreErrorOnMissingContext: true, + lookupRoleArn: 'arn:${AWS::Partition}:iam::123456789012:role/cdk-hnb659fds-lookup-role-123456789012-us-east-1', + dummyValue: { + keyId: '1234abcd-12ab-34cd-56ef-1234567890ab', + }, + region: 'us-east-1', + }, + provider: 'key-provider', + }, + ]); +}); + +describe('isLookupDummy method', () => { + test('return false if the lookup key is not a dummy key', () => { + const previous = mockKeyContextProviderWith({ + keyId: '12345678-1234-1234-1234-123456789012', + }, options => { + expect(options.aliasName).toEqual('alias/foo'); + }); + + const app = new App(); + const stack = new Stack(app, 'MyStack', { env: { region: 'us-east-1', account: '123456789012' } }); + const key = Key.fromLookup(stack, 'Key', { + aliasName: 'alias/foo', + returnDummyKeyOnMissing: true, + }); + + expect(Key.isLookupDummy(key)).toEqual(false); + + restoreContextProvider(previous); + }); + + test('return true if the lookup key is a dummy key', () => { + const app = new App(); + const stack = new Stack(app, 'MyStack', { env: { region: 'us-east-1', account: '123456789012' } }); + const key = Key.fromLookup(stack, 'Key', { + aliasName: 'alias/foo', + returnDummyKeyOnMissing: true, + }); + + expect(Key.isLookupDummy(key)).toEqual(true); + }); +}); + interface MockKeyContextResponse { readonly keyId: string; } diff --git a/packages/aws-cdk-lib/core/lib/context-provider.ts b/packages/aws-cdk-lib/core/lib/context-provider.ts index fb4cdd00496c2..b58405098174b 100644 --- a/packages/aws-cdk-lib/core/lib/context-provider.ts +++ b/packages/aws-cdk-lib/core/lib/context-provider.ts @@ -117,7 +117,7 @@ export class ContextProvider { // We'll store the extendedProps in the missingContextKey report // so that we can retrieve the dummyValue and ignoreError flag - // in the aws-cdk's ssm-context provider + // in the aws-cdk's ssm-context and kms key provider stack.reportMissingContextKey({ key, provider: options.provider as cxschema.ContextProvider, diff --git a/packages/aws-cdk/lib/context-providers/keys.ts b/packages/aws-cdk/lib/context-providers/keys.ts index 52c5dd08f140d..270714f7be3a2 100644 --- a/packages/aws-cdk/lib/context-providers/keys.ts +++ b/packages/aws-cdk/lib/context-providers/keys.ts @@ -40,6 +40,12 @@ export class KeyContextProviderPlugin implements ContextProviderPlugin { nextMarker = response.NextMarker; } while (response.Truncated); + const suppressError = 'ignoreErrorOnMissingContext' in args && args.ignoreErrorOnMissingContext as boolean; + const hasDummyKeyId = 'dummyValue' in args && typeof args.dummyValue === 'object' && args.dummyValue !== null && 'keyId' in args.dummyValue; + if (suppressError && hasDummyKeyId) { + const keyId = (args.dummyValue as { keyId: string }).keyId; + return { TargetKeyId: keyId }; + } throw new Error(`Could not find any key with alias named ${args.aliasName}`); } diff --git a/packages/aws-cdk/test/context-providers/keys.test.ts b/packages/aws-cdk/test/context-providers/keys.test.ts index 2503110745963..d46ea86b3f2f6 100644 --- a/packages/aws-cdk/test/context-providers/keys.test.ts +++ b/packages/aws-cdk/test/context-providers/keys.test.ts @@ -152,5 +152,32 @@ test('throw exception - no key found', async () => { region: 'us-east-1', aliasName: 'alias/foo', })).rejects.toThrow(/Could not find any key with alias named/); +}); + +test('don\'t throw exception - no key found but ignoreErrorOnMissingContext is true', async () => { + // GIVEN + const provider = new KeyContextProviderPlugin(mockSDK); + + AWS.mock('KMS', 'listAliases', (params: aws.KMS.ListAliasesRequest, cb: AwsCallback) => { + expect(params.KeyId).toBeUndefined(); + return cb(null, { + }); + }); + // WHEN + const args = { + account: '1234', + region: 'us-east-1', + aliasName: 'alias/foo', + dummyValue: { + keyId: '1234abcd-12ab-34cd-56ef-1234567890ab', + }, + ignoreErrorOnMissingContext: true, + }; + const result = await provider.getValue(args); + + // THEN + expect(result).toEqual({ + keyId: '1234abcd-12ab-34cd-56ef-1234567890ab', + }); }); \ No newline at end of file