diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/manifest.json index f60ffe65dd302..8914ced711482 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/manifest.json @@ -18,7 +18,7 @@ "validateOnSynth": false, "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}/eadae23a0571e6a284dc45b98337d3e67ef5d04838b9d04d55ca4bcde0421baf.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/366a6425f7ac8f2ea13c41c91ec578a46fbdae2d8dd46ac2cbd8137eb89ee85b.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -34,10 +34,16 @@ "stepfunctions-cmk-cwl-integ.assets" ], "metadata": { - "/stepfunctions-cmk-cwl-integ/Key/Resource": [ + "/stepfunctions-cmk-cwl-integ/StateMachineKey/Resource": [ { "type": "aws:cdk:logicalId", - "data": "Key961B73FD" + "data": "StateMachineKey3DE756E3" + } + ], + "/stepfunctions-cmk-cwl-integ/LogGroupKey/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LogGroupKeyD49CF390" } ], "/stepfunctions-cmk-cwl-integ/MyLogGroup/Resource": [ @@ -75,6 +81,15 @@ "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } + ], + "Key961B73FD": [ + { + "type": "aws:cdk:logicalId", + "data": "Key961B73FD", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } ] }, "displayName": "stepfunctions-cmk-cwl-integ" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.assets.json index 8dea92adb44e2..80b62d865af78 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "eadae23a0571e6a284dc45b98337d3e67ef5d04838b9d04d55ca4bcde0421baf": { + "366a6425f7ac8f2ea13c41c91ec578a46fbdae2d8dd46ac2cbd8137eb89ee85b": { "source": { "path": "stepfunctions-cmk-cwl-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "eadae23a0571e6a284dc45b98337d3e67ef5d04838b9d04d55ca4bcde0421baf.json", + "objectKey": "366a6425f7ac8f2ea13c41c91ec578a46fbdae2d8dd46ac2cbd8137eb89ee85b.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.template.json index aabecd129013d..6750631708a49 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/stepfunctions-cmk-cwl-integ.template.json @@ -1,6 +1,6 @@ { "Resources": { - "Key961B73FD": { + "StateMachineKey3DE756E3": { "Type": "AWS::KMS::Key", "Properties": { "KeyPolicy": { @@ -95,6 +95,33 @@ "kms:GenerateDataKey*", "kms:ReEncrypt*" ], + "Condition": { + "ArnEquals": { + "kms:EncryptionContext:aws:logs:arn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "MyLogGroup5C0DAD85" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { "Service": { @@ -127,12 +154,60 @@ "UpdateReplacePolicy": "Retain", "DeletionPolicy": "Retain" }, + "LogGroupKeyD49CF390": { + "Type": "AWS::KMS::Key", + "Properties": { + "KeyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Principal": { + "Service": "logs.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, "MyLogGroup5C0DAD85": { "Type": "AWS::Logs::LogGroup", "Properties": { "KmsKeyId": { "Fn::GetAtt": [ - "Key961B73FD", + "LogGroupKeyD49CF390", "Arn" ] }, @@ -197,7 +272,7 @@ "KmsDataKeyReusePeriodSeconds": 75, "KmsKeyId": { "Fn::GetAtt": [ - "Key961B73FD", + "StateMachineKey3DE756E3", "Arn" ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/tree.json index 399e23fd5e9f7..fcc0fa687fca9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.js.snapshot/tree.json @@ -8,13 +8,13 @@ "id": "stepfunctions-cmk-cwl-integ", "path": "stepfunctions-cmk-cwl-integ", "children": { - "Key": { - "id": "Key", - "path": "stepfunctions-cmk-cwl-integ/Key", + "StateMachineKey": { + "id": "StateMachineKey", + "path": "stepfunctions-cmk-cwl-integ/StateMachineKey", "children": { "Resource": { "id": "Resource", - "path": "stepfunctions-cmk-cwl-integ/Key/Resource", + "path": "stepfunctions-cmk-cwl-integ/StateMachineKey/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::KMS::Key", "aws:cdk:cloudformation:props": { @@ -110,6 +110,33 @@ "kms:GenerateDataKey*", "kms:ReEncrypt*" ], + "Condition": { + "ArnEquals": { + "kms:EncryptionContext:aws:logs:arn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:", + { + "Ref": "MyLogGroup5C0DAD85" + } + ] + ] + } + } + }, "Effect": "Allow", "Principal": { "Service": { @@ -151,6 +178,70 @@ "version": "0.0.0" } }, + "LogGroupKey": { + "id": "LogGroupKey", + "path": "stepfunctions-cmk-cwl-integ/LogGroupKey", + "children": { + "Resource": { + "id": "Resource", + "path": "stepfunctions-cmk-cwl-integ/LogGroupKey/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::KMS::Key", + "aws:cdk:cloudformation:props": { + "keyPolicy": { + "Statement": [ + { + "Action": "kms:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ReEncrypt*" + ], + "Effect": "Allow", + "Principal": { + "Service": "logs.amazonaws.com" + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.CfnKey", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_kms.Key", + "version": "0.0.0" + } + }, "MyLogGroup": { "id": "MyLogGroup", "path": "stepfunctions-cmk-cwl-integ/MyLogGroup", @@ -163,7 +254,7 @@ "aws:cdk:cloudformation:props": { "kmsKeyId": { "Fn::GetAtt": [ - "Key961B73FD", + "LogGroupKeyD49CF390", "Arn" ] }, @@ -295,7 +386,7 @@ "encryptionConfiguration": { "kmsKeyId": { "Fn::GetAtt": [ - "Key961B73FD", + "StateMachineKey3DE756E3", "Arn" ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.ts index ad5bd629c8d92..e3b78ede1b803 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-stepfunctions/test/integ.state-machine-cmk-with-cwl-encryption.ts @@ -1,28 +1,37 @@ import * as cdk from 'aws-cdk-lib'; import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; +import * as iam from 'aws-cdk-lib/aws-iam'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as logs from 'aws-cdk-lib/aws-logs'; import { IntegTest } from '@aws-cdk/integ-tests-alpha'; class KMSStateMachine extends cdk.Stack { readonly stateMachine: sfn.StateMachine; - readonly kmsKey: kms.Key; + readonly stateMachineKmsKey: kms.Key; + readonly logGroupKmsKey: kms.Key; readonly logGroup: logs.LogGroup; constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); - this.kmsKey = new kms.Key(this, 'Key'); + this.stateMachineKmsKey = new kms.Key(this, 'StateMachineKey'); + this.logGroupKmsKey = new kms.Key(this, 'LogGroupKey'); + /** + * We need to grant the service principal encrypt and decrypt permissions since passing + * a KMS key when creating a LogGroup doesn't automatically grant the service principal encrypt/decrypt permissions + * see: https://github.com/aws/aws-cdk/issues/28304 + * */ + this.logGroupKmsKey.grantEncryptDecrypt(new iam.ServicePrincipal('logs.amazonaws.com')); this.logGroup = new logs.LogGroup(this, 'MyLogGroup', { logGroupName: '/aws/vendedlogs/states/MyLogGroup', - encryptionKey: this.kmsKey, + encryptionKey: this.logGroupKmsKey, }); this.stateMachine = new sfn.StateMachine(this, 'StateMachineWithCMKWithCWLEncryption', { stateMachineName: 'StateMachineWithCMKWithCWLEncryption', definitionBody: sfn.DefinitionBody.fromChainable(sfn.Chain.start(new sfn.Pass(this, 'Pass'))), stateMachineType: sfn.StateMachineType.STANDARD, - kmsKey: this.kmsKey, + kmsKey: this.stateMachineKmsKey, kmsDataKeyReusePeriodSeconds: cdk.Duration.seconds(75), enableEncryptedLogging: true, logs: { diff --git a/packages/aws-cdk-lib/aws-stepfunctions/README.md b/packages/aws-cdk-lib/aws-stepfunctions/README.md index a26dfbfb9a681..4708d159240f0 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions/README.md +++ b/packages/aws-cdk-lib/aws-stepfunctions/README.md @@ -122,23 +122,32 @@ const stateMachine = new sfn.StateMachine(this, 'StateMachineWithCMKEncryptionCo ``` ### Creating a StateMachine with CWL Encryption using a Customer Managed Key, -You can encrypt data sent to CloudWatch Logs. To use encrypted logging, you must set `enableEncryptedLogging` to `true` and provide the `logs?` prop: +You can encrypt data sent to CloudWatch Logs. To use encrypted logging, you must set `enableEncryptedLogging` to `true` and provide the `logs?` prop. ``` -const kmsKey = new kms.Key(this, 'Key'); +const stateMachineKmsKey = new kms.Key(this, 'StateMachineKey'); +const logGroupKmsKey = new kms.Key(this, 'LogGroupKmsKey'); + +/** + * We need to grant the service principal encrypt and decrypt permissions since passing + * a KMS key when creating a LogGroup doesn't automatically grant the service principal encrypt/decrypt permissions + * see: https://github.com/aws/aws-cdk/issues/28304 + * +*/ +logGroupKmsKey.grantEncryptDecrypt(new iam.ServicePrincipal('logs.amazonaws.com')); const logGroup = new logs.LogGroup(this, 'MyLogGroup', { logGroupName: '/aws/vendedlogs/states/MyLogGroup', - encryptionKey: kmsKey, + encryptionKey: logGroupKmsKey, }); const stateMachine = new sfn.StateMachine(this, 'StateMachineWithCMKWithCWLEncryption', { stateMachineName: 'StateMachineWithCMKWithCWLEncryption', definitionBody: sfn.DefinitionBody.fromChainable(sfn.Chain.start(new sfn.Pass(this, 'Pass'))), stateMachineType: sfn.StateMachineType.STANDARD, - kmsKey: this.kmsKey, + kmsKey: stateMachineKmsKey, kmsDataKeyReusePeriodSeconds: cdk.Duration.seconds(75), enableEncryptedLogging: true, logs: { - destination: this.logGroup, + destination: logGroup, level: sfn.LogLevel.FATAL, includeExecutionData: false, }, diff --git a/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts b/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts index 2fc3bfd97cc3a..361e73ce57b66 100644 --- a/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts +++ b/packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts @@ -519,6 +519,16 @@ export class StateMachine extends StateMachineBase { resources: ['*'], actions: ['kms:Encrypt*', 'kms:Decrypt*', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:Describe*'], principals: [new iam.ServicePrincipal(`logs.${Stack.of(this).region}.amazonaws.com`)], + conditions: { + ArnEquals: { + 'kms:EncryptionContext:aws:logs:arn': Stack.of(this).formatArn({ + service: 'logs', + resource: 'log-group', + sep: ':', + resourceName: props.logs.destination.logGroupName, + }), + }, + }, })); props.kmsKey.addToResourcePolicy(new iam.PolicyStatement({