From 30f0db6ad810f0e93187082bd50ddb46726d8f5f Mon Sep 17 00:00:00 2001 From: mazyu36 Date: Mon, 13 May 2024 04:09:26 +0900 Subject: [PATCH 1/2] feat(docdb): add copyTagsToSnapshot property to the DatabaseCluster Construct (#30120) ### Issue # (if applicable) Closes #30090 ### Reason for this change As described in the issue. ### Description of changes Add copyTagsToSnapshot property to the DatabaseCluster Construct. ### Description of how you validated changes Add both 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* --- ...efaultTestDeployAssert6A1BBA9D.assets.json | 19 + ...aultTestDeployAssert6A1BBA9D.template.json | 36 + .../__entrypoint__.js | 155 +++ .../index.js | 1 + .../aws-cdk-docdb-integ.assets.json | 32 + .../aws-cdk-docdb-integ.template.json | 805 +++++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 335 +++++ .../tree.json | 1204 +++++++++++++++++ .../integ.cluster-copy-tags-to-snapshot.ts | 38 + packages/aws-cdk-lib/aws-docdb/README.md | 1 + packages/aws-cdk-lib/aws-docdb/lib/cluster.ts | 9 + .../aws-docdb/test/cluster.test.ts | 42 + 14 files changed, 2690 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/__entrypoint__.js create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/index.js create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json new file mode 100644 index 0000000000000..1a49d2e15b492 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "ClusterTestDefaultTestDeployAssert6A1BBA9D.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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/ClusterTestDefaultTestDeployAssert6A1BBA9D.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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/__entrypoint__.js b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/__entrypoint__.js new file mode 100644 index 0000000000000..02033f55cf612 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/__entrypoint__.js @@ -0,0 +1,155 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.withRetries = exports.handler = exports.external = void 0; +const https = require("https"); +const url = require("url"); +// for unit tests +exports.external = { + sendHttpRequest: defaultSendHttpRequest, + log: defaultLog, + includeStackTraces: true, + userHandlerIndex: './index', +}; +const CREATE_FAILED_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::CREATE_FAILED'; +const MISSING_PHYSICAL_ID_MARKER = 'AWSCDK::CustomResourceProviderFramework::MISSING_PHYSICAL_ID'; +async function handler(event, context) { + const sanitizedEvent = { ...event, ResponseURL: '...' }; + exports.external.log(JSON.stringify(sanitizedEvent, undefined, 2)); + // ignore DELETE event when the physical resource ID is the marker that + // indicates that this DELETE is a subsequent DELETE to a failed CREATE + // operation. + if (event.RequestType === 'Delete' && event.PhysicalResourceId === CREATE_FAILED_PHYSICAL_ID_MARKER) { + exports.external.log('ignoring DELETE event caused by a failed CREATE event'); + await submitResponse('SUCCESS', event); + return; + } + try { + // invoke the user handler. this is intentionally inside the try-catch to + // ensure that if there is an error it's reported as a failure to + // cloudformation (otherwise cfn waits). + // eslint-disable-next-line @typescript-eslint/no-require-imports + const userHandler = require(exports.external.userHandlerIndex).handler; + const result = await userHandler(sanitizedEvent, context); + // validate user response and create the combined event + const responseEvent = renderResponse(event, result); + // submit to cfn as success + await submitResponse('SUCCESS', responseEvent); + } + catch (e) { + const resp = { + ...event, + Reason: exports.external.includeStackTraces ? e.stack : e.message, + }; + if (!resp.PhysicalResourceId) { + // special case: if CREATE fails, which usually implies, we usually don't + // have a physical resource id. in this case, the subsequent DELETE + // operation does not have any meaning, and will likely fail as well. to + // address this, we use a marker so the provider framework can simply + // ignore the subsequent DELETE. + if (event.RequestType === 'Create') { + exports.external.log('CREATE failed, responding with a marker physical resource id so that the subsequent DELETE will be ignored'); + resp.PhysicalResourceId = CREATE_FAILED_PHYSICAL_ID_MARKER; + } + else { + // otherwise, if PhysicalResourceId is not specified, something is + // terribly wrong because all other events should have an ID. + exports.external.log(`ERROR: Malformed event. "PhysicalResourceId" is required: ${JSON.stringify(event)}`); + } + } + // this is an actual error, fail the activity altogether and exist. + await submitResponse('FAILED', resp); + } +} +exports.handler = handler; +function renderResponse(cfnRequest, handlerResponse = {}) { + // if physical ID is not returned, we have some defaults for you based + // on the request type. + const physicalResourceId = handlerResponse.PhysicalResourceId ?? cfnRequest.PhysicalResourceId ?? cfnRequest.RequestId; + // if we are in DELETE and physical ID was changed, it's an error. + if (cfnRequest.RequestType === 'Delete' && physicalResourceId !== cfnRequest.PhysicalResourceId) { + throw new Error(`DELETE: cannot change the physical resource ID from "${cfnRequest.PhysicalResourceId}" to "${handlerResponse.PhysicalResourceId}" during deletion`); + } + // merge request event and result event (result prevails). + return { + ...cfnRequest, + ...handlerResponse, + PhysicalResourceId: physicalResourceId, + }; +} +async function submitResponse(status, event) { + const json = { + Status: status, + Reason: event.Reason ?? status, + StackId: event.StackId, + RequestId: event.RequestId, + PhysicalResourceId: event.PhysicalResourceId || MISSING_PHYSICAL_ID_MARKER, + LogicalResourceId: event.LogicalResourceId, + NoEcho: event.NoEcho, + Data: event.Data, + }; + const parsedUrl = url.parse(event.ResponseURL); + const loggingSafeUrl = `${parsedUrl.protocol}//${parsedUrl.hostname}/${parsedUrl.pathname}?***`; + exports.external.log('submit response to cloudformation', loggingSafeUrl, json); + const responseBody = JSON.stringify(json); + const req = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: 'PUT', + headers: { + 'content-type': '', + 'content-length': Buffer.byteLength(responseBody, 'utf8'), + }, + }; + const retryOptions = { + attempts: 5, + sleep: 1000, + }; + await withRetries(retryOptions, exports.external.sendHttpRequest)(req, responseBody); +} +async function defaultSendHttpRequest(options, requestBody) { + return new Promise((resolve, reject) => { + try { + const request = https.request(options, (response) => { + response.resume(); // Consume the response but don't care about it + if (!response.statusCode || response.statusCode >= 400) { + reject(new Error(`Unsuccessful HTTP response: ${response.statusCode}`)); + } + else { + resolve(); + } + }); + request.on('error', reject); + request.write(requestBody); + request.end(); + } + catch (e) { + reject(e); + } + }); +} +function defaultLog(fmt, ...params) { + // eslint-disable-next-line no-console + console.log(fmt, ...params); +} +function withRetries(options, fn) { + return async (...xs) => { + let attempts = options.attempts; + let ms = options.sleep; + while (true) { + try { + return await fn(...xs); + } + catch (e) { + if (attempts-- <= 0) { + throw e; + } + await sleep(Math.floor(Math.random() * ms)); + ms *= 2; + } + } + }; +} +exports.withRetries = withRetries; +async function sleep(ms) { + return new Promise((ok) => setTimeout(ok, ms)); +} diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/index.js b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/index.js new file mode 100644 index 0000000000000..013bcaffd8fe5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1/index.js @@ -0,0 +1 @@ +"use strict";var I=Object.create;var t=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var g=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty;var G=(r,e)=>{for(var o in e)t(r,o,{get:e[o],enumerable:!0})},n=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!l.call(r,s)&&s!==o&&t(r,s,{get:()=>e[s],enumerable:!(i=y(e,s))||i.enumerable});return r};var R=(r,e,o)=>(o=r!=null?I(g(r)):{},n(e||!r||!r.__esModule?t(o,"default",{value:r,enumerable:!0}):o,r)),S=r=>n(t({},"__esModule",{value:!0}),r);var k={};G(k,{handler:()=>f});module.exports=S(k);var a=R(require("@aws-sdk/client-ec2")),u=new a.EC2({});function c(r,e){return{GroupId:r,IpPermissions:[{UserIdGroupPairs:[{GroupId:r,UserId:e}],IpProtocol:"-1"}]}}function d(r){return{GroupId:r,IpPermissions:[{IpRanges:[{CidrIp:"0.0.0.0/0"}],IpProtocol:"-1"}]}}async function f(r){let e=r.ResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.Account;switch(r.RequestType){case"Create":return p(e,o);case"Update":return h(r);case"Delete":return m(e,o)}}async function h(r){let e=r.OldResourceProperties.DefaultSecurityGroupId,o=r.ResourceProperties.DefaultSecurityGroupId;e!==o&&(await m(e,r.ResourceProperties.Account),await p(o,r.ResourceProperties.Account))}async function p(r,e){try{await u.revokeSecurityGroupEgress(d(r))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}try{await u.revokeSecurityGroupIngress(c(r,e))}catch(o){if(o.name!=="InvalidPermission.NotFound")throw o}}async function m(r,e){await u.authorizeSecurityGroupIngress(c(r,e)),await u.authorizeSecurityGroupEgress(d(r))}0&&(module.exports={handler}); diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.assets.json new file mode 100644 index 0000000000000..646a8e5881500 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.assets.json @@ -0,0 +1,32 @@ +{ + "version": "36.0.0", + "files": { + "bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1": { + "source": { + "path": "asset.bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "11f0f2efc8a009948fd095d8a79efce755af0a7e5e0f3f92db99c9eaea4443e6": { + "source": { + "path": "aws-cdk-docdb-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "11f0f2efc8a009948fd095d8a79efce755af0a7e5e0f3f92db99c9eaea4443e6.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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.template.json new file mode 100644 index 0000000000000..f9158ba941630 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/aws-cdk-docdb-integ.template.json @@ -0,0 +1,805 @@ +{ + "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-docdb-integ/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-docdb-integ/VPC/PublicSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PublicSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PrivateSubnet1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PrivateSubnet2" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-docdb-integ/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-docdb-integ/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + }, + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "VPCRestrictDefaultSecurityGroupCustomResource59474679": { + "Type": "Custom::VpcRestrictDefaultSG", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E", + "Arn" + ] + }, + "DefaultSecurityGroupId": { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "DefaultSecurityGroup" + ] + }, + "Account": { + "Ref": "AWS::AccountId" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ec2:AuthorizeSecurityGroupIngress", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + "ec2:RevokeSecurityGroupEgress" + ], + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":security-group/", + { + "Fn::GetAtt": [ + "VPCB9E5F0B4", + "DefaultSecurityGroup" + ] + } + ] + ] + } + ] + } + ] + } + } + ] + } + }, + "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "bde7b5c89cb43285f884c94f0b9e17cdb0f5eb5345005114dd60342e0b8a85a1.zip" + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Description": "Lambda function for removing all inbound/outbound rules from the VPC default security group" + }, + "DependsOn": [ + "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + ] + }, + "DatabaseCopyTagsToSnapshotDisabledSubnets798D8ABD": { + "Type": "AWS::DocDB::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnets for DatabaseCopyTagsToSnapshotDisabled database", + "SubnetIds": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "DatabaseCopyTagsToSnapshotDisabledSecurityGroupDA546735": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "DocumentDB security group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": "\"@/", + "GenerateStringKey": "password", + "PasswordLength": 41, + "SecretStringTemplate": "{\"username\":\"docdb\"}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotDisabledSecretAttachment8F3CA1ED": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + "TargetId": { + "Ref": "DatabaseCopyTagsToSnapshotDisabled06721937" + }, + "TargetType": "AWS::DocDB::DBCluster" + } + }, + "DatabaseCopyTagsToSnapshotDisabled06721937": { + "Type": "AWS::DocDB::DBCluster", + "Properties": { + "CopyTagsToSnapshot": false, + "DBSubnetGroupName": { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSubnets798D8ABD" + }, + "EngineVersion": "3.6.0", + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + ":SecretString:password::}}" + ] + ] + }, + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + ":SecretString:username::}}" + ] + ] + }, + "StorageEncrypted": true, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseCopyTagsToSnapshotDisabledSecurityGroupDA546735", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotDisabledInstance1437B1FA7": { + "Type": "AWS::DocDB::DBInstance", + "Properties": { + "DBClusterIdentifier": { + "Ref": "DatabaseCopyTagsToSnapshotDisabled06721937" + }, + "DBInstanceClass": "db.r5.large" + }, + "DependsOn": [ + "VPCPrivateSubnet1DefaultRouteAE1D6490", + "VPCPrivateSubnet1RouteTableAssociation347902D1", + "VPCPrivateSubnet2DefaultRouteF4F5CFD2", + "VPCPrivateSubnet2RouteTableAssociation0C73D413" + ], + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotEnabledSubnets0F90F00C": { + "Type": "AWS::DocDB::DBSubnetGroup", + "Properties": { + "DBSubnetGroupDescription": "Subnets for DatabaseCopyTagsToSnapshotEnabled database", + "SubnetIds": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "DatabaseCopyTagsToSnapshotEnabledSecurityGroup227F101E": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "DocumentDB security group", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "GenerateSecretString": { + "ExcludeCharacters": "\"@/", + "GenerateStringKey": "password", + "PasswordLength": 41, + "SecretStringTemplate": "{\"username\":\"docdb\"}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotEnabledSecretAttachment760D889A": { + "Type": "AWS::SecretsManager::SecretTargetAttachment", + "Properties": { + "SecretId": { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + "TargetId": { + "Ref": "DatabaseCopyTagsToSnapshotEnabled84786E21" + }, + "TargetType": "AWS::DocDB::DBCluster" + } + }, + "DatabaseCopyTagsToSnapshotEnabled84786E21": { + "Type": "AWS::DocDB::DBCluster", + "Properties": { + "CopyTagsToSnapshot": true, + "DBSubnetGroupName": { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSubnets0F90F00C" + }, + "EngineVersion": "3.6.0", + "MasterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + ":SecretString:password::}}" + ] + ] + }, + "MasterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + ":SecretString:username::}}" + ] + ] + }, + "StorageEncrypted": true, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseCopyTagsToSnapshotEnabledSecurityGroup227F101E", + "GroupId" + ] + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "DatabaseCopyTagsToSnapshotEnabledInstance15DFB3914": { + "Type": "AWS::DocDB::DBInstance", + "Properties": { + "DBClusterIdentifier": { + "Ref": "DatabaseCopyTagsToSnapshotEnabled84786E21" + }, + "DBInstanceClass": "db.r5.large" + }, + "DependsOn": [ + "VPCPrivateSubnet1DefaultRouteAE1D6490", + "VPCPrivateSubnet1RouteTableAssociation347902D1", + "VPCPrivateSubnet2DefaultRouteF4F5CFD2", + "VPCPrivateSubnet2RouteTableAssociation0C73D413" + ], + "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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/integ.json new file mode 100644 index 0000000000000..b6cca8d910078 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "ClusterTest/DefaultTest": { + "stacks": [ + "aws-cdk-docdb-integ" + ], + "assertionStack": "ClusterTest/DefaultTest/DeployAssert", + "assertionStackName": "ClusterTestDefaultTestDeployAssert6A1BBA9D" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/manifest.json new file mode 100644 index 0000000000000..50c5dc28ab65b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/manifest.json @@ -0,0 +1,335 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-docdb-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-docdb-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-docdb-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-docdb-integ.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}/11f0f2efc8a009948fd095d8a79efce755af0a7e5e0f3f92db99c9eaea4443e6.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-docdb-integ.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-docdb-integ.assets" + ], + "metadata": { + "/aws-cdk-docdb-integ/VPC/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCB9E5F0B4" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1SubnetB4246D30" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableFEE4B781" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1RouteTableAssociation0B0896DC" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1DefaultRoute91CEF279" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1EIP6AD938E8" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet1/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet1NATGatewayE0556630" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2Subnet74179F39" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTable6F1A15F1" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2RouteTableAssociation5A808732" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2DefaultRouteB7481BBA" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/EIP": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2EIP4947BC00" + } + ], + "/aws-cdk-docdb-integ/VPC/PublicSubnet2/NATGateway": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPublicSubnet2NATGateway3C070193" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1Subnet8BCA10E0" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet1/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableBE8A6027" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1RouteTableAssociation347902D1" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet1/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet1DefaultRouteAE1D6490" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet2/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTable0A19E10E" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2RouteTableAssociation0C73D413" + } + ], + "/aws-cdk-docdb-integ/VPC/PrivateSubnet2/DefaultRoute": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCPrivateSubnet2DefaultRouteF4F5CFD2" + } + ], + "/aws-cdk-docdb-integ/VPC/IGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCIGWB7E252D3" + } + ], + "/aws-cdk-docdb-integ/VPC/VPCGW": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCVPCGW99B986DC" + } + ], + "/aws-cdk-docdb-integ/VPC/RestrictDefaultSecurityGroupCustomResource/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCRestrictDefaultSecurityGroupCustomResource59474679" + } + ], + "/aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0" + } + ], + "/aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Subnets": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabledSubnets798D8ABD" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabledSecurityGroupDA546735" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabledSecretAttachment8F3CA1ED" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabled06721937" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Instance1": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotDisabledInstance1437B1FA7" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Subnets": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabledSubnets0F90F00C" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/SecurityGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabledSecurityGroup227F101E" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret/Attachment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabledSecretAttachment760D889A" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabled84786E21" + } + ], + "/aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Instance1": [ + { + "type": "aws:cdk:logicalId", + "data": "DatabaseCopyTagsToSnapshotEnabledInstance15DFB3914" + } + ], + "/aws-cdk-docdb-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-docdb-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-docdb-integ" + }, + "ClusterTestDefaultTestDeployAssert6A1BBA9D.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ClusterTestDefaultTestDeployAssert6A1BBA9D.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ClusterTestDefaultTestDeployAssert6A1BBA9D": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ClusterTestDefaultTestDeployAssert6A1BBA9D.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": [ + "ClusterTestDefaultTestDeployAssert6A1BBA9D.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": [ + "ClusterTestDefaultTestDeployAssert6A1BBA9D.assets" + ], + "metadata": { + "/ClusterTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ClusterTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ClusterTest/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-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/tree.json new file mode 100644 index 0000000000000..99c59f4d5a561 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.js.snapshot/tree.json @@ -0,0 +1,1204 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-docdb-integ": { + "id": "aws-cdk-docdb-integ", + "path": "aws-cdk-docdb-integ", + "children": { + "VPC": { + "id": "VPC", + "path": "aws-cdk-docdb-integ/VPC", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/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-docdb-integ/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "PublicSubnet1": { + "id": "PublicSubnet1", + "path": "aws-cdk-docdb-integ/VPC/PublicSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-docdb-integ/VPC/PublicSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-docdb-integ/VPC/PublicSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PublicSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/PublicSubnet1/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PublicSubnet1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/PublicSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-docdb-integ/VPC/PublicSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-docdb-integ/VPC/PublicSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PublicSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/PublicSubnet2/EIP", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::EIP", + "aws:cdk:cloudformation:props": { + "domain": "vpc", + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PublicSubnet2" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnEIP", + "version": "0.0.0" + } + }, + "NATGateway": { + "id": "NATGateway", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/PrivateSubnet1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-docdb-integ/VPC/PrivateSubnet1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-docdb-integ/VPC/PrivateSubnet1/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PrivateSubnet1" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/PrivateSubnet2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "aws-cdk-docdb-integ/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-docdb-integ/VPC/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "aws-cdk-docdb-integ/VPC/PrivateSubnet2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "aws-cdk-docdb-integ/VPC/PrivateSubnet2/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC/PrivateSubnet2" + } + ], + "vpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "aws-cdk-docdb-integ/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-docdb-integ/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-docdb-integ/VPC/IGW", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::InternetGateway", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "aws-cdk-docdb-integ/VPC" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnInternetGateway", + "version": "0.0.0" + } + }, + "VPCGW": { + "id": "VPCGW", + "path": "aws-cdk-docdb-integ/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" + } + }, + "RestrictDefaultSecurityGroupCustomResource": { + "id": "RestrictDefaultSecurityGroupCustomResource", + "path": "aws-cdk-docdb-integ/VPC/RestrictDefaultSecurityGroupCustomResource", + "children": { + "Default": { + "id": "Default", + "path": "aws-cdk-docdb-integ/VPC/RestrictDefaultSecurityGroupCustomResource/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.Vpc", + "version": "0.0.0" + } + }, + "Custom::VpcRestrictDefaultSGCustomResourceProvider": { + "id": "Custom::VpcRestrictDefaultSGCustomResourceProvider", + "path": "aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider", + "children": { + "Staging": { + "id": "Staging", + "path": "aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider/Staging", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "aws-cdk-docdb-integ/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.CustomResourceProviderBase", + "version": "0.0.0" + } + }, + "DatabaseCopyTagsToSnapshotDisabled": { + "id": "DatabaseCopyTagsToSnapshotDisabled", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled", + "children": { + "Subnets": { + "id": "Subnets", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Subnets", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBSubnetGroup", + "aws:cdk:cloudformation:props": { + "dbSubnetGroupDescription": "Subnets for DatabaseCopyTagsToSnapshotDisabled database", + "subnetIds": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBSubnetGroup", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "DocumentDB security group", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "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" + } + }, + "Secret": { + "id": "Secret", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "generateSecretString": { + "passwordLength": 41, + "secretStringTemplate": "{\"username\":\"docdb\"}", + "generateStringKey": "password", + "excludeCharacters": "\"@/" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + "targetId": { + "Ref": "DatabaseCopyTagsToSnapshotDisabled06721937" + }, + "targetType": "AWS::DocDB::DBCluster" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.SecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.DatabaseSecret", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBCluster", + "aws:cdk:cloudformation:props": { + "copyTagsToSnapshot": false, + "dbSubnetGroupName": { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSubnets798D8ABD" + }, + "engineVersion": "3.6.0", + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotDisabledSecret84FDE733" + }, + ":SecretString:password::}}" + ] + ] + }, + "storageEncrypted": true, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseCopyTagsToSnapshotDisabledSecurityGroupDA546735", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBCluster", + "version": "0.0.0" + } + }, + "Instance1": { + "id": "Instance1", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotDisabled/Instance1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBInstance", + "aws:cdk:cloudformation:props": { + "dbClusterIdentifier": { + "Ref": "DatabaseCopyTagsToSnapshotDisabled06721937" + }, + "dbInstanceClass": "db.r5.large" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.DatabaseCluster", + "version": "0.0.0" + } + }, + "DatabaseCopyTagsToSnapshotEnabled": { + "id": "DatabaseCopyTagsToSnapshotEnabled", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled", + "children": { + "Subnets": { + "id": "Subnets", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Subnets", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBSubnetGroup", + "aws:cdk:cloudformation:props": { + "dbSubnetGroupDescription": "Subnets for DatabaseCopyTagsToSnapshotEnabled database", + "subnetIds": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBSubnetGroup", + "version": "0.0.0" + } + }, + "SecurityGroup": { + "id": "SecurityGroup", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/SecurityGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/SecurityGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SecurityGroup", + "aws:cdk:cloudformation:props": { + "groupDescription": "DocumentDB security group", + "securityGroupEgress": [ + { + "cidrIp": "0.0.0.0/0", + "description": "Allow all outbound traffic by default", + "ipProtocol": "-1" + } + ], + "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" + } + }, + "Secret": { + "id": "Secret", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "description": { + "Fn::Join": [ + "", + [ + "Generated by the CDK for stack: ", + { + "Ref": "AWS::StackName" + } + ] + ] + }, + "generateSecretString": { + "passwordLength": 41, + "secretStringTemplate": "{\"username\":\"docdb\"}", + "generateStringKey": "password", + "excludeCharacters": "\"@/" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + }, + "Attachment": { + "id": "Attachment", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret/Attachment", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Secret/Attachment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::SecretTargetAttachment", + "aws:cdk:cloudformation:props": { + "secretId": { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + "targetId": { + "Ref": "DatabaseCopyTagsToSnapshotEnabled84786E21" + }, + "targetType": "AWS::DocDB::DBCluster" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.SecretTargetAttachment", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.DatabaseSecret", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBCluster", + "aws:cdk:cloudformation:props": { + "copyTagsToSnapshot": true, + "dbSubnetGroupName": { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSubnets0F90F00C" + }, + "engineVersion": "3.6.0", + "masterUsername": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + ":SecretString:username::}}" + ] + ] + }, + "masterUserPassword": { + "Fn::Join": [ + "", + [ + "{{resolve:secretsmanager:", + { + "Ref": "DatabaseCopyTagsToSnapshotEnabledSecret3AD5FDE3" + }, + ":SecretString:password::}}" + ] + ] + }, + "storageEncrypted": true, + "vpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "DatabaseCopyTagsToSnapshotEnabledSecurityGroup227F101E", + "GroupId" + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBCluster", + "version": "0.0.0" + } + }, + "Instance1": { + "id": "Instance1", + "path": "aws-cdk-docdb-integ/DatabaseCopyTagsToSnapshotEnabled/Instance1", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DocDB::DBInstance", + "aws:cdk:cloudformation:props": { + "dbClusterIdentifier": { + "Ref": "DatabaseCopyTagsToSnapshotEnabled84786E21" + }, + "dbInstanceClass": "db.r5.large" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.CfnDBInstance", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_docdb.DatabaseCluster", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-docdb-integ/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-docdb-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "ClusterTest": { + "id": "ClusterTest", + "path": "ClusterTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "ClusterTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "ClusterTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "ClusterTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ClusterTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ClusterTest/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-docdb/test/integ.cluster-copy-tags-to-snapshot.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.ts new file mode 100644 index 0000000000000..b1ab797450ad9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-copy-tags-to-snapshot.ts @@ -0,0 +1,38 @@ +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { DatabaseCluster } from 'aws-cdk-lib/aws-docdb'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'aws-cdk-docdb-integ'); + +const vpc = new ec2.Vpc(stack, 'VPC'); + +new DatabaseCluster(stack, 'DatabaseCopyTagsToSnapshotDisabled', { + engineVersion: '3.6.0', + masterUser: { + username: 'docdb', + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE), + vpc, + removalPolicy: cdk.RemovalPolicy.DESTROY, + copyTagsToSnapshot: false, +}); + +new DatabaseCluster(stack, 'DatabaseCopyTagsToSnapshotEnabled', { + engineVersion: '3.6.0', + masterUser: { + username: 'docdb', + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE), + vpc, + removalPolicy: cdk.RemovalPolicy.DESTROY, + copyTagsToSnapshot: true, +}); + +new integ.IntegTest(app, 'ClusterTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-docdb/README.md b/packages/aws-cdk-lib/aws-docdb/README.md index 539b4a9908eb8..ecde57b120695 100644 --- a/packages/aws-cdk-lib/aws-docdb/README.md +++ b/packages/aws-cdk-lib/aws-docdb/README.md @@ -20,6 +20,7 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', { subnetType: ec2.SubnetType.PUBLIC, }, vpc, + copyTagsToSnapshot: true // whether to save the cluster tags when creating the snapshot. }); ``` diff --git a/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts b/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts index 696e2b85aa5ba..f6485143614da 100644 --- a/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts @@ -229,6 +229,13 @@ export interface DatabaseClusterProps { * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html */ readonly securityGroupRemovalPolicy?: RemovalPolicy; + + /** + * Whether to copy tags to the snapshot when a snapshot is created. + * + * @default - false + */ + readonly copyTagsToSnapshot?: boolean; } /** @@ -519,6 +526,8 @@ export class DatabaseCluster extends DatabaseClusterBase { // Encryption kmsKeyId: props.kmsKey?.keyArn, storageEncrypted, + // Tags + copyTagsToSnapshot: props.copyTagsToSnapshot, }); this.cluster.applyRemovalPolicy(props.removalPolicy, { diff --git a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts index 07a597f399fb6..3c5139140b541 100644 --- a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts @@ -1138,6 +1138,48 @@ describe('DatabaseCluster', () => { }); }).toThrow(/AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy/); }); + + test('cluster with copyTagsToSnapshot default', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { + CopyTagsToSnapshot: Match.absent(), + }); + }); + + test.each([false, true])('cluster with copyTagsToSnapshot set', (value) => { + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + copyTagsToSnapshot: value, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DocDB::DBCluster', { + CopyTagsToSnapshot: value, + }); + }); }); function testStack() { From 5be88a3055fe1e6b55884847d1b8a75b03341b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brice=20Pell=C3=A9?= Date: Sun, 12 May 2024 20:14:12 -0700 Subject: [PATCH 2/2] feat(events-targets): add support for AppSync as an EventBridge rule target (#29584) ### Reason for this change Introduces support to configure AppSync GraphQLAPI as an EventBridge target. - announcement: https://aws.amazon.com/about-aws/whats-new/2024/01/amazon-eventbridge-appsync-target-buses/ - documentation: https://docs.aws.amazon.com/eventbridge/latest/userguide/target-appsync.html ### Description of changes - Expose `GraphQLEndpointArn` attribute in L2 GraphQLAPI construct - Implement `events.IRuleTarget` for `AppSync` ```ts rule.addTarget(new targets.AppSync(api, { graphQLOperation: 'mutation Publish($message: String!){ publish(message: $message) { message } }', variables: events.RuleTargetInput.fromObject({ message: 'hello world', }), deadLetterQueue: queue, })); ``` ### Description of how you validated changes unit test and integration tests ### Issue Solves #29884 ### 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* --- .../test/appsync/appsync.test.graphql | 13 + ...s-cdk-aws-appsync-target-integ.assets.json | 19 + ...cdk-aws-appsync-target-integ.template.json | 238 +++++++++ ...efaultTestDeployAssert7E101DC4.assets.json | 19 + ...aultTestDeployAssert7E101DC4.template.json | 36 ++ .../integ.appsync-events.js.snapshot/cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 161 ++++++ .../tree.json | 464 ++++++++++++++++++ .../test/appsync/integ.appsync-events.ts | 50 ++ packages/aws-cdk-lib/aws-appsync/README.md | 43 +- .../aws-appsync/lib/graphqlapi-base.ts | 71 +++ .../aws-cdk-lib/aws-appsync/lib/graphqlapi.ts | 84 ++-- .../aws-cdk-lib/aws-events-targets/README.md | 61 ++- .../aws-events-targets/lib/appsync.ts | 84 ++++ .../aws-events-targets/lib/index.ts | 1 + .../test/appsync/appsync.test.graphql | 9 + .../test/appsync/appsync.test.ts | 395 +++++++++++++++ packages/aws-cdk-lib/aws-events/lib/rule.ts | 1 + packages/aws-cdk-lib/aws-events/lib/target.ts | 6 + 20 files changed, 1715 insertions(+), 53 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/appsync.test.graphql create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.ts create mode 100644 packages/aws-cdk-lib/aws-events-targets/lib/appsync.ts create mode 100644 packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.graphql create mode 100644 packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/appsync.test.graphql b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/appsync.test.graphql new file mode 100644 index 0000000000000..9a2546e396abf --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/appsync.test.graphql @@ -0,0 +1,13 @@ +type Event { + message: String +} +type Query { + getTests: [Event]! +} +type Mutation { + publish(message: String!): Event +} + +type Subscription { + onPublish: Event @aws_subscribe(mutations: ["publish"]) +} diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.assets.json new file mode 100644 index 0000000000000..399b85c136688 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "fa005296bfe6cbad01df3cb800a324adbe7bcdb6273343c59488a74b1a9834a1": { + "source": { + "path": "aws-cdk-aws-appsync-target-integ.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "fa005296bfe6cbad01df3cb800a324adbe7bcdb6273343c59488a74b1a9834a1.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-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.template.json new file mode 100644 index 0000000000000..fef7e923e6aba --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/aws-cdk-aws-appsync-target-integ.template.json @@ -0,0 +1,238 @@ +{ + "Resources": { + "baseApiCDA4D43A": { + "Type": "AWS::AppSync::GraphQLApi", + "Properties": { + "AuthenticationType": "AWS_IAM", + "Name": "aws-cdk-aws-appsync-target-integ-api" + } + }, + "baseApiSchemaB12C7BB0": { + "Type": "AWS::AppSync::GraphQLSchema", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "Definition": "type Event {\n message: String\n}\ntype Query {\n getTests: [Event]!\n}\ntype Mutation {\n publish(message: String!): Event\n}\n\ntype Subscription {\n onPublish: Event @aws_subscribe(mutations: [\"publish\"])\n}\n" + } + }, + "baseApinone7DDDEE3D": { + "Type": "AWS::AppSync::DataSource", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "Name": "none", + "Type": "NONE" + } + }, + "baseApipublisherC3F47EA2": { + "Type": "AWS::AppSync::Resolver", + "Properties": { + "ApiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "Code": "export const request = (ctx) => ({payload: null})\nexport const response = (ctx) => ctx.args.message", + "DataSourceName": "none", + "FieldName": "publish", + "Kind": "UNIT", + "Runtime": { + "Name": "APPSYNC_JS", + "RuntimeVersion": "1.0.0" + }, + "TypeName": "Mutation" + }, + "DependsOn": [ + "baseApinone7DDDEE3D", + "baseApiSchemaB12C7BB0" + ] + }, + "baseApiEventsRoleAC472BD7": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "baseApiEventsRoleDefaultPolicy94199357": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "/types/Mutation/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "baseApiEventsRoleDefaultPolicy94199357", + "Roles": [ + { + "Ref": "baseApiEventsRoleAC472BD7" + } + ] + } + }, + "Queue4A7E3555": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "QueuePolicy25439813": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "TimerBF6F831F", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + }, + "Sid": "AllowEventRuleawscdkawsappsynctargetintegTimer2A2187F8" + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "Queue4A7E3555" + } + ] + } + }, + "TimerBF6F831F": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "AppSyncParameters": { + "GraphQLOperation": "mutation Publish($message: String!){ publish(message: $message) { message } }" + }, + "Arn": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "GraphQLEndpointArn" + ] + }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + }, + "Id": "Target0", + "Input": "{\"message\":\"hello world\"}", + "RoleArn": { + "Fn::GetAtt": [ + "baseApiEventsRoleAC472BD7", + "Arn" + ] + } + } + ] + } + } + }, + "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-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.assets.json new file mode 100644 index 0000000000000..7c6f6b0aa6d9f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "awsappsyncintegDefaultTestDeployAssert7E101DC4.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-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/awsappsyncintegDefaultTestDeployAssert7E101DC4.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-events-targets/test/appsync/integ.appsync-events.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.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-events-targets/test/appsync/integ.appsync-events.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/integ.json new file mode 100644 index 0000000000000..3b6cc9115d7e0 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "aws-appsync-integ/DefaultTest": { + "stacks": [ + "aws-cdk-aws-appsync-target-integ" + ], + "assertionStack": "aws-appsync-integ/DefaultTest/DeployAssert", + "assertionStackName": "awsappsyncintegDefaultTestDeployAssert7E101DC4" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/manifest.json new file mode 100644 index 0000000000000..5ec8f4c577f0c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/manifest.json @@ -0,0 +1,161 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-cdk-aws-appsync-target-integ.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-aws-appsync-target-integ.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-aws-appsync-target-integ": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-aws-appsync-target-integ.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}/fa005296bfe6cbad01df3cb800a324adbe7bcdb6273343c59488a74b1a9834a1.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-aws-appsync-target-integ.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-aws-appsync-target-integ.assets" + ], + "metadata": { + "/aws-cdk-aws-appsync-target-integ/baseApi/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApiCDA4D43A" + } + ], + "/aws-cdk-aws-appsync-target-integ/baseApi/Schema": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApiSchemaB12C7BB0" + } + ], + "/aws-cdk-aws-appsync-target-integ/baseApi/none/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApinone7DDDEE3D" + } + ], + "/aws-cdk-aws-appsync-target-integ/baseApi/publisher/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApipublisherC3F47EA2" + } + ], + "/aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApiEventsRoleAC472BD7" + } + ], + "/aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "baseApiEventsRoleDefaultPolicy94199357" + } + ], + "/aws-cdk-aws-appsync-target-integ/Queue/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Queue4A7E3555" + } + ], + "/aws-cdk-aws-appsync-target-integ/Queue/Policy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "QueuePolicy25439813" + } + ], + "/aws-cdk-aws-appsync-target-integ/Timer/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TimerBF6F831F" + } + ], + "/aws-cdk-aws-appsync-target-integ/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-aws-appsync-target-integ/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-aws-appsync-target-integ" + }, + "awsappsyncintegDefaultTestDeployAssert7E101DC4.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "awsappsyncintegDefaultTestDeployAssert7E101DC4.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "awsappsyncintegDefaultTestDeployAssert7E101DC4": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "awsappsyncintegDefaultTestDeployAssert7E101DC4.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": [ + "awsappsyncintegDefaultTestDeployAssert7E101DC4.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": [ + "awsappsyncintegDefaultTestDeployAssert7E101DC4.assets" + ], + "metadata": { + "/aws-appsync-integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appsync-integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appsync-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-events-targets/test/appsync/integ.appsync-events.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/tree.json new file mode 100644 index 0000000000000..e95bb0b5f8d49 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.js.snapshot/tree.json @@ -0,0 +1,464 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-aws-appsync-target-integ": { + "id": "aws-cdk-aws-appsync-target-integ", + "path": "aws-cdk-aws-appsync-target-integ", + "children": { + "baseApi": { + "id": "baseApi", + "path": "aws-cdk-aws-appsync-target-integ/baseApi", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLApi", + "aws:cdk:cloudformation:props": { + "authenticationType": "AWS_IAM", + "name": "aws-cdk-aws-appsync-target-integ-api" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnGraphQLApi", + "version": "0.0.0" + } + }, + "Schema": { + "id": "Schema", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/Schema", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLSchema", + "aws:cdk:cloudformation:props": { + "apiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "definition": "type Event {\n message: String\n}\ntype Query {\n getTests: [Event]!\n}\ntype Mutation {\n publish(message: String!): Event\n}\n\ntype Subscription {\n onPublish: Event @aws_subscribe(mutations: [\"publish\"])\n}\n" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnGraphQLSchema", + "version": "0.0.0" + } + }, + "LogGroup": { + "id": "LogGroup", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/LogGroup", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "none": { + "id": "none", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/none", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/none/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::DataSource", + "aws:cdk:cloudformation:props": { + "apiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "name": "none", + "type": "NONE" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnDataSource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.NoneDataSource", + "version": "0.0.0" + } + }, + "publisher": { + "id": "publisher", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/publisher", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/publisher/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppSync::Resolver", + "aws:cdk:cloudformation:props": { + "apiId": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "code": "export const request = (ctx) => ({payload: null})\nexport const response = (ctx) => ctx.args.message", + "dataSourceName": "none", + "fieldName": "publish", + "kind": "UNIT", + "runtime": { + "name": "APPSYNC_JS", + "runtimeVersion": "1.0.0" + }, + "typeName": "Mutation" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.CfnResolver", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.Resolver", + "version": "0.0.0" + } + }, + "EventsRole": { + "id": "EventsRole", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/EventsRole", + "children": { + "ImportEventsRole": { + "id": "ImportEventsRole", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/ImportEventsRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/baseApi/EventsRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "appsync:GraphQL", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":appsync:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":apis/", + { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "ApiId" + ] + }, + "/types/Mutation/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "baseApiEventsRoleDefaultPolicy94199357", + "roles": [ + { + "Ref": "baseApiEventsRoleAC472BD7" + } + ] + } + }, + "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" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_appsync.GraphqlApi", + "version": "0.0.0" + } + }, + "Queue": { + "id": "Queue", + "path": "aws-cdk-aws-appsync-target-integ/Queue", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/Queue/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::Queue", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueue", + "version": "0.0.0" + } + }, + "Policy": { + "id": "Policy", + "path": "aws-cdk-aws-appsync-target-integ/Queue/Policy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/Queue/Policy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SQS::QueuePolicy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "TimerBF6F831F", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + }, + "Sid": "AllowEventRuleawscdkawsappsynctargetintegTimer2A2187F8" + } + ], + "Version": "2012-10-17" + }, + "queues": [ + { + "Ref": "Queue4A7E3555" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.CfnQueuePolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.QueuePolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "Timer": { + "id": "Timer", + "path": "aws-cdk-aws-appsync-target-integ/Timer", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-appsync-target-integ/Timer/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Events::Rule", + "aws:cdk:cloudformation:props": { + "scheduleExpression": "rate(1 minute)", + "state": "ENABLED", + "targets": [ + { + "id": "Target0", + "arn": { + "Fn::GetAtt": [ + "baseApiCDA4D43A", + "GraphQLEndpointArn" + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "baseApiEventsRoleAC472BD7", + "Arn" + ] + }, + "deadLetterConfig": { + "arn": { + "Fn::GetAtt": [ + "Queue4A7E3555", + "Arn" + ] + } + }, + "appSyncParameters": { + "graphQlOperation": "mutation Publish($message: String!){ publish(message: $message) { message } }" + }, + "input": "{\"message\":\"hello world\"}" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_events.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_events.Rule", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-aws-appsync-target-integ/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-aws-appsync-target-integ/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "aws-appsync-integ": { + "id": "aws-appsync-integ", + "path": "aws-appsync-integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "aws-appsync-integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "aws-appsync-integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "aws-appsync-integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appsync-integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appsync-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-events-targets/test/appsync/integ.appsync-events.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.ts new file mode 100644 index 0000000000000..82622e771cc43 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/appsync/integ.appsync-events.ts @@ -0,0 +1,50 @@ +import * as events from 'aws-cdk-lib/aws-events'; +import * as appsync from 'aws-cdk-lib/aws-appsync'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as cdk from 'aws-cdk-lib'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; +import * as path from 'path'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); + +class AwsAppSyncEvent extends cdk.Stack { + + constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const api = new appsync.GraphqlApi(this, 'baseApi', { + name: 'aws-cdk-aws-appsync-target-integ-api', + definition: appsync.Definition.fromFile(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { defaultAuthorization: { authorizationType: appsync.AuthorizationType.IAM } }, + }); + const none = api.addNoneDataSource('none'); + none.createResolver('publisher', { + typeName: 'Mutation', + fieldName: 'publish', + code: appsync.AssetCode.fromInline(` +export const request = (ctx) => ({payload: null}) +export const response = (ctx) => ctx.args.message +`.trim()), + runtime: appsync.FunctionRuntime.JS_1_0_0, + }); + + const graphQLOperation = 'mutation Publish($message: String!){ publish(message: $message) { message } }'; + const queue = new sqs.Queue(this, 'Queue'); + + const timer = new events.Rule(this, 'Timer', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + timer.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: 'hello world', + }), + deadLetterQueue: queue, + })); + + } +} + +const stack = new AwsAppSyncEvent(app, 'aws-cdk-aws-appsync-target-integ'); +new IntegTest(app, 'aws-appsync-integ', { testCases: [stack] }); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-appsync/README.md b/packages/aws-cdk-lib/aws-appsync/README.md index dfb8fca7ce144..a239a481e057a 100644 --- a/packages/aws-cdk-lib/aws-appsync/README.md +++ b/packages/aws-cdk-lib/aws-appsync/README.md @@ -1,6 +1,5 @@ # AWS AppSync Construct Library - The `aws-cdk-lib/aws-appsync` package contains constructs for building flexible APIs that use GraphQL. @@ -86,8 +85,6 @@ demoDS.createResolver('QueryGetDemosConsistentResolver', { }); ``` - - ### Aurora Serverless AppSync provides a data source for executing SQL commands against Amazon Aurora @@ -303,6 +300,7 @@ httpDs.createResolver('MutationCallStepFunctionResolver', { ``` ### EventBridge + Integrating AppSync with EventBridge enables developers to use EventBridge rules to route commands for GraphQL mutations that need to perform any one of a variety of asynchronous tasks. More broadly, it enables teams to expose an event bus as a part of a GraphQL schema. @@ -437,6 +435,7 @@ ds.createResolver('QueryGetTestsResolver', { ``` ## Merged APIs + AppSync supports [Merged APIs](https://docs.aws.amazon.com/appsync/latest/devguide/merged-api.html) which can be used to merge multiple source APIs into a single API. ```ts @@ -602,9 +601,9 @@ sources and resolvers, an `apiId` is sufficient. ## Private APIs -By default all AppSync GraphQL APIs are public and can be accessed from the internet. -For customers that want to limit access to be from their VPC, the optional API `visibility` property can be set to `Visibility.PRIVATE` -at creation time. To explicitly create a public API, the `visibility` property should be set to `Visibility.GLOBAL`. +By default all AppSync GraphQL APIs are public and can be accessed from the internet. +For customers that want to limit access to be from their VPC, the optional API `visibility` property can be set to `Visibility.PRIVATE` +at creation time. To explicitly create a public API, the `visibility` property should be set to `Visibility.GLOBAL`. If visibility is not set, the service will default to `GLOBAL`. CDK stack file `app-stack.ts`: @@ -617,8 +616,8 @@ const api = new appsync.GraphqlApi(this, 'api', { }); ``` -See [documentation](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html) -for more details about Private APIs +See [documentation](https://docs.aws.amazon.com/appsync/latest/devguide/using-private-apis.html) +for more details about Private APIs ## Authorization @@ -842,8 +841,8 @@ const api = new appsync.GraphqlApi(this, 'api', { ## Resolver Count Limits -You can control how many resolvers each query can process. -By default, each query can process up to 10000 resolvers. +You can control how many resolvers each query can process. +By default, each query can process up to 10000 resolvers. By setting a limit AppSync will not handle any resolvers past a certain number limit. ```ts @@ -870,3 +869,27 @@ const api = new appsync.GraphqlApi(this, 'api', { api.addEnvironmentVariable('EnvKey2', 'non-empty-2'); ``` + +## Configure an EventBridge target that invokes an AppSync GraphQL API + +Configuring the target relies on the `graphQLEndpointArn` property. + +Use the `AppSync` event target to trigger an AppSync GraphQL API. You need to +create an `AppSync.GraphqlApi` configured with `AWS_IAM` authorization mode. + +The code snippet below creates a AppSync GraphQL API target that is invoked, calling the `publish` mutation. + +```ts +import * as events from 'aws-cdk-lib/aws-events'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; + +declare const rule: events.Rule; +declare const api: appsync.GraphqlApi; + +rule.addTarget(new targets.AppSync(api, { + graphQLOperation: 'mutation Publish($message: String!){ publish(message: $message) { message } }', + variables: events.RuleTargetInput.fromObject({ + message: 'hello world', + }), +})); +``` diff --git a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts index 7f91d270c93ec..7b87718a52e5d 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi-base.ts @@ -112,6 +112,47 @@ export class IamResource { } } +/** + * Visibility type for a GraphQL API + */ +export enum Visibility { + + /** + * Public, open to the internet + */ + GLOBAL = 'GLOBAL', + /** + * Only accessible through a VPC + */ + PRIVATE = 'PRIVATE', +} + +/** + * enum with all possible values for AppSync authorization type + */ +export enum AuthorizationType { + /** + * API Key authorization type + */ + API_KEY = 'API_KEY', + /** + * AWS IAM authorization type. Can be used with Cognito Identity Pool federated credentials + */ + IAM = 'AWS_IAM', + /** + * Cognito User Pool authorization type + */ + USER_POOL = 'AMAZON_COGNITO_USER_POOLS', + /** + * OpenID Connect authorization type + */ + OIDC = 'OPENID_CONNECT', + /** + * Lambda authorization type + */ + LAMBDA = 'AWS_LAMBDA', +} + /** * Interface for GraphQL */ @@ -132,6 +173,21 @@ export interface IGraphqlApi extends IResource { */ readonly arn: string; + /** + * The GraphQL endpoint ARN + */ + readonly graphQLEndpointArn: string; + + /** + * the visibility of the API + */ + readonly visibility: Visibility; + + /** + * The Authorization Types for this GraphQL Api + */ + readonly modes: AuthorizationType[]; + /** * add a new dummy data source to this API. Useful for pipeline resolvers * and for backend changes that don't require a data source. @@ -290,11 +346,26 @@ export abstract class GraphqlApiBase extends Resource implements IGraphqlApi { */ public abstract readonly apiId: string; + /** + * The GraphQL endpoint ARN + */ + public abstract readonly graphQLEndpointArn: string; + + /** + * The visibility of the API + */ + public abstract readonly visibility: Visibility; + /** * the ARN of the API */ public abstract readonly arn: string; + /** + * The Authorization Types for this GraphQL Api + */ + public abstract readonly modes: AuthorizationType[]; + /** * add a new dummy data source to this API. Useful for pipeline resolvers * and for backend changes that don't require a data source. diff --git a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts index a301d90328d1d..4332257d5484c 100644 --- a/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts +++ b/packages/aws-cdk-lib/aws-appsync/lib/graphqlapi.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema, CfnDomainName, CfnDomainNameApiAssociation, CfnSourceApiAssociation } from './appsync.generated'; -import { IGraphqlApi, GraphqlApiBase } from './graphqlapi-base'; +import { IGraphqlApi, GraphqlApiBase, Visibility, AuthorizationType } from './graphqlapi-base'; import { ISchema, SchemaFile } from './schema'; import { MergeType, addSourceApiAutoMergePermission, addSourceGraphQLPermission } from './source-api-association'; import { ICertificate } from '../../aws-certificatemanager'; @@ -11,32 +11,6 @@ import { ILogGroup, LogGroup, LogRetention, RetentionDays } from '../../aws-logs import { CfnResource, Duration, Expiration, FeatureFlags, IResolvable, Lazy, Stack, Token } from '../../core'; import * as cxapi from '../../cx-api'; -/** - * enum with all possible values for AppSync authorization type - */ -export enum AuthorizationType { - /** - * API Key authorization type - */ - API_KEY = 'API_KEY', - /** - * AWS IAM authorization type. Can be used with Cognito Identity Pool federated credentials - */ - IAM = 'AWS_IAM', - /** - * Cognito User Pool authorization type - */ - USER_POOL = 'AMAZON_COGNITO_USER_POOLS', - /** - * OpenID Connect authorization type - */ - OIDC = 'OPENID_CONNECT', - /** - * Lambda authorization type - */ - LAMBDA = 'AWS_LAMBDA', -} - /** * Interface to specify default or additional authorization(s) */ @@ -260,21 +234,6 @@ export interface LogConfig { readonly retention?: RetentionDays; } -/** - * Visibility type for a GraphQL API - */ -export enum Visibility { - - /** - * Public, open to the internet - */ - GLOBAL = 'GLOBAL', - /** - * Only accessible through a VPC - */ - PRIVATE = 'PRIVATE', -} - /** * Domain name configuration for AppSync */ @@ -489,6 +448,27 @@ export interface GraphqlApiAttributes { * @default - autogenerated arn */ readonly graphqlApiArn?: string; + + /** + * The GraphQl endpoint arn for the GraphQL API + * + * @default - none, required to construct event rules from imported APIs + */ + readonly graphQLEndpointArn?: string; + + /** + * The GraphQl API visibility + * + * @default - GLOBAL + */ + readonly visibility?: Visibility; + + /** + * The Authorization Types for this GraphQL Api + * + * @default - none, required to construct event rules from imported APIs + */ + readonly modes?: AuthorizationType[]; } /** @@ -527,6 +507,13 @@ export class GraphqlApi extends GraphqlApiBase { class Import extends GraphqlApiBase { public readonly apiId = attrs.graphqlApiId; public readonly arn = arn; + + // the GraphQL endpoint ARN is not required to identify an AppSync GraphQL API + // this value is only needed to construct event rules. + public readonly graphQLEndpointArn = attrs.graphQLEndpointArn ?? ''; + public readonly visibility = attrs.visibility ?? Visibility.GLOBAL; + public readonly modes = attrs.modes ?? [] + constructor(s: Construct, i: string) { super(s, i); } @@ -545,6 +532,11 @@ export class GraphqlApi extends GraphqlApiBase { */ public readonly arn: string; + /** + * The GraphQL endpoint ARN + */ + public readonly graphQLEndpointArn: string; + /** * the URL of the endpoint created by AppSync * @@ -557,6 +549,11 @@ export class GraphqlApi extends GraphqlApiBase { */ public readonly name: string; + /** + * the visibility of the API + */ + public readonly visibility: Visibility; + /** * the schema attached to this api (only available for GraphQL APIs, not available for merged APIs) */ @@ -630,6 +627,8 @@ export class GraphqlApi extends GraphqlApiBase { } this.node.addValidation({ validate: () => this.validateEnvironmentVariables() }); + this.visibility = props.visibility ?? Visibility.GLOBAL; + this.api = new CfnGraphQLApi(this, 'Resource', { name: props.name, authenticationType: defaultMode.authorizationType, @@ -652,6 +651,7 @@ export class GraphqlApi extends GraphqlApiBase { this.arn = this.api.attrArn; this.graphqlUrl = this.api.attrGraphQlUrl; this.name = this.api.name; + this.graphQLEndpointArn = this.api.attrGraphQlEndpointArn; if (this.definition.schema) { this.schemaResource = new CfnGraphQLSchema(this, 'Schema', this.definition.schema.bind(this)); diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 45e956687b750..310e7ea65e180 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -1,6 +1,5 @@ # Event Targets for Amazon EventBridge - This library contains integration classes to send Amazon EventBridge to any number of supported AWS Services. Instances of these classes should be passed to the `rule.addTarget()` method. @@ -17,6 +16,7 @@ Currently supported are: - [Queue a Batch job](#queue-a-batch-job) - [Invoke an API Gateway REST API](#invoke-an-api-gateway-rest-api) - [Invoke an API Destination](#invoke-an-api-destination) + - [Invoke an AppSync GraphQL API](#invoke-an-appsync-graphql-api) - [Put an event on an EventBridge bus](#put-an-event-on-an-eventbridge-bus) - [Run an ECS Task](#run-an-ecs-task) - [Tagging Tasks](#tagging-tasks) @@ -367,6 +367,65 @@ const rule = new events.Rule(this, 'OtherRule', { }); ``` +## Invoke an AppSync GraphQL API + +Use the `AppSync` target to trigger an AppSync GraphQL API. You need to +create an `AppSync.GraphqlApi` configured with `AWS_IAM` authorization mode. + +The code snippet below creates an AppSync GraphQL API target that is invoked every hour, calling the `publish` mutation. + +```ts +import * as appsync from 'aws-cdk-lib/aws-appsync'; + +const api = new appsync.GraphqlApi(this, 'api', { + name: 'api', + definition: appsync.Definition.fromFile('schema.graphql'), + authorizationConfig: { + defaultAuthorization: { authorizationType: appsync.AuthorizationType.IAM } + }, +}); + +const rule = new events.Rule(this, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), +}); + +rule.addTarget(new targets.AppSync(api, { + graphQLOperation: 'mutation Publish($message: String!){ publish(message: $message) { message } }', + variables: events.RuleTargetInput.fromObject({ + message: 'hello world', + }), +})); +``` + +You can pass an existing role with the proper permissions to be used for the target when the rule is triggered. The code snippet below uses an existing role and grants permissions to use the publish Mutation on the GraphQL API. + +```ts +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as appsync from 'aws-cdk-lib/aws-appsync'; + +const api = appsync.GraphqlApi.fromGraphqlApiAttributes(this, 'ImportedAPI', { + graphqlApiId: '', + graphqlApiArn: '', + graphQLEndpointArn: '', + visibility: appsync.Visibility.GLOBAL, + modes: [appsync.AuthorizationType.IAM], +}); + +const rule = new events.Rule(this, 'Rule', { schedule: events.Schedule.rate(cdk.Duration.minutes(1)), }); +const role = new iam.Role(this, 'Role', { assumedBy: new iam.ServicePrincipal('events.amazonaws.com') }); + +// allow EventBridge to use the `publish` mutation +api.grantMutation(role, 'publish'); + +rule.addTarget(new targets.AppSync(api, { + graphQLOperation: 'mutation Publish($message: String!){ publish(message: $message) { message } }', + variables: events.RuleTargetInput.fromObject({ + message: 'hello world', + }), + eventRole: role +})); +``` + ## Put an event on an EventBridge bus Use the `EventBus` target to route event to a different EventBus. diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/appsync.ts b/packages/aws-cdk-lib/aws-events-targets/lib/appsync.ts new file mode 100644 index 0000000000000..13330066b974b --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/lib/appsync.ts @@ -0,0 +1,84 @@ +import { addToDeadLetterQueueResourcePolicy, bindBaseTargetConfig, singletonEventRole, TargetBaseProps } from './util'; +import * as appsync from '../../aws-appsync'; +import * as events from '../../aws-events'; +import * as iam from '../../aws-iam'; + +/** + * Customize the AppSync GraphQL API target + */ +export interface AppSyncGraphQLApiProps extends TargetBaseProps { + /** + * The GraphQL operation; that is, the query, mutation, or subscription + * to be parsed and executed by the GraphQL service. + */ + readonly graphQLOperation: string; + + /** + * The variables that are include in the GraphQL operation. + * + * @default - The entire event is used + */ + readonly variables?: events.RuleTargetInput; + + /** + * The role to assume before invoking the target + * (i.e., the pipeline) when the given rule is triggered. + * + * @default - a new role with permissions to access mutations will be created + */ + readonly eventRole?: iam.IRole; +} + +/** + * Use an AppSync GraphQL API as a target for Amazon EventBridge rules. + */ +export class AppSync implements events.IRuleTarget { + + constructor(private readonly appsyncApi: appsync.IGraphqlApi, private readonly props: AppSyncGraphQLApiProps) { + } + + /** + * Returns a RuleTarget that can be used to trigger this AppSync GraphQL API + * as a result from an EventBridge event. + */ + public bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { + if (this.props.deadLetterQueue) { + addToDeadLetterQueueResourcePolicy(rule, this.props.deadLetterQueue); + } + + // make sure the API has AWS_IAM configured. + if (!this.appsyncApi.modes.includes(appsync.AuthorizationType.IAM)) { + throw new Error('You must have AWS_IAM authorization mode enabled on your API to configure an AppSync target'); + } + + // make sure this is a 'public' (i.e.: 'GLOBAL') API + if (this.appsyncApi.visibility !== appsync.Visibility.GLOBAL) { + throw new Error('Your API visibility must be "GLOBAL"'); + } + + // make sure the EndpointArn is not blank + if (this.appsyncApi.graphQLEndpointArn === '') { + throw new Error('You must have a valid `graphQLEndpointArn` set'); + } + + const role = this.props.eventRole || singletonEventRole(this.appsyncApi); + + // if a role was not provided, attach a permission + if (!this.props.eventRole) { + this.appsyncApi.grantMutation(role); + } + + return { + ...bindBaseTargetConfig(this.props), + arn: this.appsyncApi.graphQLEndpointArn, + role, + deadLetterConfig: this.props.deadLetterQueue && { arn: this.props.deadLetterQueue?.queueArn }, + input: this.props.variables, + targetResource: this.appsyncApi, + appSyncParameters: { + graphQlOperation: this.props.graphQLOperation, + }, + }; + } +} + diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts index 6c91810ebca33..a7db6ab24a04f 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts @@ -14,4 +14,5 @@ export * from './log-group'; export * from './kinesis-firehose-stream'; export * from './api-gateway'; export * from './api-destination'; +export * from './appsync'; export * from './util'; diff --git a/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.graphql b/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.graphql new file mode 100644 index 0000000000000..251775e1e6902 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.graphql @@ -0,0 +1,9 @@ +type Event { + message: String +} +type Query { + getTests: [Event]! +} +type Mutation { + publish(message: String!): Event +} diff --git a/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.ts new file mode 100644 index 0000000000000..c58eb2efb5332 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/test/appsync/appsync.test.ts @@ -0,0 +1,395 @@ +import * as path from 'path'; +import { Template } from '../../../assertions'; +import * as appsync from '../../../aws-appsync'; +import * as events from '../../../aws-events'; +import * as iam from '../../../aws-iam'; +import * as sqs from '../../../aws-sqs'; +import * as cdk from '../../../core'; +import * as targets from '../../lib'; + +const graphQLOperation = 'mutation Publish($message: String!){ publish(message: $message) { event } }'; + +describe('AppSync GraphQL API target', () => { + let stack: cdk.Stack; + beforeEach(() => { + stack = new cdk.Stack(); + }); + + test('fails when AWS_IAM auth is not configured', () => { + const noiam_api = new appsync.GraphqlApi(stack, 'noiamApi', { + name: 'no_iam_api', + definition: appsync.Definition.fromFile(path.join(__dirname, 'appsync.test.graphql')), + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + expect(() => { + rule.addTarget(new targets.AppSync(noiam_api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + }).toThrow('You must have AWS_IAM authorization mode enabled on your API to configure an AppSync target'); + }); + + test('fails when graphQLEndpointArn is not configured', () => { + const noEndpointArnAPI = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'ImportedAPI', { + graphqlApiId: 'MyApiId', + graphqlApiArn: 'MyApiArn', + modes: [appsync.AuthorizationType.IAM], + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + expect(() => { + rule.addTarget(new targets.AppSync(noEndpointArnAPI, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + }).toThrow('You must have a valid `graphQLEndpointArn` set'); + }); + + test('Accepts API create with fromGraphqlApiAttributes', () => { + const api = appsync.GraphqlApi.fromGraphqlApiAttributes(stack, 'ImportedAPI', { + graphqlApiId: 'MyApiId', + graphqlApiArn: 'MyApiArn', + graphQLEndpointArn: 'arn:aws:appsync:us-east-2:000000000000:endpoints/graphql-api/00000000000000000000000000', + visibility: appsync.Visibility.GLOBAL, + modes: [appsync.AuthorizationType.IAM], + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + // WHEN + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: api.graphQLEndpointArn, + AppSyncParameters: { GraphQLOperation: graphQLOperation }, + Id: 'Target0', + InputTransformer: { + InputPathsMap: { detail: '$.detail' }, + InputTemplate: '{"message":}', + }, + RoleArn: { + 'Fn::GetAtt': [ + 'ImportedAPIEventsRole9CE171B7', + 'Arn', + ], + }, + }, + ], + }); + }); + + test('allows secondary auth with AWS_IAM configured', () => { + const sec_api = new appsync.GraphqlApi(stack, 'sec_api', { + name: 'no_iam_api', + definition: appsync.Definition.fromFile(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { additionalAuthorizationModes: [{ authorizationType: appsync.AuthorizationType.IAM }] }, + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + expect(() => { + rule.addTarget(new targets.AppSync(sec_api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + }).not.toThrow('You must have AWS_IAM authorization mode enabled on your API to configure an AppSync target'); + }); + + test('fails when VISIBILITY is not "GLOBAL"', () => { + const sec_api = new appsync.GraphqlApi(stack, 'sec_api', { + name: 'no_iam_api', + visibility: appsync.Visibility.PRIVATE, + definition: appsync.Definition.fromFile(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { additionalAuthorizationModes: [{ authorizationType: appsync.AuthorizationType.IAM }] }, + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + expect(() => { + rule.addTarget(new targets.AppSync(sec_api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + }).toThrow('Your API visibility must be "GLOBAL"'); + }); + +}); + +describe('AppSync API with AWS_IAM auth', () => { + let api: appsync.GraphqlApi; + let stack: cdk.Stack; + beforeEach(() => { + stack = new cdk.Stack(); + api = new appsync.GraphqlApi(stack, 'baseApi', { + name: 'api', + definition: appsync.Definition.fromFile(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { defaultAuthorization: { authorizationType: appsync.AuthorizationType.IAM } }, + }); + }); + + test('use AppSync GraphQL API as an event rule target', () => { + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + // WHEN + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'GraphQLEndpointArn'] }, + AppSyncParameters: { GraphQLOperation: graphQLOperation }, + Id: 'Target0', + InputTransformer: { + InputPathsMap: { detail: '$.detail' }, + InputTemplate: '{"message":}', + }, + RoleArn: { + 'Fn::GetAtt': [ + 'baseApiEventsRoleAC472BD7', + 'Arn', + ], + }, + }, + ], + }); + }); + + test('use a Dead Letter Queue', () => { + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + // WHEN + const queue = new sqs.Queue(stack, 'Queue'); + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + deadLetterQueue: queue, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + DeadLetterConfig: { + Arn: { + 'Fn::GetAtt': [ + 'Queue4A7E3555', + 'Arn', + ], + }, + }, + Id: 'Target0', + }, + ], + }); + }); + + test('when no mutation fields provided, grant access to Mutations only', () => { + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + // WHEN + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'GraphQLEndpointArn'] }, + AppSyncParameters: { GraphQLOperation: graphQLOperation }, + Id: 'Target0', + InputTransformer: { + InputPathsMap: { detail: '$.detail' }, + InputTemplate: '{"message":}', + }, + RoleArn: { + 'Fn::GetAtt': [ + 'baseApiEventsRoleAC472BD7', + 'Arn', + ], + }, + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'events.amazonaws.com', + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'baseApiEventsRoleDefaultPolicy94199357', + PolicyDocument: { + Statement: [{ + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: + { + 'Fn::Join': [ + '', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':appsync:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':apis/', + { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, + '/types/Mutation/*', + ], + ], + }, + }], + Version: '2012-10-17', + }, + }); + }); + + test('a role is provided', () => { + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + const eventRole = new iam.Role(stack, 'role', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com'), + }); + api.grantMutation(eventRole, 'publish'); + + // WHEN + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + eventRole, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: { 'Fn::GetAtt': ['baseApiCDA4D43A', 'GraphQLEndpointArn'] }, + AppSyncParameters: { GraphQLOperation: graphQLOperation }, + Id: 'Target0', + InputTransformer: { + InputPathsMap: { detail: '$.detail' }, + InputTemplate: '{"message":}', + }, + RoleArn: { + 'Fn::GetAtt': [ + 'roleC7B7E775', + 'Arn', + ], + }, + }, + ], + }); + }); + + test('a role is not provided', () => { + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + + // WHEN + rule.addTarget(new targets.AppSync(api, { + graphQLOperation, + variables: events.RuleTargetInput.fromObject({ + message: events.EventField.fromPath('$.detail'), + }), + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [{ + Action: 'appsync:GraphQL', + Effect: 'Allow', + Resource: + { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':appsync:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':apis/', { 'Fn::GetAtt': ['baseApiCDA4D43A', 'ApiId'] }, '/types/Mutation/*']], + }, + + }], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'events.amazonaws.com', + }, + }, + ], + }, + }); + + }); + +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-events/lib/rule.ts b/packages/aws-cdk-lib/aws-events/lib/rule.ts index faa7ea7c488f6..b96347e20ba2b 100644 --- a/packages/aws-cdk-lib/aws-events/lib/rule.ts +++ b/packages/aws-cdk-lib/aws-events/lib/rule.ts @@ -238,6 +238,7 @@ export class Rule extends Resource implements IRule { deadLetterConfig: targetProps.deadLetterConfig, retryPolicy: targetProps.retryPolicy, sqsParameters: targetProps.sqsParameters, + appSyncParameters: targetProps.appSyncParameters, input: inputProps && inputProps.input, inputPath: inputProps && inputProps.inputPath, inputTransformer: inputProps?.inputTemplate !== undefined ? { diff --git a/packages/aws-cdk-lib/aws-events/lib/target.ts b/packages/aws-cdk-lib/aws-events/lib/target.ts index e836bcf75e53d..711b20b416bdf 100644 --- a/packages/aws-cdk-lib/aws-events/lib/target.ts +++ b/packages/aws-cdk-lib/aws-events/lib/target.ts @@ -60,6 +60,12 @@ export interface RuleTargetConfig { */ readonly retryPolicy?: CfnRule.RetryPolicyProperty; + /** + * Contains the GraphQL operation to be parsed and executed, if the event target is an AWS AppSync API. + * @default - None + */ + readonly appSyncParameters?: CfnRule.AppSyncParametersProperty; + /** * The Amazon ECS task definition and task count to use, if the event target * is an Amazon ECS task.