From 9076d6e522002357da307bc09417a5b12bcfb35e Mon Sep 17 00:00:00 2001 From: GZ Date: Thu, 14 Mar 2024 15:17:20 -0700 Subject: [PATCH 1/8] fix(kms): kms key grant methods misidentify region when enclosing stack is different region (#29315) ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/29308 ### Reason for this change This problem is grant() determines the region of a Key using Stack.of(key).region, however the enclosing Stack's region may differ to that of the actual resource. When this happens, the IAM policy generated allows a `*` resource which is against the least privilege rule. ### Description of changes KMS key already has `env` value on account and region, use this first. If not exist, use stack account and region. ### Description of how you validated changes New unit test ### 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* --------- Co-authored-by: paulhcsun <47882901+paulhcsun@users.noreply.github.com> --- packages/aws-cdk-lib/aws-kms/lib/key.ts | 12 ++++ packages/aws-cdk-lib/aws-kms/test/key.test.ts | 61 +++++++++++++++++++ packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 18 +++++- packages/aws-cdk-lib/cx-api/README.md | 17 ++++++ packages/aws-cdk-lib/cx-api/lib/features.ts | 13 ++++ 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-kms/lib/key.ts b/packages/aws-cdk-lib/aws-kms/lib/key.ts index 1f2c13e0615c7..ba5d33c2988e9 100644 --- a/packages/aws-cdk-lib/aws-kms/lib/key.ts +++ b/packages/aws-cdk-lib/aws-kms/lib/key.ts @@ -262,6 +262,12 @@ abstract class KeyBase extends Resource implements IKey { } const bucketStack = Stack.of(this); const identityStack = Stack.of(grantee.grantPrincipal); + + if (FeatureFlags.of(this).isEnabled(cxapi.KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE)) { + // if two compared stacks have the same region, this should return 'false' since it's from the + // same region; if two stacks have different region, then compare env.region + return bucketStack.region !== identityStack.region && this.env.region !== identityStack.region; + } return bucketStack.region !== identityStack.region; } @@ -271,6 +277,12 @@ abstract class KeyBase extends Resource implements IKey { } const bucketStack = Stack.of(this); const identityStack = Stack.of(grantee.grantPrincipal); + + if (FeatureFlags.of(this).isEnabled(cxapi.KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE)) { + // if two compared stacks have the same region, this should return 'false' since it's from the + // same region; if two stacks have different region, then compare env.account + return bucketStack.account !== identityStack.account && this.env.account !== identityStack.account; + } return bucketStack.account !== identityStack.account; } } diff --git a/packages/aws-cdk-lib/aws-kms/test/key.test.ts b/packages/aws-cdk-lib/aws-kms/test/key.test.ts index 9c0ce901dd910..3493c31dff3ac 100644 --- a/packages/aws-cdk-lib/aws-kms/test/key.test.ts +++ b/packages/aws-cdk-lib/aws-kms/test/key.test.ts @@ -2,6 +2,7 @@ import { describeDeprecated } from '@aws-cdk/cdk-build-tools'; import { Match, Template } from '../../assertions'; import * as iam from '../../aws-iam'; import * as cdk from '../../core'; +import * as cxapi from '../../cx-api'; import * as kms from '../lib'; import { KeySpec, KeyUsage } from '../lib'; @@ -81,6 +82,66 @@ describe('key policies', () => { }); }); + test('cross region key with iam role grant', () => { + const app = new cdk.App({ context: { [cxapi.KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE]: true } }); + const stack = new cdk.Stack(app, 'test-stack', { env: { account: '000000000000', region: 'us-west-2' } }); + const key = kms.Key.fromKeyArn( + stack, + 'Key', + 'arn:aws:kms:eu-north-1:000000000000:key/e3ab59e5-3dc3-4bc4-9c3f-c790231d2287', + ); + + const roleStack = new cdk.Stack(app, 'RoleStack', { + env: { account: '000000000000', region: 'eu-north-1' }, + }); + const role = new iam.Role(roleStack, 'Role', { + assumedBy: new iam.AccountPrincipal('000000000000'), + }); + key.grantEncryptDecrypt(role); + + Template.fromStack(roleStack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Effect: 'Allow', + Resource: 'arn:aws:kms:eu-north-1:000000000000:key/e3ab59e5-3dc3-4bc4-9c3f-c790231d2287', + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('cross region key with iam role grant when feature flag is disabled', () => { + const app = new cdk.App({ context: { [cxapi.KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE]: false } }); + const stack = new cdk.Stack(app, 'test-stack', { env: { account: '000000000000', region: 'us-west-2' } }); + const key = kms.Key.fromKeyArn( + stack, + 'Key', + 'arn:aws:kms:eu-north-1:000000000000:key/e3ab59e5-3dc3-4bc4-9c3f-c790231d2287', + ); + + const roleStack = new cdk.Stack(app, 'RoleStack', { + env: { account: '000000000000', region: 'eu-north-1' }, + }); + const role = new iam.Role(roleStack, 'Role', { + assumedBy: new iam.AccountPrincipal('000000000000'), + }); + key.grantEncryptDecrypt(role); + + Template.fromStack(roleStack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }); + }); + test('can append to the default key policy', () => { const stack = new cdk.Stack(); const statement = new iam.PolicyStatement({ resources: ['*'], actions: ['kms:Put*'] }); diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 86bb8373fa803..d73e3b1657e52 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -66,6 +66,7 @@ Flags come in three types: | [@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction](#aws-cdkaws-cloudwatch-actionschangelambdapermissionlogicalidforlambdaaction) | When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID. | 2.124.0 | (fix) | | [@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse](#aws-cdkaws-codepipelinecrossaccountkeysdefaultvaluetofalse) | Enables Pipeline to set the default value for crossAccountKeys to false. | 2.127.0 | (default) | | [@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2](#aws-cdkaws-codepipelinedefaultpipelinetypetov2) | Enables Pipeline to set the default pipeline type to V2. | V2NEXT | (default) | +| [@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope](#aws-cdkaws-kmsreducecrossaccountregionpolicyscope) | When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only. | V2NEXT | (fix) | @@ -122,7 +123,8 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, - "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true } } ``` @@ -1249,4 +1251,18 @@ construct, the construct automatically defaults the value of this property to `P **Compatibility with old behavior:** Pass `pipelineType: PipelineType.V1` to `Pipeline` construct to restore the previous behavior. +### @aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope + +*When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only.* (fix) + +When this feature flag is enabled and calling KMS key grant method, the created IAM policy will reduce the resource scope from +'*' to this specific granting KMS key. + + +| Since | Default | Recommended | +| ----- | ----- | ----- | +| (not in v1) | | | +| V2NEXT | `false` | `true` | + + diff --git a/packages/aws-cdk-lib/cx-api/README.md b/packages/aws-cdk-lib/cx-api/README.md index 394a47009b24d..cdbd86f3ae08e 100644 --- a/packages/aws-cdk-lib/cx-api/README.md +++ b/packages/aws-cdk-lib/cx-api/README.md @@ -292,3 +292,20 @@ _cdk.json_ } } ``` + +* `@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope` + +Reduce resource scope of the IAM Policy created from KMS key grant to granting key only. + +When this feature flag is enabled and calling KMS key grant method, the created IAM policy will reduce the resource scope from +'*' to this specific granting KMS key. + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true + } +} +``` diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 28b15de29a57c..7a241858c5f82 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -100,6 +100,7 @@ export const CODECOMMIT_SOURCE_ACTION_DEFAULT_BRANCH_NAME = '@aws-cdk/aws-codepi export const LAMBDA_PERMISSION_LOGICAL_ID_FOR_LAMBDA_ACTION = '@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction'; export const CODEPIPELINE_CROSS_ACCOUNT_KEYS_DEFAULT_VALUE_TO_FALSE = '@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse'; export const CODEPIPELINE_DEFAULT_PIPELINE_TYPE_TO_V2 = '@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2'; +export const KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE = '@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope'; export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// @@ -1021,6 +1022,18 @@ export const FLAGS: Record = { recommendedValue: true, compatibilityWithOldBehaviorMd: 'Pass `pipelineType: PipelineType.V1` to `Pipeline` construct to restore the previous behavior.', }, + + ////////////////////////////////////////////////////////////////////// + [KMS_REDUCE_CROSS_ACCOUNT_REGION_POLICY_SCOPE]: { + type: FlagType.BugFix, + summary: 'When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only.', + detailsMd: ` + When this feature flag is enabled and calling KMS key grant method, the created IAM policy will reduce the resource scope from + '*' to this specific granting KMS key. + `, + introducedIn: { v2: 'V2NEXT' }, + recommendedValue: true, + }, }; const CURRENT_MV = 'v2'; From bc9d0b44ef0717c6bd98fd37ab7883d830094461 Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Fri, 15 Mar 2024 07:46:35 +0900 Subject: [PATCH 2/8] feat(rds): eliminating the need for explicit `secret.grantRead()` invokes when using DataAPI with Aurora cluster (#29399) ### Issue # (if applicable) Closes #29362. ### Reason for this change As discussed [there](https://github.com/aws/aws-cdk/pull/29338#discussion_r1512026791), we should invoke `secret.grantRead()` explicitly when using DataAPI with Aurora cluster. Because it's inconvenient for users, I made `secret.grantRead()` be invoked within `cluster.grantDataApiAccess()`. ### Description of changes - move `cluster.secret` from `DatabaseClusterNew` to `DatabaseClusterBase` to use it within `DatabaseClusterBase.grantDataApiAccess()` - add `secret.grantRead()` in `cluster.grantDataApiAccess()` - add `secret` property to `DatabaseClusterAttributes` #### Points of concern `DatabaseClusterBase` class is extended by `ImportedDatabaseCluster` class. Therefore, it is necessary to define `ImportedDatabaseCluster.secret`. I simply added `secret` props to `DatabaseClusterAttributes` but I cannot believe this is the best way. Other ways are.. - add `secretArn` to `DatabaseClusterAttributes` - don't add secret info and `ImportedDatabaseCluster.secret` becomes always undefined ### Description of how you validated changes ### 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* --- ...sets.json => cluster-data-api.assets.json} | 6 +- ...te.json => cluster-data-api.template.json} | 48 ++--- .../integ.json | 2 +- .../manifest.json | 90 ++++----- .../tree.json | 174 +++++++++--------- .../aws-rds/test/integ.cluster-data-api.ts | 3 +- packages/aws-cdk-lib/aws-rds/README.md | 2 - .../aws-cdk-lib/aws-rds/lib/cluster-ref.ts | 7 + packages/aws-cdk-lib/aws-rds/lib/cluster.ts | 13 +- .../aws-cdk-lib/aws-rds/test/cluster.test.ts | 10 + 10 files changed, 186 insertions(+), 169 deletions(-) rename packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/{cluster-kerberos.assets.json => cluster-data-api.assets.json} (82%) rename packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/{cluster-kerberos.template.json => cluster-data-api.template.json} (95%) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.assets.json similarity index 82% rename from packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.assets.json rename to packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.assets.json index df5838befd59b..b800063a0def3 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.assets.json @@ -14,15 +14,15 @@ } } }, - "30c06211c853a3fd7c30a7376cbd63b2155519a67ba90ed0a731aa67dcd179e2": { + "b2fb40833cc33946a3656aa5e2cdb19e4259b9a84dbabe7a6559ad319344359d": { "source": { - "path": "cluster-kerberos.template.json", + "path": "cluster-data-api.template.json", "packaging": "file" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "30c06211c853a3fd7c30a7376cbd63b2155519a67ba90ed0a731aa67dcd179e2.json", + "objectKey": "b2fb40833cc33946a3656aa5e2cdb19e4259b9a84dbabe7a6559ad319344359d.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-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.template.json similarity index 95% rename from packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.template.json rename to packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.template.json index 43e8e05ad0450..76ea729cef991 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-kerberos.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/cluster-data-api.template.json @@ -10,7 +10,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC" + "Value": "cluster-data-api/VPC" } ] } @@ -39,7 +39,7 @@ }, { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet1" + "Value": "cluster-data-api/VPC/PublicSubnet1" } ], "VpcId": { @@ -53,7 +53,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet1" + "Value": "cluster-data-api/VPC/PublicSubnet1" } ], "VpcId": { @@ -94,7 +94,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet1" + "Value": "cluster-data-api/VPC/PublicSubnet1" } ] } @@ -114,7 +114,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet1" + "Value": "cluster-data-api/VPC/PublicSubnet1" } ] }, @@ -147,7 +147,7 @@ }, { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet2" + "Value": "cluster-data-api/VPC/PublicSubnet2" } ], "VpcId": { @@ -161,7 +161,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet2" + "Value": "cluster-data-api/VPC/PublicSubnet2" } ], "VpcId": { @@ -202,7 +202,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet2" + "Value": "cluster-data-api/VPC/PublicSubnet2" } ] } @@ -222,7 +222,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PublicSubnet2" + "Value": "cluster-data-api/VPC/PublicSubnet2" } ] }, @@ -255,7 +255,7 @@ }, { "Key": "Name", - "Value": "cluster-kerberos/VPC/PrivateSubnet1" + "Value": "cluster-data-api/VPC/PrivateSubnet1" } ], "VpcId": { @@ -269,7 +269,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PrivateSubnet1" + "Value": "cluster-data-api/VPC/PrivateSubnet1" } ], "VpcId": { @@ -324,7 +324,7 @@ }, { "Key": "Name", - "Value": "cluster-kerberos/VPC/PrivateSubnet2" + "Value": "cluster-data-api/VPC/PrivateSubnet2" } ], "VpcId": { @@ -338,7 +338,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC/PrivateSubnet2" + "Value": "cluster-data-api/VPC/PrivateSubnet2" } ], "VpcId": { @@ -375,7 +375,7 @@ "Tags": [ { "Key": "Name", - "Value": "cluster-kerberos/VPC" + "Value": "cluster-data-api/VPC" } ] } @@ -543,6 +543,16 @@ "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "DatabaseSecretAttachmentE5D1B020" + } + }, { "Action": [ "rds-data:BatchExecuteStatement", @@ -575,16 +585,6 @@ ] ] } - }, - { - "Action": [ - "secretsmanager:DescribeSecret", - "secretsmanager:GetSecretValue" - ], - "Effect": "Allow", - "Resource": { - "Ref": "DatabaseSecretAttachmentE5D1B020" - } } ], "Version": "2012-10-17" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/integ.json index 9d07906800ea0..f58e123a17c69 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/integ.json @@ -3,7 +3,7 @@ "testCases": { "integ-cluster-data-api/DefaultTest": { "stacks": [ - "cluster-kerberos" + "cluster-data-api" ], "assertionStack": "integ-cluster-data-api/DefaultTest/DeployAssert", "assertionStackName": "integclusterdataapiDefaultTestDeployAssertACD87C08" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/manifest.json index 97dcacd1833fa..873f1cd7cb6c8 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/manifest.json @@ -1,28 +1,28 @@ { "version": "36.0.0", "artifacts": { - "cluster-kerberos.assets": { + "cluster-data-api.assets": { "type": "cdk:asset-manifest", "properties": { - "file": "cluster-kerberos.assets.json", + "file": "cluster-data-api.assets.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" } }, - "cluster-kerberos": { + "cluster-data-api": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "cluster-kerberos.template.json", + "templateFile": "cluster-data-api.template.json", "terminationProtection": false, "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}/30c06211c853a3fd7c30a7376cbd63b2155519a67ba90ed0a731aa67dcd179e2.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b2fb40833cc33946a3656aa5e2cdb19e4259b9a84dbabe7a6559ad319344359d.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ - "cluster-kerberos.assets" + "cluster-data-api.assets" ], "lookupRole": { "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", @@ -31,233 +31,233 @@ } }, "dependencies": [ - "cluster-kerberos.assets" + "cluster-data-api.assets" ], "metadata": { - "/cluster-kerberos/VPC/Resource": [ + "/cluster-data-api/VPC/Resource": [ { "type": "aws:cdk:logicalId", "data": "VPCB9E5F0B4" } ], - "/cluster-kerberos/VPC/PublicSubnet1/Subnet": [ + "/cluster-data-api/VPC/PublicSubnet1/Subnet": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1SubnetB4246D30" } ], - "/cluster-kerberos/VPC/PublicSubnet1/RouteTable": [ + "/cluster-data-api/VPC/PublicSubnet1/RouteTable": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1RouteTableFEE4B781" } ], - "/cluster-kerberos/VPC/PublicSubnet1/RouteTableAssociation": [ + "/cluster-data-api/VPC/PublicSubnet1/RouteTableAssociation": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" } ], - "/cluster-kerberos/VPC/PublicSubnet1/DefaultRoute": [ + "/cluster-data-api/VPC/PublicSubnet1/DefaultRoute": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1DefaultRoute91CEF279" } ], - "/cluster-kerberos/VPC/PublicSubnet1/EIP": [ + "/cluster-data-api/VPC/PublicSubnet1/EIP": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1EIP6AD938E8" } ], - "/cluster-kerberos/VPC/PublicSubnet1/NATGateway": [ + "/cluster-data-api/VPC/PublicSubnet1/NATGateway": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet1NATGatewayE0556630" } ], - "/cluster-kerberos/VPC/PublicSubnet2/Subnet": [ + "/cluster-data-api/VPC/PublicSubnet2/Subnet": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2Subnet74179F39" } ], - "/cluster-kerberos/VPC/PublicSubnet2/RouteTable": [ + "/cluster-data-api/VPC/PublicSubnet2/RouteTable": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2RouteTable6F1A15F1" } ], - "/cluster-kerberos/VPC/PublicSubnet2/RouteTableAssociation": [ + "/cluster-data-api/VPC/PublicSubnet2/RouteTableAssociation": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2RouteTableAssociation5A808732" } ], - "/cluster-kerberos/VPC/PublicSubnet2/DefaultRoute": [ + "/cluster-data-api/VPC/PublicSubnet2/DefaultRoute": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2DefaultRouteB7481BBA" } ], - "/cluster-kerberos/VPC/PublicSubnet2/EIP": [ + "/cluster-data-api/VPC/PublicSubnet2/EIP": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2EIP4947BC00" } ], - "/cluster-kerberos/VPC/PublicSubnet2/NATGateway": [ + "/cluster-data-api/VPC/PublicSubnet2/NATGateway": [ { "type": "aws:cdk:logicalId", "data": "VPCPublicSubnet2NATGateway3C070193" } ], - "/cluster-kerberos/VPC/PrivateSubnet1/Subnet": [ + "/cluster-data-api/VPC/PrivateSubnet1/Subnet": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet1Subnet8BCA10E0" } ], - "/cluster-kerberos/VPC/PrivateSubnet1/RouteTable": [ + "/cluster-data-api/VPC/PrivateSubnet1/RouteTable": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet1RouteTableBE8A6027" } ], - "/cluster-kerberos/VPC/PrivateSubnet1/RouteTableAssociation": [ + "/cluster-data-api/VPC/PrivateSubnet1/RouteTableAssociation": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" } ], - "/cluster-kerberos/VPC/PrivateSubnet1/DefaultRoute": [ + "/cluster-data-api/VPC/PrivateSubnet1/DefaultRoute": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" } ], - "/cluster-kerberos/VPC/PrivateSubnet2/Subnet": [ + "/cluster-data-api/VPC/PrivateSubnet2/Subnet": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet2SubnetCFCDAA7A" } ], - "/cluster-kerberos/VPC/PrivateSubnet2/RouteTable": [ + "/cluster-data-api/VPC/PrivateSubnet2/RouteTable": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet2RouteTable0A19E10E" } ], - "/cluster-kerberos/VPC/PrivateSubnet2/RouteTableAssociation": [ + "/cluster-data-api/VPC/PrivateSubnet2/RouteTableAssociation": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" } ], - "/cluster-kerberos/VPC/PrivateSubnet2/DefaultRoute": [ + "/cluster-data-api/VPC/PrivateSubnet2/DefaultRoute": [ { "type": "aws:cdk:logicalId", "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" } ], - "/cluster-kerberos/VPC/IGW": [ + "/cluster-data-api/VPC/IGW": [ { "type": "aws:cdk:logicalId", "data": "VPCIGWB7E252D3" } ], - "/cluster-kerberos/VPC/VPCGW": [ + "/cluster-data-api/VPC/VPCGW": [ { "type": "aws:cdk:logicalId", "data": "VPCVPCGW99B986DC" } ], - "/cluster-kerberos/VPC/RestrictDefaultSecurityGroupCustomResource/Default": [ + "/cluster-data-api/VPC/RestrictDefaultSecurityGroupCustomResource/Default": [ { "type": "aws:cdk:logicalId", "data": "VPCRestrictDefaultSecurityGroupCustomResource59474679" } ], - "/cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role": [ + "/cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role": [ { "type": "aws:cdk:logicalId", "data": "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" } ], - "/cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler": [ + "/cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler": [ { "type": "aws:cdk:logicalId", "data": "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E" } ], - "/cluster-kerberos/Function/ServiceRole/Resource": [ + "/cluster-data-api/Function/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", "data": "FunctionServiceRole675BB04A" } ], - "/cluster-kerberos/Function/ServiceRole/DefaultPolicy/Resource": [ + "/cluster-data-api/Function/ServiceRole/DefaultPolicy/Resource": [ { "type": "aws:cdk:logicalId", "data": "FunctionServiceRoleDefaultPolicy2F49994A" } ], - "/cluster-kerberos/Function/Resource": [ + "/cluster-data-api/Function/Resource": [ { "type": "aws:cdk:logicalId", "data": "Function76856677" } ], - "/cluster-kerberos/Database/Subnets/Default": [ + "/cluster-data-api/Database/Subnets/Default": [ { "type": "aws:cdk:logicalId", "data": "DatabaseSubnets56F17B9A" } ], - "/cluster-kerberos/Database/SecurityGroup/Resource": [ + "/cluster-data-api/Database/SecurityGroup/Resource": [ { "type": "aws:cdk:logicalId", "data": "DatabaseSecurityGroup5C91FDCB" } ], - "/cluster-kerberos/Database/Secret/Resource": [ + "/cluster-data-api/Database/Secret/Resource": [ { "type": "aws:cdk:logicalId", "data": "DatabaseSecret3B817195" } ], - "/cluster-kerberos/Database/Secret/Attachment/Resource": [ + "/cluster-data-api/Database/Secret/Attachment/Resource": [ { "type": "aws:cdk:logicalId", "data": "DatabaseSecretAttachmentE5D1B020" } ], - "/cluster-kerberos/Database/Resource": [ + "/cluster-data-api/Database/Resource": [ { "type": "aws:cdk:logicalId", "data": "DatabaseB269D8BB" } ], - "/cluster-kerberos/Database/writerInstance/Resource": [ + "/cluster-data-api/Database/writerInstance/Resource": [ { "type": "aws:cdk:logicalId", "data": "DatabasewriterInstanceEBFCC003" } ], - "/cluster-kerberos/BootstrapVersion": [ + "/cluster-data-api/BootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "BootstrapVersion" } ], - "/cluster-kerberos/CheckBootstrapVersion": [ + "/cluster-data-api/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", "data": "CheckBootstrapVersion" } ] }, - "displayName": "cluster-kerberos" + "displayName": "cluster-data-api" }, "integclusterdataapiDefaultTestDeployAssertACD87C08.assets": { "type": "cdk:asset-manifest", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/tree.json index c9bbb077426b1..5b1165f6b9b53 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.js.snapshot/tree.json @@ -4,17 +4,17 @@ "id": "App", "path": "", "children": { - "cluster-kerberos": { - "id": "cluster-kerberos", - "path": "cluster-kerberos", + "cluster-data-api": { + "id": "cluster-data-api", + "path": "cluster-data-api", "children": { "VPC": { "id": "VPC", - "path": "cluster-kerberos/VPC", + "path": "cluster-data-api/VPC", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/VPC/Resource", + "path": "cluster-data-api/VPC/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::VPC", "aws:cdk:cloudformation:props": { @@ -25,7 +25,7 @@ "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC" + "value": "cluster-data-api/VPC" } ] } @@ -37,11 +37,11 @@ }, "PublicSubnet1": { "id": "PublicSubnet1", - "path": "cluster-kerberos/VPC/PublicSubnet1", + "path": "cluster-data-api/VPC/PublicSubnet1", "children": { "Subnet": { "id": "Subnet", - "path": "cluster-kerberos/VPC/PublicSubnet1/Subnet", + "path": "cluster-data-api/VPC/PublicSubnet1/Subnet", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", "aws:cdk:cloudformation:props": { @@ -66,7 +66,7 @@ }, { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet1" + "value": "cluster-data-api/VPC/PublicSubnet1" } ], "vpcId": { @@ -81,7 +81,7 @@ }, "Acl": { "id": "Acl", - "path": "cluster-kerberos/VPC/PublicSubnet1/Acl", + "path": "cluster-data-api/VPC/PublicSubnet1/Acl", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -89,14 +89,14 @@ }, "RouteTable": { "id": "RouteTable", - "path": "cluster-kerberos/VPC/PublicSubnet1/RouteTable", + "path": "cluster-data-api/VPC/PublicSubnet1/RouteTable", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", "aws:cdk:cloudformation:props": { "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet1" + "value": "cluster-data-api/VPC/PublicSubnet1" } ], "vpcId": { @@ -111,7 +111,7 @@ }, "RouteTableAssociation": { "id": "RouteTableAssociation", - "path": "cluster-kerberos/VPC/PublicSubnet1/RouteTableAssociation", + "path": "cluster-data-api/VPC/PublicSubnet1/RouteTableAssociation", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", "aws:cdk:cloudformation:props": { @@ -130,7 +130,7 @@ }, "DefaultRoute": { "id": "DefaultRoute", - "path": "cluster-kerberos/VPC/PublicSubnet1/DefaultRoute", + "path": "cluster-data-api/VPC/PublicSubnet1/DefaultRoute", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Route", "aws:cdk:cloudformation:props": { @@ -150,7 +150,7 @@ }, "EIP": { "id": "EIP", - "path": "cluster-kerberos/VPC/PublicSubnet1/EIP", + "path": "cluster-data-api/VPC/PublicSubnet1/EIP", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::EIP", "aws:cdk:cloudformation:props": { @@ -158,7 +158,7 @@ "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet1" + "value": "cluster-data-api/VPC/PublicSubnet1" } ] } @@ -170,7 +170,7 @@ }, "NATGateway": { "id": "NATGateway", - "path": "cluster-kerberos/VPC/PublicSubnet1/NATGateway", + "path": "cluster-data-api/VPC/PublicSubnet1/NATGateway", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", "aws:cdk:cloudformation:props": { @@ -186,7 +186,7 @@ "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet1" + "value": "cluster-data-api/VPC/PublicSubnet1" } ] } @@ -204,11 +204,11 @@ }, "PublicSubnet2": { "id": "PublicSubnet2", - "path": "cluster-kerberos/VPC/PublicSubnet2", + "path": "cluster-data-api/VPC/PublicSubnet2", "children": { "Subnet": { "id": "Subnet", - "path": "cluster-kerberos/VPC/PublicSubnet2/Subnet", + "path": "cluster-data-api/VPC/PublicSubnet2/Subnet", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", "aws:cdk:cloudformation:props": { @@ -233,7 +233,7 @@ }, { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet2" + "value": "cluster-data-api/VPC/PublicSubnet2" } ], "vpcId": { @@ -248,7 +248,7 @@ }, "Acl": { "id": "Acl", - "path": "cluster-kerberos/VPC/PublicSubnet2/Acl", + "path": "cluster-data-api/VPC/PublicSubnet2/Acl", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -256,14 +256,14 @@ }, "RouteTable": { "id": "RouteTable", - "path": "cluster-kerberos/VPC/PublicSubnet2/RouteTable", + "path": "cluster-data-api/VPC/PublicSubnet2/RouteTable", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", "aws:cdk:cloudformation:props": { "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet2" + "value": "cluster-data-api/VPC/PublicSubnet2" } ], "vpcId": { @@ -278,7 +278,7 @@ }, "RouteTableAssociation": { "id": "RouteTableAssociation", - "path": "cluster-kerberos/VPC/PublicSubnet2/RouteTableAssociation", + "path": "cluster-data-api/VPC/PublicSubnet2/RouteTableAssociation", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", "aws:cdk:cloudformation:props": { @@ -297,7 +297,7 @@ }, "DefaultRoute": { "id": "DefaultRoute", - "path": "cluster-kerberos/VPC/PublicSubnet2/DefaultRoute", + "path": "cluster-data-api/VPC/PublicSubnet2/DefaultRoute", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Route", "aws:cdk:cloudformation:props": { @@ -317,7 +317,7 @@ }, "EIP": { "id": "EIP", - "path": "cluster-kerberos/VPC/PublicSubnet2/EIP", + "path": "cluster-data-api/VPC/PublicSubnet2/EIP", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::EIP", "aws:cdk:cloudformation:props": { @@ -325,7 +325,7 @@ "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet2" + "value": "cluster-data-api/VPC/PublicSubnet2" } ] } @@ -337,7 +337,7 @@ }, "NATGateway": { "id": "NATGateway", - "path": "cluster-kerberos/VPC/PublicSubnet2/NATGateway", + "path": "cluster-data-api/VPC/PublicSubnet2/NATGateway", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", "aws:cdk:cloudformation:props": { @@ -353,7 +353,7 @@ "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PublicSubnet2" + "value": "cluster-data-api/VPC/PublicSubnet2" } ] } @@ -371,11 +371,11 @@ }, "PrivateSubnet1": { "id": "PrivateSubnet1", - "path": "cluster-kerberos/VPC/PrivateSubnet1", + "path": "cluster-data-api/VPC/PrivateSubnet1", "children": { "Subnet": { "id": "Subnet", - "path": "cluster-kerberos/VPC/PrivateSubnet1/Subnet", + "path": "cluster-data-api/VPC/PrivateSubnet1/Subnet", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", "aws:cdk:cloudformation:props": { @@ -400,7 +400,7 @@ }, { "key": "Name", - "value": "cluster-kerberos/VPC/PrivateSubnet1" + "value": "cluster-data-api/VPC/PrivateSubnet1" } ], "vpcId": { @@ -415,7 +415,7 @@ }, "Acl": { "id": "Acl", - "path": "cluster-kerberos/VPC/PrivateSubnet1/Acl", + "path": "cluster-data-api/VPC/PrivateSubnet1/Acl", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -423,14 +423,14 @@ }, "RouteTable": { "id": "RouteTable", - "path": "cluster-kerberos/VPC/PrivateSubnet1/RouteTable", + "path": "cluster-data-api/VPC/PrivateSubnet1/RouteTable", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", "aws:cdk:cloudformation:props": { "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PrivateSubnet1" + "value": "cluster-data-api/VPC/PrivateSubnet1" } ], "vpcId": { @@ -445,7 +445,7 @@ }, "RouteTableAssociation": { "id": "RouteTableAssociation", - "path": "cluster-kerberos/VPC/PrivateSubnet1/RouteTableAssociation", + "path": "cluster-data-api/VPC/PrivateSubnet1/RouteTableAssociation", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", "aws:cdk:cloudformation:props": { @@ -464,7 +464,7 @@ }, "DefaultRoute": { "id": "DefaultRoute", - "path": "cluster-kerberos/VPC/PrivateSubnet1/DefaultRoute", + "path": "cluster-data-api/VPC/PrivateSubnet1/DefaultRoute", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Route", "aws:cdk:cloudformation:props": { @@ -490,11 +490,11 @@ }, "PrivateSubnet2": { "id": "PrivateSubnet2", - "path": "cluster-kerberos/VPC/PrivateSubnet2", + "path": "cluster-data-api/VPC/PrivateSubnet2", "children": { "Subnet": { "id": "Subnet", - "path": "cluster-kerberos/VPC/PrivateSubnet2/Subnet", + "path": "cluster-data-api/VPC/PrivateSubnet2/Subnet", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", "aws:cdk:cloudformation:props": { @@ -519,7 +519,7 @@ }, { "key": "Name", - "value": "cluster-kerberos/VPC/PrivateSubnet2" + "value": "cluster-data-api/VPC/PrivateSubnet2" } ], "vpcId": { @@ -534,7 +534,7 @@ }, "Acl": { "id": "Acl", - "path": "cluster-kerberos/VPC/PrivateSubnet2/Acl", + "path": "cluster-data-api/VPC/PrivateSubnet2/Acl", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -542,14 +542,14 @@ }, "RouteTable": { "id": "RouteTable", - "path": "cluster-kerberos/VPC/PrivateSubnet2/RouteTable", + "path": "cluster-data-api/VPC/PrivateSubnet2/RouteTable", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", "aws:cdk:cloudformation:props": { "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC/PrivateSubnet2" + "value": "cluster-data-api/VPC/PrivateSubnet2" } ], "vpcId": { @@ -564,7 +564,7 @@ }, "RouteTableAssociation": { "id": "RouteTableAssociation", - "path": "cluster-kerberos/VPC/PrivateSubnet2/RouteTableAssociation", + "path": "cluster-data-api/VPC/PrivateSubnet2/RouteTableAssociation", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", "aws:cdk:cloudformation:props": { @@ -583,7 +583,7 @@ }, "DefaultRoute": { "id": "DefaultRoute", - "path": "cluster-kerberos/VPC/PrivateSubnet2/DefaultRoute", + "path": "cluster-data-api/VPC/PrivateSubnet2/DefaultRoute", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::Route", "aws:cdk:cloudformation:props": { @@ -609,14 +609,14 @@ }, "IGW": { "id": "IGW", - "path": "cluster-kerberos/VPC/IGW", + "path": "cluster-data-api/VPC/IGW", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", "aws:cdk:cloudformation:props": { "tags": [ { "key": "Name", - "value": "cluster-kerberos/VPC" + "value": "cluster-data-api/VPC" } ] } @@ -628,7 +628,7 @@ }, "VPCGW": { "id": "VPCGW", - "path": "cluster-kerberos/VPC/VPCGW", + "path": "cluster-data-api/VPC/VPCGW", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", "aws:cdk:cloudformation:props": { @@ -647,11 +647,11 @@ }, "RestrictDefaultSecurityGroupCustomResource": { "id": "RestrictDefaultSecurityGroupCustomResource", - "path": "cluster-kerberos/VPC/RestrictDefaultSecurityGroupCustomResource", + "path": "cluster-data-api/VPC/RestrictDefaultSecurityGroupCustomResource", "children": { "Default": { "id": "Default", - "path": "cluster-kerberos/VPC/RestrictDefaultSecurityGroupCustomResource/Default", + "path": "cluster-data-api/VPC/RestrictDefaultSecurityGroupCustomResource/Default", "constructInfo": { "fqn": "aws-cdk-lib.CfnResource", "version": "0.0.0" @@ -671,11 +671,11 @@ }, "Custom::VpcRestrictDefaultSGCustomResourceProvider": { "id": "Custom::VpcRestrictDefaultSGCustomResourceProvider", - "path": "cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider", + "path": "cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider", "children": { "Staging": { "id": "Staging", - "path": "cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider/Staging", + "path": "cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider/Staging", "constructInfo": { "fqn": "aws-cdk-lib.AssetStaging", "version": "0.0.0" @@ -683,7 +683,7 @@ }, "Role": { "id": "Role", - "path": "cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role", + "path": "cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role", "constructInfo": { "fqn": "aws-cdk-lib.CfnResource", "version": "0.0.0" @@ -691,7 +691,7 @@ }, "Handler": { "id": "Handler", - "path": "cluster-kerberos/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler", + "path": "cluster-data-api/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler", "constructInfo": { "fqn": "aws-cdk-lib.CfnResource", "version": "0.0.0" @@ -705,15 +705,15 @@ }, "Function": { "id": "Function", - "path": "cluster-kerberos/Function", + "path": "cluster-data-api/Function", "children": { "ServiceRole": { "id": "ServiceRole", - "path": "cluster-kerberos/Function/ServiceRole", + "path": "cluster-data-api/Function/ServiceRole", "children": { "ImportServiceRole": { "id": "ImportServiceRole", - "path": "cluster-kerberos/Function/ServiceRole/ImportServiceRole", + "path": "cluster-data-api/Function/ServiceRole/ImportServiceRole", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -721,7 +721,7 @@ }, "Resource": { "id": "Resource", - "path": "cluster-kerberos/Function/ServiceRole/Resource", + "path": "cluster-data-api/Function/ServiceRole/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Role", "aws:cdk:cloudformation:props": { @@ -760,16 +760,26 @@ }, "DefaultPolicy": { "id": "DefaultPolicy", - "path": "cluster-kerberos/Function/ServiceRole/DefaultPolicy", + "path": "cluster-data-api/Function/ServiceRole/DefaultPolicy", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/Function/ServiceRole/DefaultPolicy/Resource", + "path": "cluster-data-api/Function/ServiceRole/DefaultPolicy/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::IAM::Policy", "aws:cdk:cloudformation:props": { "policyDocument": { "Statement": [ + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "DatabaseSecretAttachmentE5D1B020" + } + }, { "Action": [ "rds-data:BatchExecuteStatement", @@ -802,16 +812,6 @@ ] ] } - }, - { - "Action": [ - "secretsmanager:DescribeSecret", - "secretsmanager:GetSecretValue" - ], - "Effect": "Allow", - "Resource": { - "Ref": "DatabaseSecretAttachmentE5D1B020" - } } ], "Version": "2012-10-17" @@ -843,7 +843,7 @@ }, "Resource": { "id": "Resource", - "path": "cluster-kerberos/Function/Resource", + "path": "cluster-data-api/Function/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { @@ -873,15 +873,15 @@ }, "Database": { "id": "Database", - "path": "cluster-kerberos/Database", + "path": "cluster-data-api/Database", "children": { "Subnets": { "id": "Subnets", - "path": "cluster-kerberos/Database/Subnets", + "path": "cluster-data-api/Database/Subnets", "children": { "Default": { "id": "Default", - "path": "cluster-kerberos/Database/Subnets/Default", + "path": "cluster-data-api/Database/Subnets/Default", "attributes": { "aws:cdk:cloudformation:type": "AWS::RDS::DBSubnetGroup", "aws:cdk:cloudformation:props": { @@ -909,11 +909,11 @@ }, "SecurityGroup": { "id": "SecurityGroup", - "path": "cluster-kerberos/Database/SecurityGroup", + "path": "cluster-data-api/Database/SecurityGroup", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/Database/SecurityGroup/Resource", + "path": "cluster-data-api/Database/SecurityGroup/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", "aws:cdk:cloudformation:props": { @@ -943,7 +943,7 @@ }, "AuroraPostgreSqlDatabaseClusterEngineDefaultParameterGroup": { "id": "AuroraPostgreSqlDatabaseClusterEngineDefaultParameterGroup", - "path": "cluster-kerberos/Database/AuroraPostgreSqlDatabaseClusterEngineDefaultParameterGroup", + "path": "cluster-data-api/Database/AuroraPostgreSqlDatabaseClusterEngineDefaultParameterGroup", "constructInfo": { "fqn": "aws-cdk-lib.Resource", "version": "0.0.0" @@ -951,11 +951,11 @@ }, "Secret": { "id": "Secret", - "path": "cluster-kerberos/Database/Secret", + "path": "cluster-data-api/Database/Secret", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/Database/Secret/Resource", + "path": "cluster-data-api/Database/Secret/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", "aws:cdk:cloudformation:props": { @@ -985,11 +985,11 @@ }, "Attachment": { "id": "Attachment", - "path": "cluster-kerberos/Database/Secret/Attachment", + "path": "cluster-data-api/Database/Secret/Attachment", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/Database/Secret/Attachment/Resource", + "path": "cluster-data-api/Database/Secret/Attachment/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", "aws:cdk:cloudformation:props": { @@ -1021,7 +1021,7 @@ }, "Resource": { "id": "Resource", - "path": "cluster-kerberos/Database/Resource", + "path": "cluster-data-api/Database/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::RDS::DBCluster", "aws:cdk:cloudformation:props": { @@ -1079,11 +1079,11 @@ }, "writerInstance": { "id": "writerInstance", - "path": "cluster-kerberos/Database/writerInstance", + "path": "cluster-data-api/Database/writerInstance", "children": { "Resource": { "id": "Resource", - "path": "cluster-kerberos/Database/writerInstance/Resource", + "path": "cluster-data-api/Database/writerInstance/Resource", "attributes": { "aws:cdk:cloudformation:type": "AWS::RDS::DBInstance", "aws:cdk:cloudformation:props": { @@ -1114,7 +1114,7 @@ }, "BootstrapVersion": { "id": "BootstrapVersion", - "path": "cluster-kerberos/BootstrapVersion", + "path": "cluster-data-api/BootstrapVersion", "constructInfo": { "fqn": "aws-cdk-lib.CfnParameter", "version": "0.0.0" @@ -1122,7 +1122,7 @@ }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", - "path": "cluster-kerberos/CheckBootstrapVersion", + "path": "cluster-data-api/CheckBootstrapVersion", "constructInfo": { "fqn": "aws-cdk-lib.CfnRule", "version": "0.0.0" diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.ts index 16394b4b5cd5e..f9c4f1cf28127 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-rds/test/integ.cluster-data-api.ts @@ -6,7 +6,7 @@ import * as lambda from 'aws-cdk-lib/aws-lambda'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'cluster-kerberos'); +const stack = new cdk.Stack(app, 'cluster-data-api'); const vpc = new ec2.Vpc(stack, 'VPC'); const fucntion = new lambda.Function(stack, 'Function', { @@ -23,7 +23,6 @@ const cluster = new rds.DatabaseCluster(stack, 'Database', { }); cluster.grantDataApiAccess(fucntion); -cluster.secret?.grantRead(fucntion); new integ.IntegTest(app, 'integ-cluster-data-api', { testCases: [stack], diff --git a/packages/aws-cdk-lib/aws-rds/README.md b/packages/aws-cdk-lib/aws-rds/README.md index 7ee7a8df813dc..f1c677dc434da 100644 --- a/packages/aws-cdk-lib/aws-rds/README.md +++ b/packages/aws-cdk-lib/aws-rds/README.md @@ -1191,8 +1191,6 @@ const cluster = new rds.DatabaseCluster(this, 'Cluster', { enableDataApi: true, // Optional - will be automatically set if you call grantDataApiAccess() }); cluster.grantDataApiAccess(fn); -// It is necessary to grant the function access to the secret associated with the cluster for `DatabaseCluster`. -cluster.secret!.grantRead(fn); ``` **Note**: To invoke the Data API, the resource will need to read the secret associated with the cluster. diff --git a/packages/aws-cdk-lib/aws-rds/lib/cluster-ref.ts b/packages/aws-cdk-lib/aws-rds/lib/cluster-ref.ts index a55a0c4f52396..1785548c82af4 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/cluster-ref.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/cluster-ref.ts @@ -137,4 +137,11 @@ export interface DatabaseClusterAttributes { * @default - the imported Cluster's engine is unknown */ readonly engine?: IClusterEngine; + + /** + * The secret attached to the database cluster + * + * @default - the imported Cluster's secret is unknown + */ + readonly secret?: secretsmanager.ISecret; } diff --git a/packages/aws-cdk-lib/aws-rds/lib/cluster.ts b/packages/aws-cdk-lib/aws-rds/lib/cluster.ts index 41bcc57d35a6c..5119f57a3dec5 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/cluster.ts @@ -467,6 +467,11 @@ export abstract class DatabaseClusterBase extends Resource implements IDatabaseC protected abstract enableDataApi?: boolean; + /** + * Secret in SecretsManager to store the database cluster user credentials. + */ + public abstract readonly secret?: secretsmanager.ISecret; + /** * The ARN of the cluster */ @@ -521,6 +526,7 @@ export abstract class DatabaseClusterBase extends Resource implements IDatabaseC } this.enableDataApi = true; + this.secret?.grantRead(grantee); return iam.Grant.addToPrincipal({ actions: DATA_API_ACTIONS, grantee, @@ -546,11 +552,6 @@ abstract class DatabaseClusterNew extends DatabaseClusterBase { private readonly domainId?: string; private readonly domainRole?: iam.IRole; - /** - * Secret in SecretsManager to store the database cluster user credentials. - */ - public abstract readonly secret?: secretsmanager.ISecret; - /** * The VPC network to place the cluster in. */ @@ -964,6 +965,7 @@ class ImportedDatabaseCluster extends DatabaseClusterBase implements IDatabaseCl public readonly clusterIdentifier: string; public readonly connections: ec2.Connections; public readonly engine?: IClusterEngine; + public readonly secret?: secretsmanager.ISecret; private readonly _clusterResourceIdentifier?: string; private readonly _clusterEndpoint?: Endpoint; @@ -985,6 +987,7 @@ class ImportedDatabaseCluster extends DatabaseClusterBase implements IDatabaseCl defaultPort, }); this.engine = attrs.engine; + this.secret = attrs.secret; this._clusterEndpoint = (attrs.clusterEndpointAddress && attrs.port) ? new Endpoint(attrs.clusterEndpointAddress, attrs.port) : undefined; this._clusterReadEndpoint = (attrs.readerEndpointAddress && attrs.port) ? new Endpoint(attrs.readerEndpointAddress, attrs.port) : undefined; diff --git a/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts b/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts index 7563065fd0f10..b3a67928987b5 100644 --- a/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/cluster.test.ts @@ -4249,6 +4249,16 @@ describe('cluster', () => { Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { PolicyDocument: { Statement: [ + { + Action: [ + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + ], + Effect: 'Allow', + Resource: { + Ref: 'DatabaseSecretAttachmentE5D1B020', + }, + }, { Action: [ 'rds-data:BatchExecuteStatement', From cc377785c00a021c9b519bdda945be8e99cb1148 Mon Sep 17 00:00:00 2001 From: GZ Date: Thu, 14 Mar 2024 16:13:49 -0700 Subject: [PATCH 3/8] fix(cloudwatch): cloudwatch ec2 alarm action with multiple dimension results in error (#29364) ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/29331 ### Reason for this change While trying to create a Custom Metric with multiple dimension, and adding EC2 action, the CDK synth fails. ### Description of changes As long as there's instance id in dimension, we should accept it instead of raising exception. ### Description of how you validated changes new tests and existing tests pass. ### 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* --- .../aws-cdk-lib/aws-cloudwatch/lib/alarm.ts | 2 +- .../aws-cloudwatch/test/alarm.test.ts | 63 ++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-cloudwatch/lib/alarm.ts b/packages/aws-cdk-lib/aws-cloudwatch/lib/alarm.ts index 2c470512a2e81..e25bf8cfe1aef 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/lib/alarm.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/lib/alarm.ts @@ -267,7 +267,7 @@ export class Alarm extends AlarmBase { if (ec2ActionsRegexp.test(actionArn)) { // Check per-instance metric const metricConfig = this.metric.toMetricConfig(); - if (metricConfig.metricStat?.dimensions?.length != 1 || metricConfig.metricStat?.dimensions![0].name != 'InstanceId') { + if (metricConfig.metricStat?.dimensions?.length != 1 || !metricConfig.metricStat?.dimensions?.some(dimension => dimension.name === 'InstanceId')) { throw new Error(`EC2 alarm actions requires an EC2 Per-Instance Metric. (${JSON.stringify(metricConfig)} does not have an 'InstanceId' dimension)`); } } diff --git a/packages/aws-cdk-lib/aws-cloudwatch/test/alarm.test.ts b/packages/aws-cdk-lib/aws-cloudwatch/test/alarm.test.ts index ffc79405b1fe8..85feb315eda91 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/test/alarm.test.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/test/alarm.test.ts @@ -1,6 +1,8 @@ import { Construct } from 'constructs'; import { Match, Template, Annotations } from '../../assertions'; -import { Duration, Stack } from '../../core'; +import { Ec2Action, Ec2InstanceAction } from '../../aws-cloudwatch-actions/lib'; +import { Duration, Stack, App } from '../../core'; +import { ENABLE_PARTITION_LITERALS } from '../../cx-api'; import { Alarm, IAlarm, IAlarmAction, Metric, MathExpression, IMetric, Stats } from '../lib'; const testMetric = new Metric({ @@ -232,6 +234,65 @@ describe('Alarm', () => { }); }); + test('EC2 alarm actions with InstanceId dimension', () => { + // GIVEN + const app = new App({ context: { [ ENABLE_PARTITION_LITERALS]: true } }); + const stack = new Stack(app, 'EC2AlarmStack', { env: { region: 'us-west-2', account: '123456789012' } }); + + // WHEN + const metric = new Metric({ + namespace: 'CWAgent', + metricName: 'disk_used_percent', + dimensionsMap: { + InstanceId: 'instance-id', + }, + period: Duration.minutes(5), + statistic: 'Average', + }); + + const sev3Alarm = new Alarm(stack, 'DISK_USED_PERCENT_SEV3', { + alarmName: 'DISK_USED_PERCENT_SEV3', + actionsEnabled: true, + metric: metric, + threshold: 1, + evaluationPeriods: 1, + }); + + expect(() => { + sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT)); + }).not.toThrow(); + }); + + test('EC2 alarm actions without InstanceId dimension', () => { + // GIVEN + const app = new App({ context: { [ ENABLE_PARTITION_LITERALS]: true } }); + const stack = new Stack(app, 'EC2AlarmStack', { env: { region: 'us-west-2', account: '123456789012' } }); + + // WHEN + const metric = new Metric({ + namespace: 'CWAgent', + metricName: 'disk_used_percent', + dimensionsMap: { + ImageId: 'image-id', + InstanceType: 't2.micro', + }, + period: Duration.minutes(5), + statistic: 'Average', + }); + + const sev3Alarm = new Alarm(stack, 'DISK_USED_PERCENT_SEV3', { + alarmName: 'DISK_USED_PERCENT_SEV3', + actionsEnabled: true, + metric: metric, + threshold: 1, + evaluationPeriods: 1, + }); + + expect(() => { + sev3Alarm.addAlarmAction(new Ec2Action(Ec2InstanceAction.REBOOT)); + }).toThrow(/EC2 alarm actions requires an EC2 Per-Instance Metric/); + }); + test('can use percentile string to make alarm', () => { // GIVEN const stack = new Stack(); From 3c1307ed6a4b7f09db929093914eeaad93dae097 Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Thu, 14 Mar 2024 17:42:08 -0600 Subject: [PATCH 4/8] chore(synthetics): deprecate nodejs runtimes (#29472) Per this [doc](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html#CloudWatch_Synthetics_Canaries_runtime_support), these 5 runtimes were deprecated on March 8, 2024. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts b/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts index b2b5207e6235b..f56b3f2114927 100644 --- a/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts @@ -98,6 +98,7 @@ export class Runtime { * - **Dependency upgrades**: The Node.js dependency is updated to 16.x. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-4.0 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_4_0 = new Runtime('syn-nodejs-puppeteer-4.0', RuntimeFamily.NODEJS); @@ -111,6 +112,7 @@ export class Runtime { * - **Dependency upgrade**: The Puppeteer-core version is updated to 19.7.0. The Chromium version is upgraded to 111.0.5563.146. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-5.0 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_5_0 = new Runtime('syn-nodejs-puppeteer-5.0', RuntimeFamily.NODEJS); @@ -124,6 +126,7 @@ export class Runtime { * - **Bug fix**: This runtime fixes a bug in `syn-nodejs-puppeteer-5.0` where the HAR files created by the canaries were missing request headers. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-5.1 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_5_1 = new Runtime('syn-nodejs-puppeteer-5.1', RuntimeFamily.NODEJS); @@ -153,6 +156,7 @@ export class Runtime { * - **Bug fix**: Clean up core dump generated when Chromium crashes during a canary run. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-6.0 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_6_0 = new Runtime('syn-nodejs-puppeteer-6.0', RuntimeFamily.NODEJS); @@ -168,6 +172,7 @@ export class Runtime { * - **Canaries without Amazon S3 permissions**: Bug fixes, such that canaries that don't have any Amazon S3 permissions can still run. These canaries with no Amazon S3 permissions won't be able to upload screenshots or other artifacts to Amazon S3. For more information about permissions for canaries, see Required roles and permissions for canaries. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-6.1 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_6_1 = new Runtime('syn-nodejs-puppeteer-6.1', RuntimeFamily.NODEJS); From 9ec41ad39711c16a5a19fcc142a265b1236844f3 Mon Sep 17 00:00:00 2001 From: GZ Date: Thu, 14 Mar 2024 17:09:22 -0700 Subject: [PATCH 5/8] chore(integ-test-alpha): add log group retention days to integ test stack (#29277) ### Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/29260 ### Reason for this change CloudWatch log groups created by the integ tests are set to never expire, so end up cluttering up your aws account and need manual cleanup. ### Description of changes Added custom log group with log group retention ### Description of how you validated changes All existing tests and new tests pass. ### Checklist - [ ] 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* --- packages/@aws-cdk/integ-tests-alpha/README.md | 22 +++++++++ .../lib/assertions/providers/provider.ts | 46 +++++++++++++++---- .../integ-tests-alpha/lib/assertions/sdk.ts | 13 +++++- .../assertions/providers/provider.test.ts | 20 ++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/integ-tests-alpha/README.md b/packages/@aws-cdk/integ-tests-alpha/README.md index 9ca5a2416d016..ff04b1cb477c1 100644 --- a/packages/@aws-cdk/integ-tests-alpha/README.md +++ b/packages/@aws-cdk/integ-tests-alpha/README.md @@ -439,6 +439,28 @@ invoke.expect(ExpectedResult.objectLike({ })); ``` +The above example will by default create a CloudWatch log group that's never +expired. If you want to configure it with custom log retention days, you need +to specify the `logRetention` property. + +```ts +import * as logs from 'aws-cdk-lib/aws-logs'; + +declare const lambdaFunction: lambda.IFunction; +declare const app: App; + +const stack = new Stack(app, 'cdk-integ-lambda-bundling'); + +const integ = new IntegTest(app, 'IntegTest', { + testCases: [stack], +}); + +const invoke = integ.assertions.invokeFunction({ + functionName: lambdaFunction.functionName, + logRetention: logs.RetentionDays.ONE_WEEK, +}); +``` + #### Make an AWS API Call In this example there is a StepFunctions state machine that is executed diff --git a/packages/@aws-cdk/integ-tests-alpha/lib/assertions/providers/provider.ts b/packages/@aws-cdk/integ-tests-alpha/lib/assertions/providers/provider.ts index 1fe737965c841..36d268ad37379 100644 --- a/packages/@aws-cdk/integ-tests-alpha/lib/assertions/providers/provider.ts +++ b/packages/@aws-cdk/integ-tests-alpha/lib/assertions/providers/provider.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import { Duration, CfnResource, AssetStaging, Stack, FileAssetPackaging, Token, Lazy, Reference } from 'aws-cdk-lib/core'; import { Construct } from 'constructs'; import { awsSdkToIamAction } from 'aws-cdk-lib/custom-resources/lib/helpers-internal'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; /** * Properties for a lambda function provider @@ -13,6 +14,13 @@ export interface LambdaFunctionProviderProps { * @default index.handler */ readonly handler?: string; + + /** + * How long, in days, the log contents will be retained. + * + * @default - no retention days specified + */ + readonly logRetention?: RetentionDays; } /** @@ -75,18 +83,34 @@ class LambdaFunctionProvider extends Construct { }, }); + const functionProperties: any = { + Runtime: 'nodejs18.x', + Code: { + S3Bucket: asset.bucketName, + S3Key: asset.objectKey, + }, + Timeout: Duration.minutes(2).toSeconds(), + Handler: props?.handler ?? 'index.handler', + Role: role.getAtt('Arn'), + }; + + if (props?.logRetention) { + const logGroup = new CfnResource(this, 'LogGroup', { + type: 'AWS::Logs::LogGroup', + properties: { + LogGroupName: `/aws/lambda/${id}`, + RetentionInDays: props.logRetention, + }, + }); + + functionProperties.LoggingConfig = { + LogGroup: logGroup.ref, + }; + } + const handler = new CfnResource(this, 'Handler', { type: 'AWS::Lambda::Function', - properties: { - Runtime: 'nodejs18.x', - Code: { - S3Bucket: asset.bucketName, - S3Key: asset.objectKey, - }, - Timeout: Duration.minutes(2).toSeconds(), - Handler: props?.handler ?? 'index.handler', - Role: role.getAtt('Arn'), - }, + properties: functionProperties, }); this.serviceToken = Token.asString(handler.getAtt('Arn')); @@ -131,6 +155,7 @@ class SingletonFunction extends Construct { return new LambdaFunctionProvider(Stack.of(this), constructName, { handler: props.handler, + logRetention: props.logRetention, }); } @@ -204,6 +229,7 @@ export class AssertionsProvider extends Construct { this.handler = new SingletonFunction(this, 'AssertionsProvider', { handler: props?.handler, uuid: props?.uuid ?? '1488541a-7b23-4664-81b6-9b4408076b81', + logRetention: props?.logRetention, }); this.handlerRoleArn = this.handler.lambdaFunction.roleArn; diff --git a/packages/@aws-cdk/integ-tests-alpha/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests-alpha/lib/assertions/sdk.ts index d437f54c5ddbf..6a535f8fb2fac 100644 --- a/packages/@aws-cdk/integ-tests-alpha/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests-alpha/lib/assertions/sdk.ts @@ -4,6 +4,7 @@ import { ApiCallBase, IApiCall } from './api-call-base'; import { ExpectedResult } from './common'; import { AssertionsProvider, SDK_RESOURCE_TYPE_PREFIX } from './providers'; import { WaiterStateMachine, WaiterStateMachineOptions } from './waiter-state-machine'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; /** * Options to perform an AWS JavaScript V2 API call @@ -75,7 +76,9 @@ export class AwsApiCall extends ApiCallBase { constructor(scope: Construct, id: string, props: AwsApiCallProps) { super(scope, id); - this.provider = new AssertionsProvider(this, 'SdkProvider'); + this.provider = new AssertionsProvider(this, 'SdkProvider', { + logRetention: props.parameters?.RetentionDays, + }); this.provider.addPolicyStatementFromSdkCall(props.service, props.api); this.name = `${props.service}${props.api}`; this.api = props.api; @@ -210,6 +213,13 @@ export interface LambdaInvokeFunctionProps { */ readonly logType?: LogType; + /** + * How long, in days, the log contents will be retained. + * + * @default - no retention days specified + */ + readonly logRetention?: RetentionDays; + /** * Payload to send as part of the invoke * @@ -234,6 +244,7 @@ export class LambdaInvokeFunction extends AwsApiCall { InvocationType: props.invocationType, LogType: props.logType, Payload: props.payload, + RetentionDays: props.logRetention, }, }); diff --git a/packages/@aws-cdk/integ-tests-alpha/test/assertions/providers/provider.test.ts b/packages/@aws-cdk/integ-tests-alpha/test/assertions/providers/provider.test.ts index 823c297c8923b..03643d2c9779d 100644 --- a/packages/@aws-cdk/integ-tests-alpha/test/assertions/providers/provider.test.ts +++ b/packages/@aws-cdk/integ-tests-alpha/test/assertions/providers/provider.test.ts @@ -1,6 +1,7 @@ import { Template } from 'aws-cdk-lib/assertions'; import { Stack } from 'aws-cdk-lib'; import { AssertionsProvider } from '../../../lib/assertions'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; let stack: Stack; beforeEach(() => { @@ -20,6 +21,25 @@ describe('AssertionProvider', () => { }); }); + test('default', () => { + // WHEN + const provider = new AssertionsProvider(stack, 'AssertionProvider', { + logRetention: RetentionDays.ONE_WEEK, + }); + + // THEN + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Logs::LogGroup', 1); + expect(stack.resolve(provider.serviceToken)).toEqual({ 'Fn::GetAtt': ['SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F', 'Arn'] }); + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + Handler: 'index.handler', + Timeout: 120, + }); + template.hasResourceProperties('AWS::Logs::LogGroup', { + RetentionInDays: 7, + }); + }); + describe('addPolicyStatementForSdkCall', () => { test('default', () => { // WHEN From bbc624cdaed8d4931a7d355c37d4986db0b0d08b Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Thu, 14 Mar 2024 18:37:43 -0600 Subject: [PATCH 6/8] chore(iam): clarify policy docs (#29416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #29398. @abdulkadirdere – Let me know if this clarifies things. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/aws-cdk-lib/aws-iam/lib/policy.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk-lib/aws-iam/lib/policy.ts b/packages/aws-cdk-lib/aws-iam/lib/policy.ts index 43c3addd72b0f..a8dd67f5fecbf 100644 --- a/packages/aws-cdk-lib/aws-iam/lib/policy.ts +++ b/packages/aws-cdk-lib/aws-iam/lib/policy.ts @@ -79,7 +79,8 @@ export interface PolicyProps { * creating invalid--and hence undeployable--CloudFormation templates. * * In cases where you know the policy must be created and it is actually - * an error if no statements have been added to it, you can set this to `true`. + * an error if no statements have been added to it or it remains unattached to + * an IAM identity, you can set this to `true`. * * @default false */ @@ -96,9 +97,9 @@ export interface PolicyProps { } /** - * The AWS::IAM::Policy resource associates an IAM policy with IAM users, roles, - * or groups. For more information about IAM policies, see [Overview of IAM - * Policies](http://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html) + * The AWS::IAM::Policy resource associates an [inline](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#inline) + * IAM policy with IAM users, roles, or groups. For more information about IAM policies, see + * [Overview of IAM Policies](http://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html) * in the IAM User Guide guide. */ export class Policy extends Resource implements IPolicy, IGrantable { From 87139ab2eeae96df9d775118b870cfb326438197 Mon Sep 17 00:00:00 2001 From: Jimmy Gaussen Date: Fri, 15 Mar 2024 15:35:31 +0100 Subject: [PATCH 7/8] chore(synthetics): update runtimes (#29446) ### Issue # (if applicable) Could not find any in the backlog ### Reason for this change Update the CDK listed Synthetics runtimes to match the current availability, as well as add missing deprecated versions ### Description of changes * Added new runtimes * Marked existing runtimes as deprecated * Add previously deprecated runtimes * For the sake of consistency and readability, the static fields for `syn-1.0` and `syn-nodejs-2.*` were still named `SYNTHETICS_NODEJS_PUPPETEER_*` * Fix documented versions for existing runtimes * Added `{@link}` tags to match the web documentation ### Description of how you validated changes I compared the current CDK versions to live SDK data, using the `synthetics:DescribeRuntimeVersions` API results. Deprecated versions were established using the `DeprecationDate` field ### 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* --- .../aws-cdk-lib/aws-synthetics/lib/runtime.ts | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts b/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts index f56b3f2114927..9c4acfa496bfa 100644 --- a/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts +++ b/packages/aws-cdk-lib/aws-synthetics/lib/runtime.ts @@ -25,9 +25,12 @@ export class Runtime { /** * `syn-nodejs-puppeteer-3.5` includes the following: * - Lambda runtime Node.js 14.x - * - Puppeteer-core version 10.1.0 + * - Puppeteer-core version 5.5.0 * - Chromium version 92.0.4512 * + * New features: + * - **Updated dependencies**: The only new features in this runtime are the updated dependencies. + * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-3.5 * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. */ @@ -36,7 +39,7 @@ export class Runtime { /** * `syn-nodejs-puppeteer-3.6` includes the following: * - Lambda runtime Node.js 14.x - * - Puppeteer-core version 10.1.0 + * - Puppeteer-core version 5.5.0 * - Chromium version 92.0.4512 * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-3.6 @@ -47,7 +50,7 @@ export class Runtime { /** * `syn-nodejs-puppeteer-3.7` includes the following: * - Lambda runtime Node.js 14.x - * - Puppeteer-core version 10.1.0 + * - Puppeteer-core version 5.5.0 * - Chromium version 92.0.4512 * * New Features: @@ -169,7 +172,7 @@ export class Runtime { * New Features: * - **Stability improvements**: Added auto-retry logic for handling intermittent Puppeteer launch errors. * - **Dependency upgrades**: Upgrades for some third-party dependency packages. - * - **Canaries without Amazon S3 permissions**: Bug fixes, such that canaries that don't have any Amazon S3 permissions can still run. These canaries with no Amazon S3 permissions won't be able to upload screenshots or other artifacts to Amazon S3. For more information about permissions for canaries, see Required roles and permissions for canaries. + * - **Canaries without Amazon S3 permissions**: Bug fixes, such that canaries that don't have any Amazon S3 permissions can still run. These canaries with no Amazon S3 permissions won't be able to upload screenshots or other artifacts to Amazon S3. For more information about permissions for canaries, see {@link https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_CanaryPermissions.html | Required roles and permissions for canaries}. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-6.1 * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest NodeJS Puppeteer runtime. @@ -191,6 +194,19 @@ export class Runtime { */ public static readonly SYNTHETICS_NODEJS_PUPPETEER_6_2 = new Runtime('syn-nodejs-puppeteer-6.2', RuntimeFamily.NODEJS); + /** + * `syn-nodejs-puppeteer-7.0` includes the following: + * - Lambda runtime Node.js 18.x + * - Puppeteer-core version 21.9.0 + * - Chromium version 121.0.6167.139 + * + * New Features: + * - **Updated versions of the bundled libraries in Puppeteer and Chromium**: The Puppeteer and Chromium dependencies are updated to new versions. + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_nodejs_puppeteer.html#CloudWatch_Synthetics_runtimeversion-nodejs-puppeteer-7.0 + */ + public static readonly SYNTHETICS_NODEJS_PUPPETEER_7_0 = new Runtime('syn-nodejs-puppeteer-7.0', RuntimeFamily.NODEJS); + /** * `syn-python-selenium-1.0` includes the following: * - Lambda runtime Python 3.8 @@ -198,6 +214,7 @@ export class Runtime { * - Chromium version 83.0.4103.0 * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-1.0 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python Selenium runtime. */ public static readonly SYNTHETICS_PYTHON_SELENIUM_1_0 = new Runtime('syn-python-selenium-1.0', RuntimeFamily.PYTHON); @@ -214,6 +231,7 @@ export class Runtime { * - **Cross-Region artifact buckets**: A canary can store its artifacts in an Amazon S3 bucket in a different Region. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-1.1 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python Selenium runtime. */ public static readonly SYNTHETICS_PYTHON_SELENIUM_1_1 = new Runtime('syn-python-selenium-1.1', RuntimeFamily.PYTHON); @@ -227,6 +245,7 @@ export class Runtime { * - **Updated dependencies**: The only new features in this runtime are the updated dependencies. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-1.2 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python Selenium runtime. */ public static readonly SYNTHETICS_PYTHON_SELENIUM_1_2 = new Runtime('syn-python-selenium-1.2', RuntimeFamily.PYTHON); @@ -240,6 +259,7 @@ export class Runtime { * - **More precise timestamps**: The start time and stop time of canary runs are now precise to the millisecond. * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-1.3 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python Selenium runtime. */ public static readonly SYNTHETICS_PYTHON_SELENIUM_1_3 = new Runtime('syn-python-selenium-1.3', RuntimeFamily.PYTHON); @@ -256,7 +276,9 @@ export class Runtime { * Bug fixes: * - **Timestamp added**: A timestamp has been added to canary logs. * - **Session re-use**: A bug was fixed so that canaries are now prevented from reusing the session from their previous canary run. + * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-2.0 + * @deprecated Legacy runtime no longer supported by AWS Lambda. Migrate to the latest Python Selenium runtime. */ public static readonly SYNTHETICS_PYTHON_SELENIUM_2_0 = new Runtime('syn-python-selenium-2.0', RuntimeFamily.PYTHON); @@ -273,6 +295,19 @@ export class Runtime { */ public static readonly SYNTHETICS_PYTHON_SELENIUM_2_1 = new Runtime('syn-python-selenium-2.1', RuntimeFamily.PYTHON); + /** + * `syn-python-selenium-3.0` includes the following: + * - Lambda runtime Python 3.8 + * - Selenium version 4.15.1 + * - Chromium version 121.0.6167.139 + * + * New Features: + * - **Updated versions of the bundled libraries in Chromium**: The Chromium dependency is updated to a new version. + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Library_python_selenium.html#CloudWatch_Synthetics_runtimeversion-syn-python-selenium-3.0 + */ + public static readonly SYNTHETICS_PYTHON_SELENIUM_3_0 = new Runtime('syn-python-selenium-3.0', RuntimeFamily.PYTHON); + /** * @param name The name of the runtime version * @param family The Lambda runtime family From f2ad98027a896228c554985e746107a4c7089f70 Mon Sep 17 00:00:00 2001 From: Xia Zhao <78883180+xazhao@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:58:50 -0700 Subject: [PATCH 8/8] fix(cloudwatch): unrecognized statistic warning when using percentileRank statistic in Stats helper (#29498) ### Issue # (if applicable) Closes #29465. ### Reason for this change There shouldn't be a warning when `Stats.percentileRank` ### Description of changes Add a new parser for percentileRank statistic ### Description of how you validated changes unit test ### Checklist - [ ] 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* --- .../aws-cloudwatch/lib/private/statistic.ts | 9 +++++++++ .../aws-cdk-lib/aws-cloudwatch/test/stats.test.ts | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/aws-cdk-lib/aws-cloudwatch/lib/private/statistic.ts b/packages/aws-cdk-lib/aws-cloudwatch/lib/private/statistic.ts index 395f347e97ebf..80e05f26488d7 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/lib/private/statistic.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/lib/private/statistic.ts @@ -34,6 +34,10 @@ export interface PercentileStatistic extends SingleStatistic { statName: 'percentile'; } +export interface PercentileRankStatistic extends PairStatistic { + statName: 'percentileRank'; +} + export interface TrimmedMeanStatistic extends PairStatistic { statName: 'trimmedMean'; } @@ -154,6 +158,7 @@ export function parseStatistic( ): | SimpleStatistic | PercentileStatistic + | PercentileRankStatistic | TrimmedMeanStatistic | WinsorizedMeanStatistic | TrimmedCountStatistic @@ -188,6 +193,10 @@ export function parseStatistic( m = parseSingleStatistic(stat, 'p'); if (m) return { ...m, statName: 'percentile' } as PercentileStatistic; + // Percentile Rank statistics + m = parsePairStatistic(stat, 'pr'); + if (m) return { ...m, statName: 'percentileRank' } as PercentileRankStatistic; + // Trimmed mean statistics m = parseSingleStatistic(stat, 'tm') || parsePairStatistic(stat, 'tm'); if (m) return { ...m, statName: 'trimmedMean' } as TrimmedMeanStatistic; diff --git a/packages/aws-cdk-lib/aws-cloudwatch/test/stats.test.ts b/packages/aws-cdk-lib/aws-cloudwatch/test/stats.test.ts index 0a65ae2e143de..f2b399e243c79 100644 --- a/packages/aws-cdk-lib/aws-cloudwatch/test/stats.test.ts +++ b/packages/aws-cdk-lib/aws-cloudwatch/test/stats.test.ts @@ -1,5 +1,20 @@ +import { Metric, Stats } from '../../aws-cloudwatch'; import * as cloudwatch from '../lib'; +it.each([ + Stats.percentileRank(0), + Stats.percentileRank(0, 1), + Stats.percentileRank(0, undefined), +])('Stats can create valid statistics %s without causing warnings', (statistic) => { + const metric = new Metric({ + namespace: 'example', + metricName: 'example', + statistic, + }); + + expect(metric.warningsV2).toEqual(undefined); +}); + test('spot check some constants', () => { expect(cloudwatch.Stats.AVERAGE).toEqual('Average'); expect(cloudwatch.Stats.IQM).toEqual('IQM');