diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json new file mode 100644 index 0000000000000..2aa2b214acb69 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.5", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "LogGroupDefaultTestDeployAssert353EE07A.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/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/LogGroupDefaultTestDeployAssert353EE07A.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/redshift-query/integ.redshift-query.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out new file mode 100644 index 0000000000000..bd5311dc372de --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.5"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json new file mode 100644 index 0000000000000..027efdcf5d2be --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.5", + "testCases": { + "LogGroup/DefaultTest": { + "stacks": [ + "redshift-query-events" + ], + "assertionStack": "LogGroup/DefaultTest/DeployAssert", + "assertionStackName": "LogGroupDefaultTestDeployAssert353EE07A" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json new file mode 100644 index 0000000000000..02a985ee171ac --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/manifest.json @@ -0,0 +1,149 @@ +{ + "version": "36.0.5", + "artifacts": { + "redshift-query-events.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "redshift-query-events.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "redshift-query-events": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "redshift-query-events.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}/97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "redshift-query-events.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": [ + "redshift-query-events.assets" + ], + "metadata": { + "/redshift-query-events/Namespace": [ + { + "type": "aws:cdk:logicalId", + "data": "Namespace" + } + ], + "/redshift-query-events/WorkGroup": [ + { + "type": "aws:cdk:logicalId", + "data": "WorkGroup" + } + ], + "/redshift-query-events/dlq/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "dlq09C78ACC" + } + ], + "/redshift-query-events/Secret/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SecretA720EF05" + } + ], + "/redshift-query-events/Timer3/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer30894E3BB" + } + ], + "/redshift-query-events/Timer3/EventsRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer3EventsRole909B99A1" + } + ], + "/redshift-query-events/Timer3/EventsRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Timer3EventsRoleDefaultPolicy3A2ECE32" + } + ], + "/redshift-query-events/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/redshift-query-events/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "redshift-query-events" + }, + "LogGroupDefaultTestDeployAssert353EE07A.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "LogGroupDefaultTestDeployAssert353EE07A.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "LogGroupDefaultTestDeployAssert353EE07A": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "LogGroupDefaultTestDeployAssert353EE07A.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": [ + "LogGroupDefaultTestDeployAssert353EE07A.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": [ + "LogGroupDefaultTestDeployAssert353EE07A.assets" + ], + "metadata": { + "/LogGroup/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/LogGroup/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "LogGroup/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/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json new file mode 100644 index 0000000000000..6d1c133d923ae --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.5", + "files": { + "97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9": { + "source": { + "path": "redshift-query-events.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "97ed69e0c6a3717cdba2853084a186e0bfd33216748d11445a14282087c0b2e9.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/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json new file mode 100644 index 0000000000000..92de97c1b462c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/redshift-query-events.template.json @@ -0,0 +1,193 @@ +{ + "Resources": { + "Namespace": { + "Type": "AWS::RedshiftServerless::Namespace", + "Properties": { + "NamespaceName": "namespace" + } + }, + "WorkGroup": { + "Type": "AWS::RedshiftServerless::Workgroup", + "Properties": { + "NamespaceName": "namespace", + "SecurityGroupIds": [ + "sg-0f3ee03c20cc6056c" + ], + "SubnetIds": [ + "subnet-06c91b5d4c16df0ff", + "subnet-04b90752f12ed5174", + "subnet-0d42bcb68396ffd19" + ], + "WorkgroupName": "workgroup" + }, + "DependsOn": [ + "Namespace" + ] + }, + "dlq09C78ACC": { + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SecretA720EF05": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "Timer30894E3BB": { + "Type": "AWS::Events::Rule", + "Properties": { + "ScheduleExpression": "rate(1 minute)", + "State": "ENABLED", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "Id": "Target0", + "RedshiftDataParameters": { + "Database": "dev", + "SecretManagerArn": { + "Ref": "SecretA720EF05" + }, + "Sql": "SELECT * FROM baz" + }, + "RoleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + } + }, + { + "Arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "DeadLetterConfig": { + "Arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "Id": "Target1", + "RedshiftDataParameters": { + "Database": "dev", + "SecretManagerArn": { + "Ref": "SecretA720EF05" + }, + "Sqls": [ + "SELECT * FROM foo", + "SELECT * FROM bar" + ] + }, + "RoleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + } + } + ] + } + }, + "Timer3EventsRole909B99A1": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "Timer3EventsRoleDefaultPolicy3A2ECE32": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "Timer3EventsRoleDefaultPolicy3A2ECE32", + "Roles": [ + { + "Ref": "Timer3EventsRole909B99A1" + } + ] + } + } + }, + "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/redshift-query/integ.redshift-query.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json new file mode 100644 index 0000000000000..979b6c8cc3a6c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.js.snapshot/tree.json @@ -0,0 +1,364 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "redshift-query-events": { + "id": "redshift-query-events", + "path": "redshift-query-events", + "children": { + "Namespace": { + "id": "Namespace", + "path": "redshift-query-events/Namespace", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RedshiftServerless::Namespace", + "aws:cdk:cloudformation:props": { + "namespaceName": "namespace" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_redshiftserverless.CfnNamespace", + "version": "0.0.0" + } + }, + "WorkGroup": { + "id": "WorkGroup", + "path": "redshift-query-events/WorkGroup", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::RedshiftServerless::Workgroup", + "aws:cdk:cloudformation:props": { + "namespaceName": "namespace", + "securityGroupIds": [ + "sg-0f3ee03c20cc6056c" + ], + "subnetIds": [ + "subnet-06c91b5d4c16df0ff", + "subnet-04b90752f12ed5174", + "subnet-0d42bcb68396ffd19" + ], + "workgroupName": "workgroup" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_redshiftserverless.CfnWorkgroup", + "version": "0.0.0" + } + }, + "dlq": { + "id": "dlq", + "path": "redshift-query-events/dlq", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/dlq/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" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_sqs.Queue", + "version": "0.0.0" + } + }, + "Secret": { + "id": "Secret", + "path": "redshift-query-events/Secret", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Secret/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::SecretsManager::Secret", + "aws:cdk:cloudformation:props": { + "generateSecretString": {} + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.CfnSecret", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_secretsmanager.Secret", + "version": "0.0.0" + } + }, + "Timer3": { + "id": "Timer3", + "path": "redshift-query-events/Timer3", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/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": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + }, + "deadLetterConfig": { + "arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "redshiftDataParameters": { + "database": "dev", + "secretManagerArn": { + "Ref": "SecretA720EF05" + }, + "sql": "SELECT * FROM baz" + } + }, + { + "id": "Target1", + "arn": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "Timer3EventsRole909B99A1", + "Arn" + ] + }, + "deadLetterConfig": { + "arn": { + "Fn::GetAtt": [ + "dlq09C78ACC", + "Arn" + ] + } + }, + "redshiftDataParameters": { + "database": "dev", + "secretManagerArn": { + "Ref": "SecretA720EF05" + }, + "sqls": [ + "SELECT * FROM foo", + "SELECT * FROM bar" + ] + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_events.CfnRule", + "version": "0.0.0" + } + }, + "EventsRole": { + "id": "EventsRole", + "path": "redshift-query-events/Timer3/EventsRole", + "children": { + "ImportEventsRole": { + "id": "ImportEventsRole", + "path": "redshift-query-events/Timer3/EventsRole/ImportEventsRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/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": "redshift-query-events/Timer3/EventsRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "redshift-query-events/Timer3/EventsRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "redshift-data:BatchExecuteStatement", + "redshift-data:ExecuteStatement" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "WorkGroup", + "Workgroup.WorkgroupArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "Timer3EventsRoleDefaultPolicy3A2ECE32", + "roles": [ + { + "Ref": "Timer3EventsRole909B99A1" + } + ] + } + }, + "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_events.Rule", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "redshift-query-events/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "redshift-query-events/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "LogGroup": { + "id": "LogGroup", + "path": "LogGroup", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "LogGroup/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "LogGroup/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "LogGroup/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "LogGroup/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "LogGroup/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/redshift-query/integ.redshift-query.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts new file mode 100644 index 0000000000000..72d2c1b1ddc79 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-events-targets/test/redshift-query/integ.redshift-query.ts @@ -0,0 +1,50 @@ +import * as events from 'aws-cdk-lib/aws-events'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; +import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless'; +import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'redshift-query-events'); + +const namespace = new redshiftserverless.CfnNamespace(stack, 'Namespace', { + namespaceName: 'namespace', +}); + +const workGroup = new redshiftserverless.CfnWorkgroup(stack, 'WorkGroup', { + workgroupName: 'workgroup', + namespaceName: namespace.namespaceName, + subnetIds: ['subnet-06c91b5d4c16df0ff', 'subnet-04b90752f12ed5174', 'subnet-0d42bcb68396ffd19'], + securityGroupIds: ['sg-0f3ee03c20cc6056c'], +}); +workGroup.addDependency(namespace); + +const queue = new sqs.Queue(stack, 'dlq'); + +const secret = new secretsmanager.Secret(stack, 'Secret'); + +const timer3 = new events.Rule(stack, 'Timer3', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), +}); +timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { + database: 'dev', + deadLetterQueue: queue, + sql: ['SELECT * FROM baz'], + secret, +})); + +timer3.addTarget(new targets.RedshiftQuery(workGroup.attrWorkgroupWorkgroupArn, { + database: 'dev', + deadLetterQueue: queue, + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], + secret, +})); + +new IntegTest(app, 'LogGroup', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-events-targets/README.md b/packages/aws-cdk-lib/aws-events-targets/README.md index 310e7ea65e180..73c052ac889a2 100644 --- a/packages/aws-cdk-lib/aws-events-targets/README.md +++ b/packages/aws-cdk-lib/aws-events-targets/README.md @@ -23,6 +23,7 @@ Currently supported are: - [Launch type for ECS Task](#launch-type-for-ecs-task) - [Assign public IP addresses to tasks](#assign-public-ip-addresses-to-tasks) - [Enable Amazon ECS Exec for ECS Task](#enable-amazon-ecs-exec-for-ecs-task) + - [Run a Redshift query](#schedule-a-redshift-query-serverless-or-cluster) See the README of the `aws-cdk-lib/aws-events` library for more information on EventBridge. @@ -562,3 +563,27 @@ rule.addTarget(new targets.EcsTask({ enableExecuteCommand: true, })); ``` + +## Schedule a Redshift query (serverless or cluster) + +Use the `RedshiftQuery` target to schedule an Amazon Redshift Query. + +The code snippet below creates the scheduled event rule that route events to an Amazon Redshift Query + +```ts +import * as redshiftserverless from 'aws-cdk-lib/aws-redshiftserverless' + +declare const workgroup: redshiftserverless.CfnWorkgroup; + +const rule = new events.Rule(this, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), +}); + +const dlq = new sqs.Queue(this, 'DeadLetterQueue'); + +rule.addTarget(new targets.RedshiftQuery(workgroup.attrWorkgroupWorkgroupArn, { + database: 'dev', + deadLetterQueue: dlq, + sql: ['SELECT * FROM foo','SELECT * FROM baz'], +})); +``` 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 a7db6ab24a04f..af88ee14bc5d3 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/index.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/index.ts @@ -16,3 +16,4 @@ export * from './api-gateway'; export * from './api-destination'; export * from './appsync'; export * from './util'; +export * from './redshift-query'; diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts new file mode 100644 index 0000000000000..6decb307ba881 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/lib/redshift-query.ts @@ -0,0 +1,138 @@ +import { bindBaseTargetConfig, singletonEventRole } from './util'; +import * as events from '../../aws-events'; +import * as iam from '../../aws-iam'; +import * as secretsmanager from '../../aws-secretsmanager'; +import * as sqs from '../../aws-sqs'; + +/** + * Configuration properties of an Amazon Redshift Query event. + */ +export interface RedshiftQueryProps { + + /** + * The Amazon Redshift database to run the query against. + */ + readonly database: string; + + /** + * The Amazon Redshift database user to run the query as. This is required when authenticating via temporary credentials. + * + * @default - No Database user is specified + */ + readonly dbUser?: string; + + /** + * The secret containing the password for the database user. This is required when authenticating via Secrets Manager. + * If the full secret ARN is not specified, this will instead use the secret name. + * + * @default - No secret is specified + */ + readonly secret?: secretsmanager.ISecret; + + /** + * The SQL queries to be executed. Each query is run sequentially within a single transaction; the next query in the array will only execute after the previous one has successfully completed. + * + * - When multiple sql queries are included, this will use the `batchExecuteStatement` API. Therefore, if any statement fails, the entire transaction is rolled back. + * - If a single SQL statement is to be executed, this will use the `executeStatement` API. + * + * @default - No SQL query is specified + */ + readonly sql: string[]; + + /** + * The name of the SQL statement. You can name the SQL statement for identitfication purposes. If you would like Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console, append a `QS2-` prefix to the statement name. + * + * @default - No statement name is specified + */ + readonly statementName?: string; + + /** + * Should an event be sent back to Event Bridge when the SQL statement is executed. + * + * @default false + */ + readonly sendEventBridgeEvent?: boolean; + + /** + * The queue to be used as dead letter queue. + * + * @default - No dead letter queue is specified + */ + readonly deadLetterQueue?: sqs.IQueue; + + /** + * The IAM role to be used to execute the SQL statement. + * + * @default - a new role will be created. + */ + readonly role?: iam.IRole; + + /** + * The input to the state machine execution + * + * @default - the entire EventBridge event + */ + readonly input?: events.RuleTargetInput; +} + +/** + * Schedule an Amazon Redshift Query to be run, using the Redshift Data API. + * + * If you would like Amazon Redshift to identify the Event Bridge rule, and present it in the Amazon Redshift console, append a `QS2-` prefix to both `statementName` and `ruleName`. + */ +export class RedshiftQuery implements events.IRuleTarget { + constructor( + /** + * The ARN of the Amazon Redshift cluster + */ + private readonly clusterArn: string, + + /** + * The properties of the Redshift Query event + */ + private readonly props: RedshiftQueryProps, + ) {} + + bind(rule: events.IRule, _id?: string): events.RuleTargetConfig { + const role = this.props.role ?? singletonEventRole(rule); + if (this.props.sql.length < 1) { + throw new Error('At least one SQL statement must be specified.'); + } + if (this.props.sql.length === 1) { + role.addToPrincipalPolicy(this.putEventStatement()); + } + if (this.props.sql.length > 1) { + role.addToPrincipalPolicy(this.putBatchEventStatement()); + } + + return { + ...bindBaseTargetConfig(this.props), + arn: this.clusterArn, + role, + input: this.props.input, + redshiftDataParameters: { + database: this.props.database, + dbUser: this.props.dbUser, + secretManagerArn: this.props.secret?.secretFullArn ?? this.props.secret?.secretName, + sql: this.props.sql.length === 1 ? this.props.sql[0] : undefined, + sqls: this.props.sql.length > 1 ? this.props.sql : undefined, + statementName: this.props.statementName, + withEvent: this.props.sendEventBridgeEvent, + }, + }; + } + + private putEventStatement() { + return new iam.PolicyStatement({ + actions: ['redshift-data:ExecuteStatement'], + resources: [this.clusterArn], + }); + } + + private putBatchEventStatement() { + return new iam.PolicyStatement({ + actions: ['redshift-data:BatchExecuteStatement'], + resources: [this.clusterArn], + }); + } +} diff --git a/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts new file mode 100644 index 0000000000000..be51299ffd6f6 --- /dev/null +++ b/packages/aws-cdk-lib/aws-events-targets/test/redshift-query/redshift-query.test.ts @@ -0,0 +1,220 @@ +import { Template } from '../../../assertions'; +import * as events from '../../../aws-events'; +import * as secretsmanager from '../../../aws-secretsmanager'; +import { Stack } from '../../../core'; +import * as targets from '../../lib'; + +describe('RedshiftQuery event target', () => { + let stack: Stack; + let clusterArn: string; + + beforeEach(() => { + stack = new Stack(); + clusterArn = 'arn:aws:redshift:us-west-2:123456789012:cluster:my-cluster'; + }); + + describe('when added to an event rule as a target', () => { + let rule: events.Rule; + + beforeEach(() => { + rule = new events.Rule(stack, 'rule', { + schedule: events.Schedule.expression('rate(1 minute)'), + }); + }); + + describe('with default settings', () => { + beforeEach(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo'], + })); + }); + + test('adds the clusters ARN and role to the targets of the rule', () => { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RoleArn: { 'Fn::GetAtt': ['ruleEventsRole7F0DD2EE', 'Arn'] }, + }, + ], + }); + }); + + test('assigns the database to the RedshiftQuery', () => { + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + RedshiftDataParameters: { + Database: 'dev', + }, + }, + ], + }); + }); + }); + + describe('with explicity set SQL statements', () => { + test('sets the SQL statement', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + }, + }, + ], + }); + }); + + test('sets the batch SQL statements', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sqls: ['SELECT * FROM foo', 'SELECT * FROM bar'], + }, + }, + ], + }); + }); + + test('creates a policy that has ExecuteStatement permission on the clusters ARN', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'redshift-data:ExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('creates a policy that has BatchExecuteStatement permission on the clusters ARN', () => { + // GIVEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo', 'SELECT * FROM bar'], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'redshift-data:BatchExecuteStatement', + Effect: 'Allow', + Resource: clusterArn, + }, + ], + Version: '2012-10-17', + }, + }); + }); + }); + + describe('with secrets manager', () => { + test('adding a secrets manager secret to the target', () => { + // GIVEN + const secret = new secretsmanager.Secret(stack, 'Secret'); + + // WHEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo'], + secret, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + SecretManagerArn: { Ref: 'SecretA720EF05' }, + }, + }, + ], + }); + }); + + test('adding an imported secrets manager secret to the target, that does not have `secretFullArn` set', () => { + // GIVEN + const secret = secretsmanager.Secret.fromSecretNameV2(stack, 'Secret', 'my-secret'); + + // WHEN + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: ['SELECT * FROM foo'], + secret, + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + Arn: clusterArn, + Id: 'Target0', + RedshiftDataParameters: { + Database: 'dev', + Sql: 'SELECT * FROM foo', + SecretManagerArn: 'my-secret', + }, + }, + ], + }); + }); + }); + + describe('failures', () => { + test('throws an error if there are no elements in the sql array', () => { + // WHEN + expect(() => { + rule.addTarget(new targets.RedshiftQuery(clusterArn, { + database: 'dev', + sql: [], + })); + }) + + // THEN + .toThrow(/At least one SQL statement must be specified./); + }); + }); + }); +}); + diff --git a/packages/aws-cdk-lib/aws-events/lib/rule.ts b/packages/aws-cdk-lib/aws-events/lib/rule.ts index bbac64b14e63a..67b26279e0322 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, + redshiftDataParameters: targetProps.redshiftDataParameters, appSyncParameters: targetProps.appSyncParameters, input: inputProps && inputProps.input, inputPath: inputProps && inputProps.inputPath, diff --git a/packages/aws-cdk-lib/aws-events/lib/target.ts b/packages/aws-cdk-lib/aws-events/lib/target.ts index 711b20b416bdf..bee7ba4de1a71 100644 --- a/packages/aws-cdk-lib/aws-events/lib/target.ts +++ b/packages/aws-cdk-lib/aws-events/lib/target.ts @@ -98,6 +98,13 @@ export interface RuleTargetConfig { */ readonly sqsParameters?: CfnRule.SqsParametersProperty; + /** + * Parameters used when the rule invokes Amazon Redshift Queries + * + * @default - no parameters set + */ + readonly redshiftDataParameters?: CfnRule.RedshiftDataParametersProperty; + /** * What input to send to the event target * diff --git a/packages/aws-cdk-lib/aws-events/test/rule.test.ts b/packages/aws-cdk-lib/aws-events/test/rule.test.ts index dbcf5ad9bd01c..b99bcdd5443b2 100644 --- a/packages/aws-cdk-lib/aws-events/test/rule.test.ts +++ b/packages/aws-cdk-lib/aws-events/test/rule.test.ts @@ -686,6 +686,49 @@ describe('rule', () => { }); }); + test('redshiftDataParameters are generated when they are specified in target props', () => { + const stack = new cdk.Stack(); + const t1: IRuleTarget = { + bind: () => ({ + id: '', + arn: 'ARN1', + redshiftDataParameters: { + database: 'database', + dbUser: 'dbUser', + secretManagerArn: 'secretManagerArn', + sqls: ['sqls'], + statementName: 'statementName', + withEvent: true, + }, + }), + }; + + new Rule(stack, 'EventRule', { + schedule: Schedule.rate(cdk.Duration.minutes(5)), + targets: [t1], + }); + + // eslint-disable-next-line no-console + console.log(Template.fromStack(stack).toJSON().Resources.EventRule5A491D2C.Properties.Targets[0]); + + Template.fromStack(stack).hasResourceProperties('AWS::Events::Rule', { + Targets: [ + { + 'Arn': 'ARN1', + 'Id': 'Target0', + 'RedshiftDataParameters': { + 'Database': 'database', + 'DbUser': 'dbUser', + 'SecretManagerArn': 'secretManagerArn', + 'Sqls': ['sqls'], + 'StatementName': 'statementName', + 'WithEvent': true, + }, + }, + ], + }); + }); + test('associate rule with event bus', () => { // GIVEN const stack = new cdk.Stack();