From 7dc6d275fe5d3d1d08f7202a6854d71c3cf40780 Mon Sep 17 00:00:00 2001 From: Lee Date: Wed, 29 May 2024 23:20:01 +0100 Subject: [PATCH 1/7] feat(dynamodb): add resource polices for table (#30251) Issue # (if applicable) Closes https://github.com/aws/aws-cdk/issues/29600. https://github.com/aws/aws-cdk/issues/29600 Reason for this change Adding a new feature Description of changes Add resourcePolicy for DynamoDB Table component in aws-dynamodb Description of how you validated changes integration test integ.dynamodb.policy.ts 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* --- .../ResourcePolicyTest-v2.assets.json | 20 ++ .../ResourcePolicyTest-v2.template.json | 89 +++++++ .../cdk.out | 1 + .../integ.json | 22 ++ .../manifest.json | 113 +++++++++ ...efaultTestDeployAssertBE3353C7.assets.json | 19 ++ ...aultTestDeployAssertBE3353C7.template.json | 36 +++ .../tree.json | 168 +++++++++++++ .../test/integ.dynamodb-v2.policy.ts | 41 ++++ .../integ.dynamodb.policy.js.snapshot/cdk.out | 1 + .../integ.json | 22 ++ .../manifest.json | 122 ++++++++++ .../resource-policy-stack.assets.json | 19 ++ .../resource-policy-stack.template.json | 114 +++++++++ ...efaultTestDeployAssert9778837B.assets.json | 19 ++ ...aultTestDeployAssert9778837B.template.json | 36 +++ .../tree.json | 225 ++++++++++++++++++ .../test/integ.dynamodb.policy.ts | 51 ++++ packages/aws-cdk-lib/aws-dynamodb/README.md | 30 +++ .../aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md | 26 ++ .../aws-dynamodb/lib/table-v2-base.ts | 35 ++- .../aws-cdk-lib/aws-dynamodb/lib/table-v2.ts | 21 +- .../aws-cdk-lib/aws-dynamodb/lib/table.ts | 56 ++++- .../aws-dynamodb/test/dynamodb.test.ts | 50 ++++ .../aws-dynamodb/test/table-v2.test.ts | 48 ++++ 25 files changed, 1369 insertions(+), 15 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json new file mode 100644 index 0000000000000..8193c8bf97527 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.assets.json @@ -0,0 +1,20 @@ +{ + "version": "36.0.0", + "files": { + "192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849": { + "source": { + "path": "ResourcePolicyTest-v2.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-eu-west-1": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1", + "objectKey": "192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849.json", + "region": "eu-west-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-eu-west-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json new file mode 100644 index 0000000000000..5932811ef4cf1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/ResourcePolicyTest-v2.template.json @@ -0,0 +1,89 @@ +{ + "Resources": { + "TableTestV215EEA02B7": { + "Type": "AWS::DynamoDB::GlobalTable", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "Replicas": [ + { + "Region": "eu-west-1", + "ResourcePolicy": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/integ.json new file mode 100644 index 0000000000000..32e11a1d2250a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/integ.json @@ -0,0 +1,22 @@ +{ + "version": "36.0.0", + "testCases": { + "table-v2-resource-policy-integ-test/DefaultTest": { + "stacks": [ + "ResourcePolicyTest-v2" + ], + "regions": [ + "us-east-1" + ], + "cdkCommandOptions": { + "deploy": { + "args": { + "rollback": true + } + } + }, + "assertionStack": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json new file mode 100644 index 0000000000000..3c95c2ac52ac0 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/manifest.json @@ -0,0 +1,113 @@ +{ + "version": "36.0.0", + "artifacts": { + "ResourcePolicyTest-v2.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ResourcePolicyTest-v2.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ResourcePolicyTest-v2": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/eu-west-1", + "properties": { + "templateFile": "ResourcePolicyTest-v2.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-eu-west-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-eu-west-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-eu-west-1/192dc5b63bb0eb2c99bc1ea8a8fe1237a00e5067ac672d4e4f0986700f476849.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "ResourcePolicyTest-v2.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-eu-west-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "ResourcePolicyTest-v2.assets" + ], + "metadata": { + "/ResourcePolicyTest-v2/TableTestV2-1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableTestV215EEA02B7" + } + ], + "/ResourcePolicyTest-v2/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ResourcePolicyTest-v2/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ResourcePolicyTest-v2" + }, + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets" + ], + "metadata": { + "/table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json new file mode 100644 index 0000000000000..8b15846a49e27 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tablev2resourcepolicyintegtestDefaultTestDeployAssertBE3353C7.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json new file mode 100644 index 0000000000000..3a0a1c50405d4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.js.snapshot/tree.json @@ -0,0 +1,168 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "ResourcePolicyTest-v2": { + "id": "ResourcePolicyTest-v2", + "path": "ResourcePolicyTest-v2", + "children": { + "TableTestV2-1": { + "id": "TableTestV2-1", + "path": "ResourcePolicyTest-v2/TableTestV2-1", + "children": { + "Resource": { + "id": "Resource", + "path": "ResourcePolicyTest-v2/TableTestV2-1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::GlobalTable", + "aws:cdk:cloudformation:props": { + "attributeDefinitions": [ + { + "attributeName": "id", + "attributeType": "S" + } + ], + "billingMode": "PAY_PER_REQUEST", + "keySchema": [ + { + "attributeName": "id", + "keyType": "HASH" + } + ], + "replicas": [ + { + "region": "eu-west-1", + "resourcePolicy": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:aws:iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnGlobalTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.TableV2", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ResourcePolicyTest-v2/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ResourcePolicyTest-v2/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "table-v2-resource-policy-integ-test": { + "id": "table-v2-resource-policy-integ-test", + "path": "table-v2-resource-policy-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "table-v2-resource-policy-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "table-v2-resource-policy-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "table-v2-resource-policy-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts new file mode 100644 index 0000000000000..5a30de692de21 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb-v2.policy.ts @@ -0,0 +1,41 @@ +import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new App(); + +class TestStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const docu = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:*'], + principals: [new iam.AccountRootPrincipal()], + resources: ['*'], + }), + ], + }); + + // table with resource policy + const table = new dynamodb.TableV2(this, 'TableTestV2-1', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + resourcePolicy: docu, + }); + + table.grantReadData(new iam.AccountPrincipal('123456789012')); + } +} + +const stack = new TestStack(app, 'ResourcePolicyTest-v2', { env: { region: 'eu-west-1' } }); + +new IntegTest(app, 'table-v2-resource-policy-integ-test', { + testCases: [stack], +}); \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/integ.json new file mode 100644 index 0000000000000..61d445eeb6f54 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/integ.json @@ -0,0 +1,22 @@ +{ + "version": "36.0.0", + "testCases": { + "resource-policy-integ-test/DefaultTest": { + "stacks": [ + "resource-policy-stack" + ], + "regions": [ + "us-east-1" + ], + "cdkCommandOptions": { + "deploy": { + "args": { + "rollback": true + } + } + }, + "assertionStack": "resource-policy-integ-test/DefaultTest/DeployAssert", + "assertionStackName": "resourcepolicyintegtestDefaultTestDeployAssert9778837B" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/manifest.json new file mode 100644 index 0000000000000..ffa0d288174ae --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/manifest.json @@ -0,0 +1,122 @@ +{ + "version": "36.0.0", + "artifacts": { + "resource-policy-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "resource-policy-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "resource-policy-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "resource-policy-stack.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}/e3fcb6ddf5b25ca1df397996de10e74311360d17c1f51a46151edee98629d5d1.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "resource-policy-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "resource-policy-stack.assets" + ], + "metadata": { + "/resource-policy-stack/TableTest1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableTest143E55AA2" + } + ], + "/resource-policy-stack/TableTest2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TableTest21D137FC9", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] + } + ], + "/resource-policy-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/resource-policy-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "resource-policy-stack" + }, + "resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "resourcepolicyintegtestDefaultTestDeployAssert9778837B": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "resourcepolicyintegtestDefaultTestDeployAssert9778837B.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets" + ], + "metadata": { + "/resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/resource-policy-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "resource-policy-integ-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.assets.json new file mode 100644 index 0000000000000..7c0077eab1646 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "e3fcb6ddf5b25ca1df397996de10e74311360d17c1f51a46151edee98629d5d1": { + "source": { + "path": "resource-policy-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "e3fcb6ddf5b25ca1df397996de10e74311360d17c1f51a46151edee98629d5d1.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.template.json new file mode 100644 index 0000000000000..1d36ff78393a6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resource-policy-stack.template.json @@ -0,0 +1,114 @@ +{ + "Resources": { + "TableTest143E55AA2": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "ResourcePolicy": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "TableTest21D137FC9": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "PK", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "PK", + "KeyType": "HASH" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets.json new file mode 100644 index 0000000000000..0861153b87513 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "resourcepolicyintegtestDefaultTestDeployAssert9778837B.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/resourcepolicyintegtestDefaultTestDeployAssert9778837B.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/tree.json new file mode 100644 index 0000000000000..72ee67ca075e2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.js.snapshot/tree.json @@ -0,0 +1,225 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "resource-policy-stack": { + "id": "resource-policy-stack", + "path": "resource-policy-stack", + "children": { + "TableTest1": { + "id": "TableTest1", + "path": "resource-policy-stack/TableTest1", + "children": { + "Resource": { + "id": "Resource", + "path": "resource-policy-stack/TableTest1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::Table", + "aws:cdk:cloudformation:props": { + "attributeDefinitions": [ + { + "attributeName": "id", + "attributeType": "S" + } + ], + "keySchema": [ + { + "attributeName": "id", + "keyType": "HASH" + } + ], + "provisionedThroughput": { + "readCapacityUnits": 5, + "writeCapacityUnits": 5 + }, + "resourcePolicy": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:*", + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":root" + ] + ] + } + }, + "Resource": "*" + } + ], + "Version": "2012-10-17" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnTable", + "version": "0.0.0" + } + }, + "ScalingRole": { + "id": "ScalingRole", + "path": "resource-policy-stack/TableTest1/ScalingRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "TableTest2": { + "id": "TableTest2", + "path": "resource-policy-stack/TableTest2", + "children": { + "Resource": { + "id": "Resource", + "path": "resource-policy-stack/TableTest2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::Table", + "aws:cdk:cloudformation:props": { + "attributeDefinitions": [ + { + "attributeName": "PK", + "attributeType": "S" + } + ], + "keySchema": [ + { + "attributeName": "PK", + "keyType": "HASH" + } + ], + "provisionedThroughput": { + "readCapacityUnits": 5, + "writeCapacityUnits": 5 + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnTable", + "version": "0.0.0" + } + }, + "ScalingRole": { + "id": "ScalingRole", + "path": "resource-policy-stack/TableTest2/ScalingRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "resource-policy-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "resource-policy-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "resource-policy-integ-test": { + "id": "resource-policy-integ-test", + "path": "resource-policy-integ-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "resource-policy-integ-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "resource-policy-integ-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "resource-policy-integ-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "resource-policy-integ-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "resource-policy-integ-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.ts new file mode 100644 index 0000000000000..8b9ade678aec4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.policy.ts @@ -0,0 +1,51 @@ +import { App, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +export class TestStack extends Stack { + + readonly table: dynamodb.Table; + readonly tableTwo: dynamodb.Table; + + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + + const doc = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:*'], + principals: [new iam.AccountRootPrincipal()], + resources: ['*'], + }), + ], + }); + + this.table = new dynamodb.Table(this, 'TableTest1', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + resourcePolicy: doc, + }); + + this.tableTwo = new dynamodb.Table(this, 'TableTest2', { + partitionKey: { + name: 'PK', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + }); + + this.tableTwo.grantReadData(new iam.AccountPrincipal('123456789012')); + } +} + +const app = new App(); +const stack = new TestStack(app, 'resource-policy-stack', {}); + +new IntegTest(app, 'resource-policy-integ-test', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-dynamodb/README.md b/packages/aws-cdk-lib/aws-dynamodb/README.md index d7a8eb1fcf16a..83d3e35c7d2f2 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/README.md +++ b/packages/aws-cdk-lib/aws-dynamodb/README.md @@ -644,6 +644,36 @@ If you intend to use the `tableStreamArn` (including indirectly, for example by To grant permissions to indexes for a referenced table you can either set `grantIndexPermissions` to `true`, or you can provide the indexes via the `globalIndexes` or `localIndexes` properties. This will enable `grant*` methods to also grant permissions to *all* table indexes. +## Resource Policy + +Using `resourcePolicy` you can add a [resource policy](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/access-control-resource-based.html) to a table in the form of a `PolicyDocument`: + +``` + // resource policy document + const policy = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new iam.AccountRootPrincipal()], + resources: ['*'], + }), + ], + }); + + // table with resource policy + new dynamodb.TableV2(this, 'TableTestV2-1', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + resourcePolicy: policy, + }); +``` + +TableV2 doesn’t support creating a replica and adding a resource-based policy to that replica in the same stack update in Regions other than the Region where you deploy the stack update. +To incorporate a resource-based policy into a replica, you'll need to initially deploy the replica without the policy, followed by a subsequent update to include the desired policy. + ## Grants Using any of the `grant*` methods on an instance of the `TableV2` construct will only apply to the primary table, its indexes, and any associated `encryptionKey`. As an example, `grantReadData` used below will only apply the table in `us-west-2`: diff --git a/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md b/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md index 730c73e5a6273..ae5049af56cd8 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md +++ b/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md @@ -237,3 +237,29 @@ const table = new dynamodb.Table(this, 'Table', { deletionProtection: true, }); ``` +## Resource Policy + +Using `resourcePolicy` you can add a [resource policy](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/access-control-resource-based.html) to a table in the form of a `PolicyDocument`: + +```ts +const policy = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new iam.AccountRootPrincipal()], + resources: ['*'], + }), + ], +}); + +new dynamodb.Table(this, 'MyTable', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + removalPolicy: RemovalPolicy.DESTROY, + resourcePolicy: policy, +}); +``` + +If you have a global table replica, note that it does not support the addition of a resource-based policy. \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts index 168302320077e..89276fae405f4 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts @@ -2,7 +2,7 @@ import { DynamoDBMetrics } from './dynamodb-canned-metrics.generated'; import * as perms from './perms'; import { Operation, SystemErrorsForOperationsMetricOptions, OperationsMetricOptions, ITable } from './shared'; import { IMetric, MathExpression, Metric, MetricOptions, MetricProps } from '../../aws-cloudwatch'; -import { Grant, IGrantable } from '../../aws-iam'; +import { AddToResourcePolicyResult, Grant, IGrantable, IResourceWithPolicy, PolicyDocument, PolicyStatement } from '../../aws-iam'; import { IKey } from '../../aws-kms'; import { Resource } from '../../core'; @@ -21,7 +21,7 @@ export interface ITableV2 extends ITable { /** * Base class for a DynamoDB table. */ -export abstract class TableBaseV2 extends Resource implements ITableV2 { +export abstract class TableBaseV2 extends Resource implements ITableV2, IResourceWithPolicy { /** * The ARN of the table. * @@ -55,6 +55,11 @@ export abstract class TableBaseV2 extends Resource implements ITableV2 { */ public abstract readonly encryptionKey?: IKey; + /** + * The resource policy for the table + */ + public abstract resourcePolicy?: PolicyDocument; + protected abstract readonly region: string; protected abstract get hasIndex(): boolean; @@ -71,11 +76,11 @@ export abstract class TableBaseV2 extends Resource implements ITableV2 { public grant(grantee: IGrantable, ...actions: string[]): Grant { const resourceArns = [this.tableArn]; this.hasIndex && resourceArns.push(`${this.tableArn}/index/*`); - return Grant.addToPrincipal({ + return Grant.addToPrincipalOrResource({ grantee, actions, resourceArns, - scope: this, + resource: this, }); } @@ -426,11 +431,11 @@ export abstract class TableBaseV2 extends Resource implements ITableV2 { if (options.tableActions) { const resourceArns = [this.tableArn]; this.hasIndex && resourceArns.push(`${this.tableArn}/index/*`); - return Grant.addToPrincipal({ + return Grant.addToPrincipalOrResource({ grantee, actions: options.tableActions, resourceArns, - scope: this, + resource: this, }); } @@ -457,4 +462,22 @@ export abstract class TableBaseV2 extends Resource implements ITableV2 { account: props?.account ?? this.stack.account, }); } + + /** + * Adds a statement to the resource policy associated with this file system. + * A resource policy will be automatically created upon the first call to `addToResourcePolicy`. + * + * Note that this does not work with imported file systems. + * + * @param statement The policy statement to add + */ + public addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult { + + this.resourcePolicy = this.resourcePolicy ?? new PolicyDocument({ statements: [] }); + this.resourcePolicy.addStatements(statement); + return { + statementAdded: true, + policyDependable: this, + }; + } } diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts index 5fc1532d6b922..a309c994d33ad 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts @@ -9,6 +9,7 @@ import { SecondaryIndexProps, BillingMode, ProjectionType, } from './shared'; import { TableBaseV2, ITableV2 } from './table-v2-base'; +import { PolicyDocument } from '../../aws-iam'; import { IStream } from '../../aws-kinesis'; import { IKey, Key } from '../../aws-kms'; import { ArnFormat, CfnTag, Lazy, PhysicalName, RemovalPolicy, Stack, Token } from '../../core'; @@ -121,6 +122,13 @@ export interface TableOptionsV2 { * @default - no tags */ readonly tags?: CfnTag[]; + + /** + * Resource policy to assign to DynamoDB Table. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-globaltable-replicaspecification.html#cfn-dynamodb-globaltable-replicaspecification-resourcepolicy + * @default - No resource policy statements are added to the created table. + */ + readonly resourcePolicy?: PolicyDocument; } /** @@ -354,13 +362,14 @@ export class TableV2 extends TableBaseV2 { public readonly tableId?: string; public readonly tableStreamArn?: string; public readonly encryptionKey?: IKey; + public readonly resourcePolicy?: PolicyDocument; protected readonly region: string; protected readonly hasIndex = (attrs.grantIndexPermissions ?? false) || (attrs.globalIndexes ?? []).length > 0 || (attrs.localIndexes ?? []).length > 0; - public constructor(tableArn: string, tableName: string, tableId?: string, tableStreamArn?: string) { + public constructor(tableArn: string, tableName: string, tableId?: string, tableStreamArn?: string, resourcePolicy?: PolicyDocument) { super(scope, id, { environmentFromArn: tableArn }); const resourceRegion = stack.splitArn(tableArn, ArnFormat.SLASH_RESOURCE_NAME).region; @@ -374,6 +383,7 @@ export class TableV2 extends TableBaseV2 { this.tableId = tableId; this.tableStreamArn = tableStreamArn; this.encryptionKey = attrs.encryptionKey; + this.resourcePolicy = resourcePolicy; } } @@ -429,6 +439,11 @@ export class TableV2 extends TableBaseV2 { public readonly encryptionKey?: IKey; + /** + * @attribute + */ + public resourcePolicy?: PolicyDocument; + protected readonly region: string; private readonly billingMode: string; @@ -600,6 +615,7 @@ export class TableV2 extends TableBaseV2 { private configureReplicaTable(props: ReplicaTableProps): CfnGlobalTable.ReplicaSpecificationProperty { const pointInTimeRecovery = props.pointInTimeRecovery ?? this.tableOptions.pointInTimeRecovery; const contributorInsights = props.contributorInsights ?? this.tableOptions.contributorInsights; + const resourcePolicy = props.resourcePolicy ?? this.tableOptions.resourcePolicy; return { region: props.region, @@ -620,6 +636,9 @@ export class TableV2 extends TableBaseV2 { ? props.readCapacity._renderReadCapacity() : this.readProvisioning, tags: props.tags, + resourcePolicy: resourcePolicy + ? { policyDocument: resourcePolicy } + : undefined, }; } diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts index 270ab71b06cf0..5d38902414514 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts @@ -370,6 +370,13 @@ export interface TableOptions extends SchemaOptions { * @default - no data import from the S3 bucket */ readonly importSource?: ImportSourceSpecification; + + /** + * Resource policy to assign to table. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html#cfn-dynamodb-table-resourcepolicy + * @default - No resource policy statement + */ + readonly resourcePolicy?: iam.PolicyDocument; } /** @@ -479,7 +486,7 @@ export interface TableAttributes { readonly grantIndexPermissions?: boolean; } -export abstract class TableBase extends Resource implements ITable { +export abstract class TableBase extends Resource implements ITable, iam.IResourceWithPolicy { /** * @attribute */ @@ -500,6 +507,12 @@ export abstract class TableBase extends Resource implements ITable { */ public abstract readonly encryptionKey?: kms.IKey; + /** + * Resource policy to assign to table. + * @attribute + */ + public abstract resourcePolicy?: iam.PolicyDocument; + protected readonly regionalArns = new Array(); /** @@ -513,7 +526,7 @@ export abstract class TableBase extends Resource implements ITable { * @param actions The set of actions to allow (i.e. "dynamodb:PutItem", "dynamodb:GetItem", ...) */ public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant { - return iam.Grant.addToPrincipal({ + return iam.Grant.addToPrincipalOrResource({ grantee, actions, resourceArns: [ @@ -524,10 +537,9 @@ export abstract class TableBase extends Resource implements ITable { produce: () => this.hasIndex ? `${arn}/index/*` : Aws.NO_VALUE, })), ], - scope: this, + resource: this, }); } - /** * Adds an IAM policy statement associated with this table's stream to an * IAM principal's policy. @@ -641,6 +653,23 @@ export abstract class TableBase extends Resource implements ITable { return this.combinedGrant(grantee, { keyActions, tableActions: ['dynamodb:*'] }); } + /** + * Adds a statement to the resource policy associated with this file system. + * A resource policy will be automatically created upon the first call to `addToResourcePolicy`. + * + * Note that this does not work with imported file systems. + * + * @param statement The policy statement to add + */ + public addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult { + this.resourcePolicy = this.resourcePolicy ?? new iam.PolicyDocument({ statements: [] }); + this.resourcePolicy.addStatements(statement); + return { + statementAdded: true, + policyDependable: this, + }; + } + /** * Return the given named metric for this Table * @@ -891,11 +920,11 @@ export abstract class TableBase extends Resource implements ITable { produce: () => this.hasIndex ? `${arn}/index/*` : Aws.NO_VALUE, })), ]; - const ret = iam.Grant.addToPrincipal({ + const ret = iam.Grant.addToPrincipalOrResource({ grantee, actions: opts.tableActions, resourceArns: resources, - scope: this, + resource: this, }); return ret; } @@ -904,11 +933,11 @@ export abstract class TableBase extends Resource implements ITable { throw new Error(`DynamoDB Streams must be enabled on the table ${this.node.path}`); } const resources = [this.tableStreamArn]; - const ret = iam.Grant.addToPrincipal({ + const ret = iam.Grant.addToPrincipalOrResource({ grantee, actions: opts.streamActions, resourceArns: resources, - scope: this, + resource: this, }); return ret; } @@ -979,6 +1008,7 @@ export class Table extends TableBase { public readonly tableArn: string; public readonly tableStreamArn?: string; public readonly encryptionKey?: kms.IKey; + public resourcePolicy?: iam.PolicyDocument; protected readonly hasIndex = (attrs.grantIndexPermissions ?? false) || (attrs.globalIndexes ?? []).length > 0 || (attrs.localIndexes ?? []).length > 0; @@ -1017,6 +1047,13 @@ export class Table extends TableBase { public readonly encryptionKey?: kms.IKey; + /** + * Resource policy to assign to DynamoDB Table. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-resourcepolicy.html + * @default - No resource policy statements are added to the created table. + */ + public resourcePolicy?: iam.PolicyDocument; + /** * @attribute */ @@ -1095,6 +1132,9 @@ export class Table extends TableBase { kinesisStreamSpecification: props.kinesisStream ? { streamArn: props.kinesisStream.streamArn } : undefined, deletionProtectionEnabled: props.deletionProtection, importSourceSpecification: this.renderImportSourceSpecification(props.importSource), + resourcePolicy: props.resourcePolicy + ? { policyDocument: props.resourcePolicy } + : undefined, }); this.table.applyRemovalPolicy(props.removalPolicy); diff --git a/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts b/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts index 9bbf19b59518e..51ff5d58a01eb 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts @@ -3482,3 +3482,53 @@ describe('import source', () => { }).toThrow(`Delimiter must be a single character and one of the following: comma (,), tab (\\t), colon (:), semicolon (;), pipe (|), space ( ), got '${delimiter}'`); }); }); + +test('Resource policy test', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack'); + + const doc = new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new iam.ArnPrincipal('arn:aws:iam::111122223333:user/foobar')], + resources: ['*'], + }), + ], + }); + + // WHEN + const table = new Table(stack, 'Table', { + partitionKey: { name: 'id', type: AttributeType.STRING }, + resourcePolicy: doc, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', { + KeySchema: [ + { AttributeName: 'id', KeyType: 'HASH' }, + ], + AttributeDefinitions: [ + { AttributeName: 'id', AttributeType: 'S' }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', { + 'ResourcePolicy': { + 'PolicyDocument': { + 'Version': '2012-10-17', + 'Statement': [ + { + 'Principal': { + 'AWS': 'arn:aws:iam::111122223333:user/foobar', + }, + 'Effect': 'Allow', + 'Action': 'dynamodb:GetItem', + 'Resource': '*', + }, + ], + }, + }, + }); +}); diff --git a/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts b/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts index fc8db61eebbc4..81e9205211a0b 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/test/table-v2.test.ts @@ -1,4 +1,5 @@ import { Match, Template } from '../../assertions'; +import { ArnPrincipal, PolicyDocument, PolicyStatement } from '../../aws-iam'; import { Stream } from '../../aws-kinesis'; import { Key } from '../../aws-kms'; import { CfnDeletionPolicy, Lazy, RemovalPolicy, Stack } from '../../core'; @@ -2805,3 +2806,50 @@ describe('imports', () => { }).toThrow('Table ARN must be of the form: arn::dynamodb:::table/'); }); }); + +test('Resource policy test', () => { + // GIVEN + const stack = new Stack(undefined, 'Stack'); + + const doc = new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['dynamodb:GetItem'], + principals: [new ArnPrincipal('arn:aws:iam::111122223333:user/foobar')], + resources: ['*'], + }), + ], + }); + + // WHEN + const table = new TableV2(stack, 'Table', { + partitionKey: { name: 'metric', type: AttributeType.STRING }, + resourcePolicy: doc, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::GlobalTable', { + Replicas: [ + { + Region: { + Ref: 'AWS::Region', + }, + ResourcePolicy: { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:GetItem', + Effect: 'Allow', + Principal: { + AWS: 'arn:aws:iam::111122223333:user/foobar', + }, + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + }, + }, + ], + }); +}); \ No newline at end of file From 3f53a4551ea7d4f2b41d062b0220e071f219207f Mon Sep 17 00:00:00 2001 From: dali546 <35352237+dali546@users.noreply.github.com> Date: Thu, 30 May 2024 00:25:07 +0100 Subject: [PATCH 2/7] fix(lambda): use enum values for applicationLogLevel and systemLogLevel (#29904) ### Issue # (if applicable) ### Reason for this change Enumerate `ApplicationLogLevel` and `SystemLogLevel` to help with typing ### Description of changes Both fields should use the enum type for available options ### 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* --- .../aws-cdk-lambda-logging-config.assets.json | 4 +- ...ws-cdk-lambda-logging-config.template.json | 55 +++++++++ .../manifest.json | 14 ++- .../tree.json | 96 ++++++++++++++++ .../aws-lambda/test/integ.logging-config.ts | 9 ++ packages/aws-cdk-lib/aws-lambda/README.md | 12 +- .../aws-cdk-lib/aws-lambda/lib/function.ts | 42 +++++-- .../aws-lambda/test/logging-config.test.ts | 104 +++++++++++++++--- 8 files changed, 305 insertions(+), 31 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json index 7c3b04d5c455e..11d9b3ad0358f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.assets.json @@ -1,7 +1,7 @@ { "version": "36.0.0", "files": { - "04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc": { + "ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5": { "source": { "path": "aws-cdk-lambda-logging-config.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc.json", + "objectKey": "ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5.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-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json index 66aaf9cdff899..d477be4583206 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/aws-cdk-lambda-logging-config.template.json @@ -393,6 +393,61 @@ "DependsOn": [ "LambdaWithLogLevelServiceRole90A45743" ] + }, + "LambdaWithLogLevelV2ServiceRoleD184382A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "LambdaWithLogLevelV2D4FB10AC": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "LoggingConfig": { + "ApplicationLogLevel": "INFO", + "LogFormat": "JSON", + "SystemLogLevel": "INFO" + }, + "Role": { + "Fn::GetAtt": [ + "LambdaWithLogLevelV2ServiceRoleD184382A", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "LambdaWithLogLevelV2ServiceRoleD184382A" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json index a9452713ec2e1..c708f873f9ab5 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.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}/04a0e9e899cf60a12349bd536e5e4764854e9e0c2e4bf28caf29641d28a1aabc.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ca1d6ec19cef9e3b887e11f0ef107dd32ef686db5e138eee03b83852d2fe7db5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -130,6 +130,18 @@ "data": "LambdaWithLogLevelCBBBEFFC" } ], + "/aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaWithLogLevelV2ServiceRoleD184382A" + } + ], + "/aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaWithLogLevelV2D4FB10AC" + } + ], "/aws-cdk-lambda-logging-config/BootstrapVersion": [ { "type": "aws:cdk:logicalId", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json index 92e1464903af9..6087427154940 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.js.snapshot/tree.json @@ -721,6 +721,102 @@ "version": "0.0.0" } }, + "LambdaWithLogLevelV2": { + "id": "LambdaWithLogLevelV2", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-logging-config/LambdaWithLogLevelV2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "foo" + }, + "handler": "index.handler", + "loggingConfig": { + "logFormat": "JSON", + "systemLogLevel": "INFO", + "applicationLogLevel": "INFO" + }, + "role": { + "Fn::GetAtt": [ + "LambdaWithLogLevelV2ServiceRoleD184382A", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-cdk-lambda-logging-config/BootstrapVersion", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts index 5488727ca8413..76454daa651ce 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.logging-config.ts @@ -62,6 +62,15 @@ new Function(stack, 'LambdaWithLogLevel', { applicationLogLevel: ApplicationLogLevel.INFO, }); +new Function(stack, 'LambdaWithLogLevelV2', { + code: new InlineCode('foo'), + handler: 'index.handler', + runtime: Runtime.NODEJS_18_X, + loggingFormat: LoggingFormat.JSON, + systemLogLevelV2: SystemLogLevel.INFO, + applicationLogLevelV2: ApplicationLogLevel.INFO, +}); + new integ.IntegTest(app, 'lambda-logging-config', { testCases: [stack], }); diff --git a/packages/aws-cdk-lib/aws-lambda/README.md b/packages/aws-cdk-lib/aws-lambda/README.md index 5a876c8ae68eb..bcf108b51d1ef 100644 --- a/packages/aws-cdk-lib/aws-lambda/README.md +++ b/packages/aws-cdk-lib/aws-lambda/README.md @@ -160,20 +160,20 @@ as choosing the log group: ```ts import { ILogGroup } from 'aws-cdk-lib/aws-logs'; -declare const logGroup: ILogGroup; +declare const logGroup: ILogGroup; new lambda.Function(this, 'Lambda', { code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_18_X, loggingFormat: lambda.LoggingFormat.JSON, - systemLogLevel: lambda.SystemLogLevel.INFO, - applicationLogLevel: lambda.ApplicationLogLevel.INFO, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, logGroup: logGroup, }); ``` -To use `applicationLogLevel` and/or `systemLogLevel` you must set `loggingFormat` to `LoggingFormat.JSON`. +To use `applicationLogLevelV2` and/or `systemLogLevelV2` you must set `loggingFormat` to `LoggingFormat.JSON`. ## Resource-based Policies @@ -1077,8 +1077,8 @@ const fn = new lambda.Function(this, 'MyLambda', { ## IPv6 support -You can configure IPv6 connectivity for lambda function by setting `Ipv6AllowedForDualStack` to true. -It allows Lambda functions to specify whether the IPv6 traffic should be allowed when using dual-stack VPCs. +You can configure IPv6 connectivity for lambda function by setting `Ipv6AllowedForDualStack` to true. +It allows Lambda functions to specify whether the IPv6 traffic should be allowed when using dual-stack VPCs. To access IPv6 network using Lambda, Dual-stack VPC is required. Using dual-stack VPC a function communicates with subnet over either of IPv4 or IPv6. ```ts diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function.ts b/packages/aws-cdk-lib/aws-lambda/lib/function.ts index dba046f447513..32e477f81f86b 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function.ts @@ -524,6 +524,7 @@ export interface FunctionOptions extends EventInvokeConfigOptions { /** * Sets the logFormat for the function. + * @deprecated Use `loggingFormat` as a property instead. * @default "Text" */ readonly logFormat?: string; @@ -536,15 +537,29 @@ export interface FunctionOptions extends EventInvokeConfigOptions { /** * Sets the application log level for the function. + * @deprecated Use `applicationLogLevelV2` as a property instead. * @default "INFO" */ readonly applicationLogLevel?: string; + /** + * Sets the application log level for the function. + * @default ApplicationLogLevel.INFO + */ + readonly applicationLogLevelV2?: ApplicationLogLevel; + /** * Sets the system log level for the function. + * @deprecated Use `systemLogLevelV2` as a property instead. * @default "INFO" */ readonly systemLogLevel?: string; + + /** + * Sets the system log level for the function. + * @default SystemLogLevel.INFO + */ + readonly systemLogLevelV2?: SystemLogLevel; } export interface FunctionProps extends FunctionOptions { @@ -1151,25 +1166,34 @@ export class Function extends FunctionBase { * function and undefined if not. */ private getLoggingConfig(props: FunctionProps): CfnFunction.LoggingConfigProperty | undefined { - if ((props.applicationLogLevel || props.systemLogLevel) && props.logFormat !== LogFormat.JSON - && props.loggingFormat === undefined) { - throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LogFormat to '${LogFormat.JSON}', got '${props.logFormat}'.`); + if (props.logFormat && props.loggingFormat) { + throw new Error('Only define LogFormat or LoggingFormat, not both.'); } - if ((props.applicationLogLevel || props.systemLogLevel) && props.loggingFormat !== LoggingFormat.JSON && props.logFormat === undefined) { - throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LoggingFormat to '${LoggingFormat.JSON}', got '${props.loggingFormat}'.`); + if (props.applicationLogLevel && props.applicationLogLevelV2) { + throw new Error('Only define applicationLogLevel or applicationLogLevelV2, not both.'); } - if (props.logFormat && props.loggingFormat) { - throw new Error('Only define LogFormat or LoggingFormat, not both.'); + if (props.systemLogLevel && props.systemLogLevelV2) { + throw new Error('Only define systemLogLevel or systemLogLevelV2, not both.'); + } + + if (props.applicationLogLevel || props.applicationLogLevelV2 || props.systemLogLevel || props.systemLogLevelV2) { + if (props.logFormat !== LoggingFormat.JSON && props.loggingFormat === undefined) { + throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LogFormat to '${LogFormat.JSON}', got '${props.logFormat}'.`); + } + + if (props.loggingFormat !== LoggingFormat.JSON && props.logFormat === undefined) { + throw new Error(`To use ApplicationLogLevel and/or SystemLogLevel you must set LoggingFormat to '${LoggingFormat.JSON}', got '${props.loggingFormat}'.`); + } } let loggingConfig: CfnFunction.LoggingConfigProperty; if (props.logFormat || props.logGroup || props.loggingFormat) { loggingConfig = { logFormat: props.logFormat || props.loggingFormat, - systemLogLevel: props.systemLogLevel, - applicationLogLevel: props.applicationLogLevel, + systemLogLevel: props.systemLogLevel || props.systemLogLevelV2, + applicationLogLevel: props.applicationLogLevel || props.applicationLogLevelV2, logGroup: props.logGroup?.logGroupName, }; return loggingConfig; diff --git a/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts b/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts index 7c8f0e861c850..ae16882c1523c 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/logging-config.test.ts @@ -142,6 +142,28 @@ describe('logging Config', () => { }); }); + test('Logging Config with LogLevel set with enum keys', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, + }); + // WHEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { + LoggingConfig: { + LogFormat: 'JSON', + SystemLogLevel: 'INFO', + ApplicationLogLevel: 'INFO', + }, + }); + }); + test('Get function custom logGroup', () => { // GIVEN const app = new cdk.App(); @@ -243,18 +265,74 @@ describe('logging Config', () => { }); }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); }); -}); -test('Throws when loggingFormat and logFormat are both specified', () => { - const app = new cdk.App(); - const stack = new cdk.Stack(app, 'stack'); - expect(() => { - new lambda.Function(stack, 'Lambda', { - code: new lambda.InlineCode('foo'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_18_X, - loggingFormat: lambda.LoggingFormat.JSON, - logFormat: lambda.LogFormat.TEXT, - }); - }).toThrow(/Only define LogFormat or LoggingFormat, not both./); + test('Throws when loggingFormat and logFormat are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + logFormat: lambda.LogFormat.TEXT, + }); + }).toThrow(/Only define LogFormat or LoggingFormat, not both./); + }); + + test('Throws when applicationLogLevel and applicationLogLevelV2 are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + applicationLogLevel: lambda.ApplicationLogLevel.INFO, + applicationLogLevelV2: lambda.ApplicationLogLevel.WARN, + }); + }).toThrow(/Only define applicationLogLevel or applicationLogLevelV2, not both./); + }); + + test('Throws when systemLogLevel and systemLogLevelV2 are both specified', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + loggingFormat: lambda.LoggingFormat.JSON, + systemLogLevel: lambda.SystemLogLevel.INFO, + systemLogLevelV2: lambda.SystemLogLevel.WARN, + }); + }).toThrow(/Only define systemLogLevel or systemLogLevelV2, not both./); + }); + + test('Throws when systemLogLevelV2 is specified if loggingFormat is undefined', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + systemLogLevelV2: lambda.SystemLogLevel.INFO, + }); + }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); + }); + + test('Throws when applicationLogLevelV2 is specified if loggingFormat is undefined', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'stack'); + expect(() => { + new lambda.Function(stack, 'Lambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_18_X, + applicationLogLevelV2: lambda.ApplicationLogLevel.INFO, + }); + }).toThrow(/To use ApplicationLogLevel and\/or SystemLogLevel you must set LogFormat to 'JSON', got 'undefined'./); + }); }); From 5f229ce8281a777ea596764b9bbc09b62dc6c95f Mon Sep 17 00:00:00 2001 From: shikha372 Date: Wed, 29 May 2024 16:53:25 -0700 Subject: [PATCH 3/7] revert: "fix(ses-actions): permissions too wide for S3 action" (#30375) ### Issue # (if applicable) Closes #[30143](https://github.com/aws/aws-cdk/issues/30143). ### Reason for this change Fix the below deployment failure Deployment fails with a Could not write to bucket error: 1:36:13 PM | CREATE_FAILED | AWS::SES::ReceiptRule | TestRuleSetStoreToBucketRule3E41D5CF Could not write to bucket: reprosess3rulestack-testemailstoref58b593c-dxh45g1m3y6b (Service: AmazonSimpleEmailService; Status Code: 400; Error Code: InvalidS3Configuration; Request ID: 817f5520-748b-4bae-b347-ec68df52b675; Proxy: null) This PR reverts the changes introduced in PR https://github.com/aws/aws-cdk/pull/29833 ### Description of changes This PR reverts the change that was made in CDK v2.139.0 to reduce overly broad permissions allocated to SES for the S3 receipt rule action. This resulted in deployment failure where SES is unable to write to s3 bucket. ### Description of how you validated changes Dry-run for integration tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-ses-receipt.assets.json | 4 +- .../aws-cdk-ses-receipt.template.json | 30 +-------- .../integ.actions.js.snapshot/manifest.json | 12 +--- .../test/integ.actions.js.snapshot/tree.json | 29 +-------- .../aws-cdk-lib/aws-ses-actions/lib/s3.ts | 61 +++++++------------ .../aws-ses-actions/test/actions.test.ts | 19 +----- .../aws-ses/lib/receipt-rule-action.ts | 8 --- .../aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 17 ++---- 8 files changed, 35 insertions(+), 145 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json index 78acf6dcc87d2..e1f4e21c3afc9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json @@ -14,7 +14,7 @@ } } }, - "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb": { + "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9": { "source": { "path": "aws-cdk-ses-receipt.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "objectKey": "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.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-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json index 9c28697b45e93..c83ff34cb83ec 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json @@ -86,35 +86,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, @@ -313,6 +286,7 @@ } }, "DependsOn": [ + "BucketPolicyE9A3008A", "FunctionAllowSes1829904A" ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json index 5d007e6251fac..aae99fcf97f24 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.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}/e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -91,19 +91,13 @@ "/aws-cdk-ses-receipt/RuleSet/FirstRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetFirstRule0A27C8CC", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetFirstRule0A27C8CC" } ], "/aws-cdk-ses-receipt/RuleSet/SecondRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetSecondRule03178AD4", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetSecondRule03178AD4" } ], "/aws-cdk-ses-receipt/SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole/Resource": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json index 4976261e07769..9ff4582ab6924 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json @@ -180,35 +180,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, diff --git a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts index 156a50813f298..bc19658117120 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts @@ -42,12 +42,32 @@ export interface S3Props { * a notification to Amazon SNS. */ export class S3 implements ses.IReceiptRuleAction { - private rule?: ses.IReceiptRule; + constructor(private readonly props: S3Props) { } public bind(rule: ses.IReceiptRule): ses.ReceiptRuleActionConfig { - this.rule = rule; + // Allow SES to write to S3 bucket + // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 + const keyPattern = this.props.objectKeyPrefix || ''; + const s3Statement = new iam.PolicyStatement({ + actions: ['s3:PutObject'], + principals: [new iam.ServicePrincipal('ses.amazonaws.com')], + resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], + conditions: { + StringEquals: { + 'aws:Referer': cdk.Aws.ACCOUNT_ID, + }, + }, + }); + this.props.bucket.addToResourcePolicy(s3Statement); + + const policy = this.props.bucket.node.tryFindChild('Policy') as s3.BucketPolicy; + if (policy) { // The bucket could be imported + rule.node.addDependency(policy); + } else { + cdk.Annotations.of(rule).addWarningV2('@aws-cdk/s3:AddBucketPermissions', 'This rule is using a S3 action with an imported bucket. Ensure permission is given to SES to write to that bucket.'); + } // Allow SES to use KMS master key // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-kms @@ -79,41 +99,4 @@ export class S3 implements ses.IReceiptRuleAction { }, }; } - - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - public _applyPolicyStatement(receiptRuleSet: ses.IReceiptRuleSet): void { - if (!this.rule) { - throw new Error('Cannot apply policy statement before binding the action to a receipt rule'); - } - - // Allow SES to write to S3 bucket - // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 - const keyPattern = this.props.objectKeyPrefix || ''; - const s3Statement = new iam.PolicyStatement({ - actions: ['s3:PutObject'], - principals: [new iam.ServicePrincipal('ses.amazonaws.com')], - resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], - conditions: { - StringEquals: { - 'aws:SourceAccount': cdk.Aws.ACCOUNT_ID, - 'aws:SourceArn': cdk.Arn.format({ - partition: cdk.Aws.PARTITION, - service: 'ses', - region: cdk.Aws.REGION, - account: cdk.Aws.ACCOUNT_ID, - resource: [ - `receipt-rule-set/${receiptRuleSet.receiptRuleSetName}`, - `receipt-rule/${this.rule.receiptRuleName}`, - ].join(':'), - }), - }, - }, - }); - this.props.bucket.addToResourcePolicy(s3Statement); - } } diff --git a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts index dc2fa57d124f3..9769df6bcb4ab 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts @@ -190,26 +190,9 @@ test('add s3 action', () => { Action: 's3:PutObject', Condition: { StringEquals: { - 'aws:SourceAccount': { + 'aws:Referer': { Ref: 'AWS::AccountId', }, - 'aws:SourceArn': { - 'Fn::Join': [ - '', - [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':ses:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':receipt-rule-set/', - { Ref: 'RuleSetE30C6C48' }, - ':receipt-rule/', - { Ref: 'RuleSetRule0B1D6BCA' }, - ], - ], - }, }, }, Effect: 'Allow', diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts index 8e95fb714c19c..c63fd2f4eef85 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts @@ -1,5 +1,4 @@ import { IReceiptRule } from './receipt-rule'; -import { IReceiptRuleSet } from './receipt-rule-set'; /** * An abstract action for a receipt rule. @@ -10,13 +9,6 @@ export interface IReceiptRuleAction { */ bind(receiptRule: IReceiptRule): ReceiptRuleActionConfig; - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - _applyPolicyStatement?(ruleSet: IReceiptRuleSet): void; } /** diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index 5b9dbe89f8c4a..ae662eb3f18d4 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -112,10 +112,7 @@ export class ReceiptRule extends Resource implements IReceiptRule { } public readonly receiptRuleName: string; - - private readonly ruleSet: IReceiptRuleSet; - private readonly actions: IReceiptRuleAction[] = []; - private readonly actionProperties: CfnReceiptRule.ActionProperty[] = []; + private readonly actions = new Array(); constructor(scope: Construct, id: string, props: ReceiptRuleProps) { super(scope, id, { @@ -136,7 +133,6 @@ export class ReceiptRule extends Resource implements IReceiptRule { }); this.receiptRuleName = resource.ref; - this.ruleSet = props.ruleSet; for (const action of props.actions || []) { this.addAction(action); @@ -147,20 +143,15 @@ export class ReceiptRule extends Resource implements IReceiptRule { * Adds an action to this receipt rule. */ public addAction(action: IReceiptRuleAction) { - this.actions.push(action); - this.actionProperties.push(action.bind(this)); + this.actions.push(action.bind(this)); } private renderActions() { - if (this.actionProperties.length === 0) { + if (this.actions.length === 0) { return undefined; } - for (const action of this.actions) { - action._applyPolicyStatement?.(this.ruleSet); - } - - return this.actionProperties; + return this.actions; } } From 6c716c68ec2a222a1262577942ffde42002d2f44 Mon Sep 17 00:00:00 2001 From: shikha372 Date: Wed, 29 May 2024 16:53:25 -0700 Subject: [PATCH 4/7] revert: "fix(ses-actions): permissions too wide for S3 action" (#30375) ### Issue # (if applicable) Closes #[30143](https://github.com/aws/aws-cdk/issues/30143). ### Reason for this change Fix the below deployment failure Deployment fails with a Could not write to bucket error: 1:36:13 PM | CREATE_FAILED | AWS::SES::ReceiptRule | TestRuleSetStoreToBucketRule3E41D5CF Could not write to bucket: reprosess3rulestack-testemailstoref58b593c-dxh45g1m3y6b (Service: AmazonSimpleEmailService; Status Code: 400; Error Code: InvalidS3Configuration; Request ID: 817f5520-748b-4bae-b347-ec68df52b675; Proxy: null) This PR reverts the changes introduced in PR https://github.com/aws/aws-cdk/pull/29833 ### Description of changes This PR reverts the change that was made in CDK v2.139.0 to reduce overly broad permissions allocated to SES for the S3 receipt rule action. This resulted in deployment failure where SES is unable to write to s3 bucket. ### Description of how you validated changes Dry-run for integration tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-ses-receipt.assets.json | 4 +- .../aws-cdk-ses-receipt.template.json | 30 +-------- .../integ.actions.js.snapshot/manifest.json | 12 +--- .../test/integ.actions.js.snapshot/tree.json | 29 +-------- .../aws-cdk-lib/aws-ses-actions/lib/s3.ts | 61 +++++++------------ .../aws-ses-actions/test/actions.test.ts | 19 +----- .../aws-ses/lib/receipt-rule-action.ts | 8 --- .../aws-cdk-lib/aws-ses/lib/receipt-rule.ts | 17 ++---- 8 files changed, 35 insertions(+), 145 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json index 78acf6dcc87d2..e1f4e21c3afc9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.assets.json @@ -14,7 +14,7 @@ } } }, - "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb": { + "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9": { "source": { "path": "aws-cdk-ses-receipt.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "objectKey": "e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.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-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json index 9c28697b45e93..c83ff34cb83ec 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/aws-cdk-ses-receipt.template.json @@ -86,35 +86,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, @@ -313,6 +286,7 @@ } }, "DependsOn": [ + "BucketPolicyE9A3008A", "FunctionAllowSes1829904A" ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json index 5d007e6251fac..aae99fcf97f24 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.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}/e6588125503215c64027666d36868f7ca8e305ebf39630158558d6379dcc5fcb.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/e75d52ecdaf0f3588db5bc3c10fdcd3a347911e7ec4edd2058d2cd142329a9c9.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -91,19 +91,13 @@ "/aws-cdk-ses-receipt/RuleSet/FirstRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetFirstRule0A27C8CC", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetFirstRule0A27C8CC" } ], "/aws-cdk-ses-receipt/RuleSet/SecondRule/Resource": [ { "type": "aws:cdk:logicalId", - "data": "RuleSetSecondRule03178AD4", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" - ] + "data": "RuleSetSecondRule03178AD4" } ], "/aws-cdk-ses-receipt/SingletonLambda224e77f9a32e4b4dac32983477abba16/ServiceRole/Resource": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json index 4976261e07769..9ff4582ab6924 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ses-actions/test/integ.actions.js.snapshot/tree.json @@ -180,35 +180,8 @@ "Action": "s3:PutObject", "Condition": { "StringEquals": { - "aws:SourceAccount": { + "aws:Referer": { "Ref": "AWS::AccountId" - }, - "aws:SourceArn": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":ses:", - { - "Ref": "AWS::Region" - }, - ":", - { - "Ref": "AWS::AccountId" - }, - ":receipt-rule-set/", - { - "Ref": "RuleSetE30C6C48" - }, - ":receipt-rule/", - { - "Ref": "RuleSetFirstRule0A27C8CC" - } - ] - ] } } }, diff --git a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts index 156a50813f298..bc19658117120 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/lib/s3.ts @@ -42,12 +42,32 @@ export interface S3Props { * a notification to Amazon SNS. */ export class S3 implements ses.IReceiptRuleAction { - private rule?: ses.IReceiptRule; + constructor(private readonly props: S3Props) { } public bind(rule: ses.IReceiptRule): ses.ReceiptRuleActionConfig { - this.rule = rule; + // Allow SES to write to S3 bucket + // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 + const keyPattern = this.props.objectKeyPrefix || ''; + const s3Statement = new iam.PolicyStatement({ + actions: ['s3:PutObject'], + principals: [new iam.ServicePrincipal('ses.amazonaws.com')], + resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], + conditions: { + StringEquals: { + 'aws:Referer': cdk.Aws.ACCOUNT_ID, + }, + }, + }); + this.props.bucket.addToResourcePolicy(s3Statement); + + const policy = this.props.bucket.node.tryFindChild('Policy') as s3.BucketPolicy; + if (policy) { // The bucket could be imported + rule.node.addDependency(policy); + } else { + cdk.Annotations.of(rule).addWarningV2('@aws-cdk/s3:AddBucketPermissions', 'This rule is using a S3 action with an imported bucket. Ensure permission is given to SES to write to that bucket.'); + } // Allow SES to use KMS master key // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-kms @@ -79,41 +99,4 @@ export class S3 implements ses.IReceiptRuleAction { }, }; } - - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - public _applyPolicyStatement(receiptRuleSet: ses.IReceiptRuleSet): void { - if (!this.rule) { - throw new Error('Cannot apply policy statement before binding the action to a receipt rule'); - } - - // Allow SES to write to S3 bucket - // See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-permissions.html#receiving-email-permissions-s3 - const keyPattern = this.props.objectKeyPrefix || ''; - const s3Statement = new iam.PolicyStatement({ - actions: ['s3:PutObject'], - principals: [new iam.ServicePrincipal('ses.amazonaws.com')], - resources: [this.props.bucket.arnForObjects(`${keyPattern}*`)], - conditions: { - StringEquals: { - 'aws:SourceAccount': cdk.Aws.ACCOUNT_ID, - 'aws:SourceArn': cdk.Arn.format({ - partition: cdk.Aws.PARTITION, - service: 'ses', - region: cdk.Aws.REGION, - account: cdk.Aws.ACCOUNT_ID, - resource: [ - `receipt-rule-set/${receiptRuleSet.receiptRuleSetName}`, - `receipt-rule/${this.rule.receiptRuleName}`, - ].join(':'), - }), - }, - }, - }); - this.props.bucket.addToResourcePolicy(s3Statement); - } } diff --git a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts index dc2fa57d124f3..9769df6bcb4ab 100644 --- a/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts +++ b/packages/aws-cdk-lib/aws-ses-actions/test/actions.test.ts @@ -190,26 +190,9 @@ test('add s3 action', () => { Action: 's3:PutObject', Condition: { StringEquals: { - 'aws:SourceAccount': { + 'aws:Referer': { Ref: 'AWS::AccountId', }, - 'aws:SourceArn': { - 'Fn::Join': [ - '', - [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':ses:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':receipt-rule-set/', - { Ref: 'RuleSetE30C6C48' }, - ':receipt-rule/', - { Ref: 'RuleSetRule0B1D6BCA' }, - ], - ], - }, }, }, Effect: 'Allow', diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts index 8e95fb714c19c..c63fd2f4eef85 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule-action.ts @@ -1,5 +1,4 @@ import { IReceiptRule } from './receipt-rule'; -import { IReceiptRuleSet } from './receipt-rule-set'; /** * An abstract action for a receipt rule. @@ -10,13 +9,6 @@ export interface IReceiptRuleAction { */ bind(receiptRule: IReceiptRule): ReceiptRuleActionConfig; - /** - * Generate and apply the receipt rule action statement - * - * @param ruleSet The rule set the rule is being added to - * @internal - */ - _applyPolicyStatement?(ruleSet: IReceiptRuleSet): void; } /** diff --git a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts index 5b9dbe89f8c4a..ae662eb3f18d4 100644 --- a/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts +++ b/packages/aws-cdk-lib/aws-ses/lib/receipt-rule.ts @@ -112,10 +112,7 @@ export class ReceiptRule extends Resource implements IReceiptRule { } public readonly receiptRuleName: string; - - private readonly ruleSet: IReceiptRuleSet; - private readonly actions: IReceiptRuleAction[] = []; - private readonly actionProperties: CfnReceiptRule.ActionProperty[] = []; + private readonly actions = new Array(); constructor(scope: Construct, id: string, props: ReceiptRuleProps) { super(scope, id, { @@ -136,7 +133,6 @@ export class ReceiptRule extends Resource implements IReceiptRule { }); this.receiptRuleName = resource.ref; - this.ruleSet = props.ruleSet; for (const action of props.actions || []) { this.addAction(action); @@ -147,20 +143,15 @@ export class ReceiptRule extends Resource implements IReceiptRule { * Adds an action to this receipt rule. */ public addAction(action: IReceiptRuleAction) { - this.actions.push(action); - this.actionProperties.push(action.bind(this)); + this.actions.push(action.bind(this)); } private renderActions() { - if (this.actionProperties.length === 0) { + if (this.actions.length === 0) { return undefined; } - for (const action of this.actions) { - action._applyPolicyStatement?.(this.ruleSet); - } - - return this.actionProperties; + return this.actions; } } From eb451cb53157192d9b475bec3c020d6c79e97449 Mon Sep 17 00:00:00 2001 From: shikha372 Date: Wed, 29 May 2024 17:16:29 -0700 Subject: [PATCH 5/7] chore(release): 2.143.1 --- CHANGELOG.v2.alpha.md | 2 ++ CHANGELOG.v2.md | 7 +++++++ version.v2.json | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.v2.alpha.md b/CHANGELOG.v2.alpha.md index 50700b1c8c176..b2a31e5941ac4 100644 --- a/CHANGELOG.v2.alpha.md +++ b/CHANGELOG.v2.alpha.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.143.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.143.0-alpha.0...v2.143.1-alpha.0) (2024-05-30) + ## [2.143.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.142.1-alpha.0...v2.143.0-alpha.0) (2024-05-23) ## [2.142.1-alpha.0](https://github.com/aws/aws-cdk/compare/v2.142.0-alpha.0...v2.142.1-alpha.0) (2024-05-17) diff --git a/CHANGELOG.v2.md b/CHANGELOG.v2.md index d040cf622e9e5..cd5098ee5e3ba 100644 --- a/CHANGELOG.v2.md +++ b/CHANGELOG.v2.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.143.1](https://github.com/aws/aws-cdk/compare/v2.143.0...v2.143.1) (2024-05-30) + + +### Reverts + +* fix(ses-actions): permissions too wide for S3 action ([#30375](https://github.com/aws/aws-cdk/issues/30375)) ([6c716c6](https://github.com/aws/aws-cdk/commit/6c716c68ec2a222a1262577942ffde42002d2f44)) + ## [2.143.0](https://github.com/aws/aws-cdk/compare/v2.142.1...v2.143.0) (2024-05-23) diff --git a/version.v2.json b/version.v2.json index 3e828334f81c6..bc1c9a5c4d295 100644 --- a/version.v2.json +++ b/version.v2.json @@ -1,4 +1,4 @@ { - "version": "2.143.0", - "alphaVersion": "2.143.0-alpha.0" + "version": "2.143.1", + "alphaVersion": "2.143.1-alpha.0" } \ No newline at end of file From 0c1aeb6d6d9b9d72624a394fff45e65dfb94b733 Mon Sep 17 00:00:00 2001 From: mazyu36 Date: Thu, 30 May 2024 11:38:37 +0900 Subject: [PATCH 6/7] feat(apprunner): add kmsKey property for the AppRunner Service class (#30352) ### Issue # (if applicable) Close #30365. ### Reason for this change AppRunner supports for using a customer managed key to encrypt all stored copies of your application source image or source bundle. https://docs.aws.amazon.com/apprunner/latest/dg/security-data-protection-encryption.html But L2 Construct (alpha module) cannot use a customer managed key. ### Description of changes Add kmsKey property to the Service class. ### Description of how you validated changes Add unit tests and integ tests ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-apprunner-alpha/README.md | 22 +- .../aws-apprunner-alpha/lib/service.ts | 13 +- ...efaultTestDeployAssert21640739.assets.json | 19 ++ ...aultTestDeployAssert21640739.template.json | 36 +++ .../cdk.out | 1 + .../integ-apprunner-encryption.assets.json | 19 ++ .../integ-apprunner-encryption.template.json | 146 ++++++++++ .../integ.json | 12 + .../manifest.json | 131 +++++++++ .../tree.json | 259 ++++++++++++++++++ .../test/integ.service-encryption.ts | 30 ++ .../aws-apprunner-alpha/test/service.test.ts | 25 ++ 12 files changed, 710 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.ts diff --git a/packages/@aws-cdk/aws-apprunner-alpha/README.md b/packages/@aws-cdk/aws-apprunner-alpha/README.md index 1cc9363c8c139..22c95ccdf2b23 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/README.md +++ b/packages/@aws-cdk/aws-apprunner-alpha/README.md @@ -32,7 +32,7 @@ The `Service` construct allows you to create AWS App Runner services with `ECR P - `Source.fromEcr()` - To define the source repository from `ECR`. - `Source.fromEcrPublic()` - To define the source repository from `ECR Public`. - `Source.fromGitHub()` - To define the source repository from the `Github repository`. -- `Source.fromAsset()` - To define the source from local asset directory. +- `Source.fromAsset()` - To define the source from local asset directory. The `Service` construct implements `IGrantable`. @@ -183,7 +183,7 @@ new apprunner.Service(this, 'Service', { ## Secrets Manager To include environment variables integrated with AWS Secrets Manager, use the `environmentSecrets` attribute. -You can use the `addSecret` method from the App Runner `Service` class to include secrets from outside the +You can use the `addSecret` method from the App Runner `Service` class to include secrets from outside the service definition. ```ts @@ -216,6 +216,24 @@ const service = new apprunner.Service(stack, 'Service', { service.addSecret('LATER_SECRET', apprunner.Secret.fromSecretsManager(secret, 'field')); ``` +## Use a customer managed key + +To use a customer managed key for your source encryption, use the `kmsKey` attribute. + +```ts +import * as kms from 'aws-cdk-lib/aws-kms'; + +declare const kmsKey: kms.IKey; + +new apprunner.Service(this, 'Service', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + kmsKey, +}); +``` + ## HealthCheck To configure the health check for the service, use the `healthCheck` attribute. diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts index 9564bbae18cf3..c66fb75d850bf 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts @@ -1,6 +1,7 @@ import * as ecr from 'aws-cdk-lib/aws-ecr'; import * as assets from 'aws-cdk-lib/aws-ecr-assets'; import * as iam from 'aws-cdk-lib/aws-iam'; +import * as kms from 'aws-cdk-lib/aws-kms'; import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; import * as ssm from 'aws-cdk-lib/aws-ssm'; import * as cdk from 'aws-cdk-lib/core'; @@ -79,7 +80,7 @@ export class Cpu { * * @param unit The unit of CPU. */ - private constructor(public readonly unit: string) {} + private constructor(public readonly unit: string) { } } /** @@ -715,6 +716,13 @@ export interface ServiceProps { * @default - no health check configuration */ readonly healthCheck?: HealthCheck; + + /** + * The customer managed key that AWS App Runner uses to encrypt copies of the source repository and service logs. + * + * @default - Use an AWS managed key + */ + readonly kmsKey?: kms.IKey; } /** @@ -1239,6 +1247,9 @@ export class Service extends cdk.Resource implements iam.IGrantable { this.renderCodeConfiguration(this.source.codeRepository!.codeConfiguration.configurationValues!) : undefined, }, + encryptionConfiguration: this.props.kmsKey ? { + kmsKey: this.props.kmsKey.keyArn, + } : undefined, networkConfiguration: { egressConfiguration: { egressType: this.props.vpcConnector ? 'VPC' : 'DEFAULT', diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.assets.json new file mode 100644 index 0000000000000..770a0d82991ff --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "AppRunnerEncryptionDefaultTestDeployAssert21640739.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/AppRunnerEncryptionDefaultTestDeployAssert21640739.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/cdk.out b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.assets.json new file mode 100644 index 0000000000000..3cb0c96545ab8 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "0fe6f95cda1a644231803b5acf00b70af151133660b969f529838e037d6ffcfd": { + "source": { + "path": "integ-apprunner-encryption.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "0fe6f95cda1a644231803b5acf00b70af151133660b969f529838e037d6ffcfd.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.template.json new file mode 100644 index 0000000000000..a66370cbe1fc5 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ-apprunner-encryption.template.json @@ -0,0 +1,146 @@ +{ + "Resources": { + "Key961B73FD": { + "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": "*" + } + ], + "Version": "2012-10-17" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ServiceInstanceRoleDFA90CEC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ServiceDBC79909": { + "Type": "AWS::AppRunner::Service", + "Properties": { + "EncryptionConfiguration": { + "KmsKey": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + } + }, + "InstanceConfiguration": { + "InstanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "NetworkConfiguration": { + "EgressConfiguration": { + "EgressType": "DEFAULT" + } + }, + "ServiceName": "service", + "SourceConfiguration": { + "AuthenticationConfiguration": {}, + "AutoDeploymentsEnabled": false, + "ImageRepository": { + "ImageConfiguration": { + "Port": "8000" + }, + "ImageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "ImageRepositoryType": "ECR_PUBLIC" + } + } + } + } + }, + "Outputs": { + "URL": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "ServiceDBC79909", + "ServiceUrl" + ] + } + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ.json new file mode 100644 index 0000000000000..5163e80181b81 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "AppRunnerEncryption/DefaultTest": { + "stacks": [ + "integ-apprunner-encryption" + ], + "assertionStack": "AppRunnerEncryption/DefaultTest/DeployAssert", + "assertionStackName": "AppRunnerEncryptionDefaultTestDeployAssert21640739" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/manifest.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/manifest.json new file mode 100644 index 0000000000000..2de3a0a6da66d --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-apprunner-encryption.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-apprunner-encryption.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-apprunner-encryption": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-apprunner-encryption.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}/0fe6f95cda1a644231803b5acf00b70af151133660b969f529838e037d6ffcfd.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-apprunner-encryption.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-apprunner-encryption.assets" + ], + "metadata": { + "/integ-apprunner-encryption/Key/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Key961B73FD" + } + ], + "/integ-apprunner-encryption/Service/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceInstanceRoleDFA90CEC" + } + ], + "/integ-apprunner-encryption/Service/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceDBC79909" + } + ], + "/integ-apprunner-encryption/URL": [ + { + "type": "aws:cdk:logicalId", + "data": "URL" + } + ], + "/integ-apprunner-encryption/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-apprunner-encryption/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-apprunner-encryption" + }, + "AppRunnerEncryptionDefaultTestDeployAssert21640739.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "AppRunnerEncryptionDefaultTestDeployAssert21640739.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "AppRunnerEncryptionDefaultTestDeployAssert21640739": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "AppRunnerEncryptionDefaultTestDeployAssert21640739.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "AppRunnerEncryptionDefaultTestDeployAssert21640739.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "AppRunnerEncryptionDefaultTestDeployAssert21640739.assets" + ], + "metadata": { + "/AppRunnerEncryption/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/AppRunnerEncryption/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "AppRunnerEncryption/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/tree.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/tree.json new file mode 100644 index 0000000000000..1cf14daae78a6 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.js.snapshot/tree.json @@ -0,0 +1,259 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-apprunner-encryption": { + "id": "integ-apprunner-encryption", + "path": "integ-apprunner-encryption", + "children": { + "Key": { + "id": "Key", + "path": "integ-apprunner-encryption/Key", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-apprunner-encryption/Key/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": "*" + } + ], + "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" + } + }, + "Service": { + "id": "Service", + "path": "integ-apprunner-encryption/Service", + "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-apprunner-encryption/Service/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-apprunner-encryption/Service/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-encryption/Service/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-encryption/Service/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppRunner::Service", + "aws:cdk:cloudformation:props": { + "encryptionConfiguration": { + "kmsKey": { + "Fn::GetAtt": [ + "Key961B73FD", + "Arn" + ] + } + }, + "instanceConfiguration": { + "instanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "networkConfiguration": { + "egressConfiguration": { + "egressType": "DEFAULT" + } + }, + "serviceName": "service", + "sourceConfiguration": { + "authenticationConfiguration": {}, + "autoDeploymentsEnabled": false, + "imageRepository": { + "imageConfiguration": { + "port": "8000" + }, + "imageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "imageRepositoryType": "ECR_PUBLIC" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apprunner.CfnService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "URL": { + "id": "URL", + "path": "integ-apprunner-encryption/URL", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-apprunner-encryption/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-apprunner-encryption/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "AppRunnerEncryption": { + "id": "AppRunnerEncryption", + "path": "AppRunnerEncryption", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "AppRunnerEncryption/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "AppRunnerEncryption/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "AppRunnerEncryption/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "AppRunnerEncryption/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "AppRunnerEncryption/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.ts new file mode 100644 index 0000000000000..17149a4b5fbd2 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-encryption.ts @@ -0,0 +1,30 @@ +import * as cdk from 'aws-cdk-lib'; +import { Service, Source } from '../lib'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as integ from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'integ-apprunner-encryption'); + +const key = new kms.Key(stack, 'Key', { removalPolicy: cdk.RemovalPolicy.DESTROY }); + +const service = new Service(stack, 'Service', { + serviceName: 'service', + source: Source.fromEcrPublic({ + imageConfiguration: { + port: 8000, + }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + autoDeploymentsEnabled: false, + kmsKey: key, +}); + +new cdk.CfnOutput(stack, 'URL', { value: `https://${service.serviceUrl}` }); + +new integ.IntegTest(app, 'AppRunnerEncryption', { + testCases: [stack], +}); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts index d4ef80d552bd1..389c36de9c7b5 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts @@ -3,6 +3,7 @@ import { Match, Template } from 'aws-cdk-lib/assertions'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as ecr from 'aws-cdk-lib/aws-ecr'; import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets'; +import * as kms from 'aws-cdk-lib/aws-kms'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; import * as ssm from 'aws-cdk-lib/aws-ssm'; @@ -1579,4 +1580,28 @@ test('timeout must be less than or equal to 20 in healthCheck', () => { }), }); }).toThrow('timeout must be between 1 and 20 seconds, got 21'); +}); + +test('create a service with a customer managed key)', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'demo-stack'); + const key = new kms.Key(stack, 'Key'); + + // WHEN + new apprunner.Service(stack, 'DemoService', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + kmsKey: key, + }); + + // THEN + // we should have the service + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', { + EncryptionConfiguration: { + KmsKey: stack.resolve(key.keyArn), + }, + }); }); \ No newline at end of file From 8b234b71f2bbfec8ceca4e062674290eb51c8c9b Mon Sep 17 00:00:00 2001 From: MohanprasadKumar ST <67147891+stm29@users.noreply.github.com> Date: Thu, 30 May 2024 17:10:05 +0530 Subject: [PATCH 7/7] fix(ecs): removed omitEmptyArray for placementConstraints (#27555) (#30382) ### Issue fixes #27555 Closes Half Fix [i.e, Allows user to give Empty Placement Constraints ] #27555 This PR does not address supporting empty placement strategies because of the following reason : [27555 : comment](https://github.com/aws/aws-cdk/pull/27572#issuecomment-1766287866) This was raised with the guidance from - [pr / 28431 : Comment ](https://github.com/aws/aws-cdk/pull/28431#issuecomment-1865233495) ### Reason for this change Users unable to give empty placementConstraints ### Description of how you validated changes - Added a UnitCase to cover with empty `[]` placementConstraints - Integration Tests ``` $ yarn integ test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js --update-on-failed ``` After integ tests were completed, `npm test` to verify the snapshot. ### 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* --- ...efaultTestDeployAssert4E6713E1.assets.json | 19 + ...aultTestDeployAssert4E6713E1.template.json | 36 + ...pty-placement-constraint-stack.assets.json | 19 + ...y-placement-constraint-stack.template.json | 1002 ++++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 365 ++++ .../tree.json | 1687 +++++++++++++++++ ...nteg.placement-constraint-default-empty.ts | 36 + .../aws-ecs/lib/ec2/ec2-service.ts | 11 +- .../aws-ecs/test/ec2/ec2-service.test.ts | 25 + 11 files changed, 3209 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json new file mode 100644 index 0000000000000..2af610f0d4a39 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "IntegDefaultTestDeployAssert4E6713E1.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets.json new file mode 100644 index 0000000000000..4b9f8c5c41ab3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "7e266e55d90d8a167dc9b1545687d2ec9103fc0e7fe4fa3c31d030f07ef554c7": { + "source": { + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "7e266e55d90d8a167dc9b1545687d2ec9103fc0e7fe4fa3c31d030f07ef554c7.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.template.json new file mode 100644 index 0000000000000..8add7ae0bef23 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.template.json @@ -0,0 +1,1002 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.0.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet1DefaultRoute91CEF279", + "VPCPublicSubnet1RouteTableAssociation0B0896DC" + ] + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.64.0/18", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ] + }, + "DependsOn": [ + "VPCPublicSubnet2DefaultRouteB7481BBA", + "VPCPublicSubnet2RouteTableAssociation5A808732" + ] + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.128.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + }, + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "CidrBlock": "10.0.192.0/18", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + }, + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "EcsCluster97242B84": { + "Type": "AWS::ECS::Cluster" + }, + "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecr:GetAuthorizationToken", + "ecs:DiscoverPollEndpoint", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A": { + "Type": "AWS::EC2::LaunchTemplate", + "Properties": { + "LaunchTemplateData": { + "IamInstanceProfile": { + "Arn": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3", + "Arn" + ] + } + }, + "ImageId": { + "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.micro", + "Monitoring": { + "Enabled": false + }, + "SecurityGroupIds": [ + { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231", + "GroupId" + ] + } + ], + "TagSpecifications": [ + { + "ResourceType": "instance", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + }, + { + "ResourceType": "volume", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + } + ], + "UserData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "EcsCluster97242B84" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "TagSpecifications": [ + { + "ResourceType": "launch-template", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + } + ] + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + ] + }, + "EcsClusterDefaultAutoScalingGroupASGC1A785DB": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "LaunchTemplate": { + "LaunchTemplateId": { + "Ref": "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A" + }, + "Version": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A", + "LatestVersionNumber" + ] + } + }, + "MaxSize": "1", + "MinSize": "1", + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + }, + "UpdatePolicy": { + "AutoScalingReplacingUpdate": { + "WillReplace": true + }, + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:DescribeHosts", + "ec2:DescribeInstanceAttribute", + "ec2:DescribeInstanceStatus", + "ec2:DescribeInstances" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "autoscaling:CompleteLifecycleAction", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":autoscaling:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + } + ] + ] + } + }, + { + "Action": [ + "ecs:DescribeContainerInstances", + "ecs:DescribeTasks", + "ecs:ListTasks", + "ecs:UpdateContainerInstancesState" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:ListContainerInstances", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "import boto3, json, os, time\n\necs = boto3.client('ecs')\nautoscaling = boto3.client('autoscaling')\n\n\ndef lambda_handler(event, context):\n print(json.dumps(dict(event, ResponseURL='...')))\n cluster = os.environ['CLUSTER']\n snsTopicArn = event['Records'][0]['Sns']['TopicArn']\n lifecycle_event = json.loads(event['Records'][0]['Sns']['Message'])\n instance_id = lifecycle_event.get('EC2InstanceId')\n if not instance_id:\n print('Got event without EC2InstanceId: %s', json.dumps(dict(event, ResponseURL='...')))\n return\n\n instance_arn = container_instance_arn(cluster, instance_id)\n print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn))\n\n if not instance_arn:\n return\n\n task_arns = container_instance_task_arns(cluster, instance_arn)\n\n if task_arns:\n print('Instance ARN %s has task ARNs %s' % (instance_arn, ', '.join(task_arns)))\n\n while has_tasks(cluster, instance_arn, task_arns):\n time.sleep(10)\n\n try:\n print('Terminating instance %s' % instance_id)\n autoscaling.complete_lifecycle_action(\n LifecycleActionResult='CONTINUE',\n **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName'))\n except Exception as e:\n # Lifecycle action may have already completed.\n print(str(e))\n\n\ndef container_instance_arn(cluster, instance_id):\n \"\"\"Turn an instance ID into a container instance ARN.\"\"\"\n arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns']\n if not arns:\n return None\n return arns[0]\n\ndef container_instance_task_arns(cluster, instance_arn):\n \"\"\"Fetch tasks for a container instance ARN.\"\"\"\n arns = ecs.list_tasks(cluster=cluster, containerInstance=instance_arn)['taskArns']\n return arns\n\ndef has_tasks(cluster, instance_arn, task_arns):\n \"\"\"Return True if the instance is running tasks for the given cluster.\"\"\"\n instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances']\n if not instances:\n return False\n instance = instances[0]\n\n if instance['status'] == 'ACTIVE':\n # Start draining, then try again later\n set_container_instance_to_draining(cluster, instance_arn)\n return True\n\n task_count = None\n\n if task_arns:\n # Fetch details for tasks running on the container instance\n tasks = ecs.describe_tasks(cluster=cluster, tasks=task_arns)['tasks']\n if tasks:\n # Consider any non-stopped tasks as running\n task_count = sum(task['lastStatus'] != 'STOPPED' for task in tasks) + instance['pendingTasksCount']\n\n if not task_count:\n # Fallback to instance task counts if detailed task information is unavailable\n task_count = instance['runningTasksCount'] + instance['pendingTasksCount']\n\n print('Instance %s has %s tasks' % (instance_arn, task_count))\n\n return task_count > 0\n\ndef set_container_instance_to_draining(cluster, instance_arn):\n ecs.update_container_instances_state(\n cluster=cluster,\n containerInstances=[instance_arn], status='DRAINING')\n\n\ndef pick(dct, *keys):\n \"\"\"Pick a subset of a dict.\"\"\"\n return {k: v for k, v in dct.items() if k in keys}\n" + }, + "Environment": { + "Variables": { + "CLUSTER": { + "Ref": "EcsCluster97242B84" + } + } + }, + "Handler": "index.lambda_handler", + "Role": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ], + "Timeout": 310 + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + ] + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D930C32786": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + }, + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Endpoint": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", + "Arn" + ] + }, + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { + "Type": "AWS::SNS::Topic", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "autoscaling.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Effect": "Allow", + "Resource": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "Roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" + } + ] + } + }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { + "Type": "AWS::AutoScaling::LifecycleHook", + "Properties": { + "AutoScalingGroupName": { + "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + }, + "DefaultResult": "CONTINUE", + "HeartbeatTimeout": 300, + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", + "NotificationTargetARN": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "RoleARN": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", + "Arn" + ] + } + }, + "DependsOn": [ + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" + ] + }, + "TaskDefTaskRole1EDB4A67": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TaskDef54694570": { + "Type": "AWS::ECS::TaskDefinition", + "Properties": { + "ContainerDefinitions": [ + { + "Essential": true, + "Image": "amazon/amazon-ecs-sample", + "Memory": 256, + "Name": "web" + } + ], + "Family": "awscdkecsintegtestwithemptyplacementconstraintstackTaskDef2E5DA7C2", + "NetworkMode": "bridge", + "RequiresCompatibilities": [ + "EC2" + ], + "TaskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "TestStackService5548C840": { + "Type": "AWS::ECS::Service", + "Properties": { + "Cluster": { + "Ref": "EcsCluster97242B84" + }, + "DeploymentConfiguration": { + "MaximumPercent": 200, + "MinimumHealthyPercent": 50 + }, + "EnableECSManagedTags": false, + "LaunchType": "EC2", + "PlacementConstraints": [], + "SchedulingStrategy": "REPLICA", + "TaskDefinition": { + "Ref": "TaskDef54694570" + } + }, + "DependsOn": [ + "TaskDefTaskRole1EDB4A67" + ] + } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + }, + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/integ.json new file mode 100644 index 0000000000000..7b47c4ec44dc5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "Integ/DefaultTest": { + "stacks": [ + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack" + ], + "assertionStack": "Integ/DefaultTest/DeployAssert", + "assertionStackName": "IntegDefaultTestDeployAssert4E6713E1" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/manifest.json new file mode 100644 index 0000000000000..6320e27294f2a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/manifest.json @@ -0,0 +1,365 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.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}/7e266e55d90d8a167dc9b1545687d2ec9103fc0e7fe4fa3c31d030f07ef554c7.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack.assets" + ], + "metadata": { + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1SubnetB4246D30" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableFEE4B781" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1DefaultRoute91CEF279" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1EIP6AD938E8" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1NATGatewayE0556630" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2Subnet74179F39" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTable6F1A15F1" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTableAssociation5A808732" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2DefaultRouteB7481BBA" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2EIP4947BC00" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2NATGateway3C070193" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableBE8A6027" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTable0A19E10E" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsCluster97242B84" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/ASG": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/AllowInvoke:awscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D9": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D930C32786" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Topic/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": [ + { + "type": "aws:cdk:logicalId", + "data": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/TaskRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TaskDefTaskRole1EDB4A67" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TaskDef54694570" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/Test_Stack/Service": [ + { + "type": "aws:cdk:logicalId", + "data": "TestStackService5548C840" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack" + }, + "IntegDefaultTestDeployAssert4E6713E1.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "IntegDefaultTestDeployAssert4E6713E1.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "IntegDefaultTestDeployAssert4E6713E1": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IntegDefaultTestDeployAssert4E6713E1.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}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "IntegDefaultTestDeployAssert4E6713E1.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "IntegDefaultTestDeployAssert4E6713E1.assets" + ], + "metadata": { + "/Integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Integ/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/tree.json new file mode 100644 index 0000000000000..9735f74c862a7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.js.snapshot/tree.json @@ -0,0 +1,1687 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack": { + "id": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack", + "children": { + "VPC": { + "id": "VPC", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.0.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "routeTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PublicSubnet2": { + "id": "PublicSubnet2", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.64.0/18", + "mapPublicIpOnLaunch": true, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Public" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Public" + }, + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "gatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "routeTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + }, + "EIP": { + "id": "EIP", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2/NATGateway", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::NatGateway", + "aws:cdk:cloudformation:props": { + "allocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "subnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnNatGateway", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PublicSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet1": { + "id": "PrivateSubnet1", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 0, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.128.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet1/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + }, + "routeTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "PrivateSubnet2": { + "id": "PrivateSubnet2", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "availabilityZone": { + "Fn::Select": [ + 1, + { + "Fn::GetAZs": "" + } + ] + }, + "cidrBlock": "10.0.192.0/18", + "mapPublicIpOnLaunch": false, + "tags": [ + { + "key": "aws-cdk:subnet-name", + "value": "Private" + }, + { + "key": "aws-cdk:subnet-type", + "value": "Private" + }, + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "subnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + }, + "DefaultRoute": { + "id": "DefaultRoute", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/PrivateSubnet2/DefaultRoute", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "0.0.0.0/0", + "natGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + }, + "routeTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.PrivateSubnet", + "version": "0.0.0" + } + }, + "IGW": { + "id": "IGW", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/VPC/VPCGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCGatewayAttachment", + "aws:cdk:cloudformation:props": { + "internetGatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCGatewayAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "EcsCluster": { + "id": "EcsCluster", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Cluster", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnCluster", + "version": "0.0.0" + } + }, + "DefaultAutoScalingGroup": { + "id": "DefaultAutoScalingGroup", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup", + "children": { + "InstanceSecurityGroup": { + "id": "InstanceSecurityGroup", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceSecurityGroup", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSecurityGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.SecurityGroup", + "version": "0.0.0" + } + }, + "InstanceRole": { + "id": "InstanceRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ecs:DeregisterContainerInstance", + "ecs:RegisterContainerInstance", + "ecs:Submit*" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + }, + { + "Action": [ + "ecs:Poll", + "ecs:StartTelemetrySession" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecr:GetAuthorizationToken", + "ecs:DiscoverPollEndpoint", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "EcsClusterDefaultAutoScalingGroupInstanceRoleDefaultPolicy04DC6C80", + "roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "InstanceProfile": { + "id": "InstanceProfile", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/InstanceProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::InstanceProfile", + "aws:cdk:cloudformation:props": { + "roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupInstanceRole3C026863" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnInstanceProfile", + "version": "0.0.0" + } + }, + "ImportedInstanceProfile": { + "id": "ImportedInstanceProfile", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/ImportedInstanceProfile", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "LaunchTemplate": { + "id": "LaunchTemplate", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::LaunchTemplate", + "aws:cdk:cloudformation:props": { + "launchTemplateData": { + "iamInstanceProfile": { + "arn": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupInstanceProfile2CE606B3", + "Arn" + ] + } + }, + "imageId": { + "Ref": "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "instanceType": "t2.micro", + "monitoring": { + "enabled": false + }, + "securityGroupIds": [ + { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupInstanceSecurityGroup912E1231", + "GroupId" + ] + } + ], + "tagSpecifications": [ + { + "resourceType": "instance", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + }, + { + "resourceType": "volume", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + } + ], + "userData": { + "Fn::Base64": { + "Fn::Join": [ + "", + [ + "#!/bin/bash\necho ECS_CLUSTER=", + { + "Ref": "EcsCluster97242B84" + }, + " >> /etc/ecs/ecs.config\nsudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\nsudo service iptables save\necho ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config" + ] + ] + } + } + }, + "tagSpecifications": [ + { + "resourceType": "launch-template", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LaunchTemplate" + } + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnLaunchTemplate", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.LaunchTemplate", + "version": "0.0.0" + } + }, + "ASG": { + "id": "ASG", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/ASG", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AutoScaling::AutoScalingGroup", + "aws:cdk:cloudformation:props": { + "launchTemplate": { + "launchTemplateId": { + "Ref": "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A" + }, + "version": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A", + "LatestVersionNumber" + ] + } + }, + "maxSize": "1", + "minSize": "1", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup", + "propagateAtLaunch": true + } + ], + "vpcZoneIdentifier": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_autoscaling.CfnAutoScalingGroup", + "version": "0.0.0" + } + }, + "DrainECSHook": { + "id": "DrainECSHook", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook", + "children": { + "Function": { + "id": "Function", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ], + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "ec2:DescribeHosts", + "ec2:DescribeInstanceAttribute", + "ec2:DescribeInstanceStatus", + "ec2:DescribeInstances" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "autoscaling:CompleteLifecycleAction", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":autoscaling:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":autoScalingGroup:*:autoScalingGroupName/", + { + "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + } + ] + ] + } + }, + { + "Action": [ + "ecs:DescribeContainerInstances", + "ecs:DescribeTasks", + "ecs:ListTasks", + "ecs:UpdateContainerInstancesState" + ], + "Condition": { + "ArnEquals": { + "ecs:cluster": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "ecs:ListContainerInstances", + "ecs:SubmitContainerStateChange", + "ecs:SubmitTaskStateChange" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsCluster97242B84", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRoleDefaultPolicyA45BF396", + "roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "import boto3, json, os, time\n\necs = boto3.client('ecs')\nautoscaling = boto3.client('autoscaling')\n\n\ndef lambda_handler(event, context):\n print(json.dumps(dict(event, ResponseURL='...')))\n cluster = os.environ['CLUSTER']\n snsTopicArn = event['Records'][0]['Sns']['TopicArn']\n lifecycle_event = json.loads(event['Records'][0]['Sns']['Message'])\n instance_id = lifecycle_event.get('EC2InstanceId')\n if not instance_id:\n print('Got event without EC2InstanceId: %s', json.dumps(dict(event, ResponseURL='...')))\n return\n\n instance_arn = container_instance_arn(cluster, instance_id)\n print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn))\n\n if not instance_arn:\n return\n\n task_arns = container_instance_task_arns(cluster, instance_arn)\n\n if task_arns:\n print('Instance ARN %s has task ARNs %s' % (instance_arn, ', '.join(task_arns)))\n\n while has_tasks(cluster, instance_arn, task_arns):\n time.sleep(10)\n\n try:\n print('Terminating instance %s' % instance_id)\n autoscaling.complete_lifecycle_action(\n LifecycleActionResult='CONTINUE',\n **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName'))\n except Exception as e:\n # Lifecycle action may have already completed.\n print(str(e))\n\n\ndef container_instance_arn(cluster, instance_id):\n \"\"\"Turn an instance ID into a container instance ARN.\"\"\"\n arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns']\n if not arns:\n return None\n return arns[0]\n\ndef container_instance_task_arns(cluster, instance_arn):\n \"\"\"Fetch tasks for a container instance ARN.\"\"\"\n arns = ecs.list_tasks(cluster=cluster, containerInstance=instance_arn)['taskArns']\n return arns\n\ndef has_tasks(cluster, instance_arn, task_arns):\n \"\"\"Return True if the instance is running tasks for the given cluster.\"\"\"\n instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances']\n if not instances:\n return False\n instance = instances[0]\n\n if instance['status'] == 'ACTIVE':\n # Start draining, then try again later\n set_container_instance_to_draining(cluster, instance_arn)\n return True\n\n task_count = None\n\n if task_arns:\n # Fetch details for tasks running on the container instance\n tasks = ecs.describe_tasks(cluster=cluster, tasks=task_arns)['tasks']\n if tasks:\n # Consider any non-stopped tasks as running\n task_count = sum(task['lastStatus'] != 'STOPPED' for task in tasks) + instance['pendingTasksCount']\n\n if not task_count:\n # Fallback to instance task counts if detailed task information is unavailable\n task_count = instance['runningTasksCount'] + instance['pendingTasksCount']\n\n print('Instance %s has %s tasks' % (instance_arn, task_count))\n\n return task_count > 0\n\ndef set_container_instance_to_draining(cluster, instance_arn):\n ecs.update_container_instances_state(\n cluster=cluster,\n containerInstances=[instance_arn], status='DRAINING')\n\n\ndef pick(dct, *keys):\n \"\"\"Pick a subset of a dict.\"\"\"\n return {k: v for k, v in dct.items() if k in keys}\n" + }, + "environment": { + "variables": { + "CLUSTER": { + "Ref": "EcsCluster97242B84" + } + } + }, + "handler": "index.lambda_handler", + "role": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", + "Arn" + ] + }, + "runtime": "python3.9", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ], + "timeout": 310 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "AllowInvoke:awscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D9": { + "id": "AllowInvoke:awscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D9", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/AllowInvoke:awscdkecsintegtestwithemptyplacementconstraintstackEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9E22C7D9", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", + "Arn" + ] + }, + "principal": "sns.amazonaws.com", + "sourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "0.0.0" + } + }, + "Topic": { + "id": "Topic", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/DrainECSHook/Function/Topic/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SNS::Subscription", + "aws:cdk:cloudformation:props": { + "endpoint": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", + "Arn" + ] + }, + "protocol": "lambda", + "topicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.CfnSubscription", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.Subscription", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "LifecycleHookDrainHook": { + "id": "LifecycleHookDrainHook", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook", + "children": { + "Topic": { + "id": "Topic", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Topic", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Topic/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SNS::Topic", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.CfnTopic", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sns.Topic", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "autoscaling.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "tags": [ + { + "key": "Name", + "value": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Role/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Effect": "Allow", + "Resource": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleDefaultPolicy75002F88", + "roles": [ + { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/EcsCluster/DefaultAutoScalingGroup/LifecycleHookDrainHook/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AutoScaling::LifecycleHook", + "aws:cdk:cloudformation:props": { + "autoScalingGroupName": { + "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB" + }, + "defaultResult": "CONTINUE", + "heartbeatTimeout": 300, + "lifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", + "notificationTargetArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "roleArn": { + "Fn::GetAtt": [ + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookRoleA38EC83B", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_autoscaling.CfnLifecycleHook", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_autoscaling.LifecycleHook", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_autoscaling.AutoScalingGroup", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Cluster", + "version": "0.0.0" + } + }, + "SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter": { + "id": "SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118": { + "id": "SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/SsmParameterValue:--aws--service--ecs--optimized-ami--amazon-linux-2--recommended--image_id:C96584B6-F00A-464E-AD19-53AFF4B05118", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "TaskDef": { + "id": "TaskDef", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef", + "children": { + "TaskRole": { + "id": "TaskRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/TaskRole", + "children": { + "ImportTaskRole": { + "id": "ImportTaskRole", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/TaskRole/ImportTaskRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/TaskRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::TaskDefinition", + "aws:cdk:cloudformation:props": { + "containerDefinitions": [ + { + "essential": true, + "image": "amazon/amazon-ecs-sample", + "memory": 256, + "name": "web" + } + ], + "family": "awscdkecsintegtestwithemptyplacementconstraintstackTaskDef2E5DA7C2", + "networkMode": "bridge", + "requiresCompatibilities": [ + "EC2" + ], + "taskRoleArn": { + "Fn::GetAtt": [ + "TaskDefTaskRole1EDB4A67", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnTaskDefinition", + "version": "0.0.0" + } + }, + "web": { + "id": "web", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/TaskDef/web", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.ContainerDefinition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Ec2TaskDefinition", + "version": "0.0.0" + } + }, + "Test_Stack": { + "id": "Test_Stack", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/Test_Stack", + "children": { + "Service": { + "id": "Service", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/Test_Stack/Service", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ECS::Service", + "aws:cdk:cloudformation:props": { + "cluster": { + "Ref": "EcsCluster97242B84" + }, + "deploymentConfiguration": { + "maximumPercent": 200, + "minimumHealthyPercent": 50 + }, + "enableEcsManagedTags": false, + "launchType": "EC2", + "placementConstraints": [], + "schedulingStrategy": "REPLICA", + "taskDefinition": { + "Ref": "TaskDef54694570" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.CfnService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ecs.Ec2Service", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "Integ": { + "id": "Integ", + "path": "Integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "Integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.ts new file mode 100644 index 0000000000000..a1b2c4468e928 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/ec2/integ.placement-constraint-default-empty.ts @@ -0,0 +1,36 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as ecs from 'aws-cdk-lib/aws-ecs'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); + +// WHEN +class EcsStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + const vpc = new ec2.Vpc(this, 'VPC', { restrictDefaultSecurityGroup: false }); + const cluster = new ecs.Cluster(this, 'EcsCluster', { vpc }); + cluster.addCapacity('DefaultAutoScalingGroup', { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.T2, ec2.InstanceSize.MICRO), + }); + const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef'); + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 256, + }); + new ecs.Ec2Service(this, 'Test_Stack', { + cluster, + taskDefinition, + placementConstraints: [], + }); + } +} + +// EXPECT +const stack = new EcsStack(app, 'aws-cdk-ecs-integ-test-with-empty-placement-constraint-stack'); +new IntegTest(app, 'Integ', { + testCases: [stack], +}); +app.synth(); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-service.ts b/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-service.ts index 7ea8451c5db17..09c731f40517f 100644 --- a/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-service.ts +++ b/packages/aws-cdk-lib/aws-ecs/lib/ec2/ec2-service.ts @@ -140,7 +140,7 @@ export class Ec2Service extends BaseService implements IEc2Service { return fromServiceAttributes(scope, id, attrs); } - private readonly constraints: CfnService.PlacementConstraintProperty[]; + private constraints?: CfnService.PlacementConstraintProperty[]; private readonly strategies: CfnService.PlacementStrategyProperty[]; private readonly daemon: boolean; @@ -179,12 +179,12 @@ export class Ec2Service extends BaseService implements IEc2Service { { cluster: props.cluster.clusterName, taskDefinition: props.deploymentController?.type === DeploymentControllerType.EXTERNAL ? undefined : props.taskDefinition.taskDefinitionArn, - placementConstraints: Lazy.any({ produce: () => this.constraints }, { omitEmptyArray: true }), + placementConstraints: Lazy.any({ produce: () => this.constraints }), placementStrategies: Lazy.any({ produce: () => this.strategies }, { omitEmptyArray: true }), schedulingStrategy: props.daemon ? 'DAEMON' : 'REPLICA', }, props.taskDefinition); - this.constraints = []; + this.constraints = undefined; this.strategies = []; this.daemon = props.daemon || false; @@ -210,7 +210,9 @@ export class Ec2Service extends BaseService implements IEc2Service { this.connections.addSecurityGroup(...securityGroupsInThisStack(this, props.cluster.connections.securityGroups)); } - this.addPlacementConstraints(...props.placementConstraints || []); + if (props.placementConstraints) { + this.addPlacementConstraints(...props.placementConstraints); + } this.addPlacementStrategies(...props.placementStrategies || []); this.node.addValidation({ @@ -239,6 +241,7 @@ export class Ec2Service extends BaseService implements IEc2Service { * [Amazon ECS Task Placement Constraints](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-placement-constraints.html). */ public addPlacementConstraints(...constraints: PlacementConstraint[]) { + this.constraints = []; for (const constraint of constraints) { this.constraints.push(...constraint.toJson()); } diff --git a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts index e50017ad80e91..879bd919a7849 100644 --- a/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts +++ b/packages/aws-cdk-lib/aws-ecs/test/ec2/ec2-service.test.ts @@ -1853,6 +1853,31 @@ describe('ec2 service', () => { }); }); + test('with empty [] placement constraints', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = new ec2.Vpc(stack, 'MyVpc', {}); + const cluster = new ecs.Cluster(stack, 'EcsCluster', { vpc }); + addDefaultCapacityProvider(cluster, stack, vpc); + const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef'); + + taskDefinition.addContainer('web', { + image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'), + memoryLimitMiB: 512, + }); + + new ecs.Ec2Service(stack, 'Ec2Service', { + cluster, + taskDefinition, + placementConstraints: [], + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ECS::Service', { + PlacementConstraints: Match.arrayEquals([]), + }); + }); + testDeprecated('with both propagateTags and propagateTaskTagsFrom defined', () => { // GIVEN const stack = new cdk.Stack();