diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/README.md b/packages/@aws-cdk/aws-iot-actions-alpha/README.md index 10d076a1ad780..624365ecf3b65 100644 --- a/packages/@aws-cdk/aws-iot-actions-alpha/README.md +++ b/packages/@aws-cdk/aws-iot-actions-alpha/README.md @@ -163,6 +163,24 @@ const topicRule = new iot.TopicRule(this, 'TopicRule', { }); ``` +## Start Step Functions State Machine + +The code snippet below creates an AWS IoT Rule that starts a Step Functions State Machine +when it is triggered. + +```ts +const stateMachine = new stepfunctions.StateMachine(this, 'SM', { + definitionBody: stepfunctions.DefinitionBody.fromChainable(new stepfunctions.Wait(this, 'Hello', { time: stepfunctions.WaitTime.duration(Duration.seconds(10)) })), +}); + +new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT * FROM 'device/+/data'"), + actions: [ + new actions.StepFunctionsStateMachineAction(stateMachine), + ], +}); +``` + ## Change the state of an Amazon CloudWatch alarm The code snippet below creates an AWS IoT Rule that changes the state of an Amazon CloudWatch alarm when it is triggered: diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts b/packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts index 0a9d23398f59b..1a983fd548fc2 100644 --- a/packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts @@ -11,3 +11,4 @@ export * from './lambda-function-action'; export * from './s3-put-object-action'; export * from './sqs-queue-action'; export * from './sns-topic-action'; +export * from './step-functions-state-machine-action'; diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/lib/step-functions-state-machine-action.ts b/packages/@aws-cdk/aws-iot-actions-alpha/lib/step-functions-state-machine-action.ts new file mode 100644 index 0000000000000..30c8641148a28 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/lib/step-functions-state-machine-action.ts @@ -0,0 +1,65 @@ +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as iot from '@aws-cdk/aws-iot-alpha'; +import * as stepfunctions from 'aws-cdk-lib/aws-stepfunctions'; +import { CommonActionProps } from './common-action-props'; +import { singletonActionRole } from './private/role'; +import { ArnFormat, Stack } from 'aws-cdk-lib'; + +/** + * Configuration properties of an action for the Step Functions State Machine. + */ +export interface StepFunctionsStateMachineActionProps extends CommonActionProps { + /** + * Name of the state machine execution prefix. + * The name given to the state machine execution consists of this prefix followed by a UUID. Step Functions creates a unique name for each state machine execution if one is not provided. + * + * @see https://docs.aws.amazon.com/iot/latest/developerguide/stepfunctions-rule-action.html#stepfunctions-rule-action-parameters + * + * @default: None - Step Functions creates a unique name for each state machine execution if one is not provided. + */ + readonly executionNamePrefix?: string; +} + +/** + * The action to put the record from an MQTT message to the Step Functions State Machine. + */ +export class StepFunctionsStateMachineAction implements iot.IAction { + private readonly executionNamePrefix?: string; + private readonly role?: iam.IRole; + + /** + * @param stateMachine The Step Functions Start Machine which shoud be executed. + * @param props Optional properties to not use default + */ + constructor(private readonly stateMachine: stepfunctions.IStateMachine, props?: StepFunctionsStateMachineActionProps) { + this.executionNamePrefix = props?.executionNamePrefix; + this.role = props?.role; + } + + /** + * @internal + */ + public _bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + const stateMachineName = Stack.of(this.stateMachine).splitArn(this.stateMachine.stateMachineArn, ArnFormat.COLON_RESOURCE_NAME).resourceName; + + if (!stateMachineName) { + throw new Error(`No state machine name found in ARN: '${this.stateMachine.stateMachineArn}'`); + } + + role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['states:StartExecution'], + resources: [this.stateMachine.stateMachineArn], + })); + + return { + configuration: { + stepFunctions: { + stateMachineName, + executionNamePrefix: this.executionNamePrefix, + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/package.json b/packages/@aws-cdk/aws-iot-actions-alpha/package.json index 55d474d5d3b4a..2ccc973a3e584 100644 --- a/packages/@aws-cdk/aws-iot-actions-alpha/package.json +++ b/packages/@aws-cdk/aws-iot-actions-alpha/package.json @@ -102,6 +102,7 @@ "@aws-cdk/aws-iot-alpha": "0.0.0", "@aws-cdk/aws-iotevents-alpha": "0.0.0", "@aws-cdk/aws-kinesisfirehose-alpha": "0.0.0", + "@aws-cdk/aws-kinesisfirehose-destinations-alpha": "0.0.0", "aws-cdk-lib": "^0.0.0", "constructs": "^10.0.0" }, diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-iot-actions-alpha/rosetta/default.ts-fixture index b8926a17ed606..c39a08ffe22d8 100644 --- a/packages/@aws-cdk/aws-iot-actions-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-iot-actions-alpha/rosetta/default.ts-fixture @@ -1,10 +1,11 @@ // Fixture with packages imported, but nothing else -import { Stack } from 'aws-cdk-lib'; +import { Stack, Duration } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as actions from '@aws-cdk/aws-iot-actions-alpha'; import * as iot from '@aws-cdk/aws-iot-alpha'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as stepfunctions from 'aws-cdk-lib/aws-stepfunctions'; class Fixture extends Stack { constructor(scope: Construct, id: string) { diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/cdk.out b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/cdk.out new file mode 100644 index 0000000000000..f0b901e7c06e5 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"32.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/integ.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/integ.json new file mode 100644 index 0000000000000..b19645d138e0a --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "32.0.0", + "testCases": { + "state-machine-integtest/DefaultTest": { + "stacks": [ + "test-step-functions-start-state-machine-action-stack" + ], + "assertionStack": "state-machine-integtest/DefaultTest/DeployAssert", + "assertionStackName": "statemachineintegtestDefaultTestDeployAssert1FAFBF55" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/manifest.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/manifest.json new file mode 100644 index 0000000000000..479672bd9856b --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/manifest.json @@ -0,0 +1,135 @@ +{ + "version": "32.0.0", + "artifacts": { + "test-step-functions-start-state-machine-action-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "test-step-functions-start-state-machine-action-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "test-step-functions-start-state-machine-action-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "test-step-functions-start-state-machine-action-stack.template.json", + "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}/2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "test-step-functions-start-state-machine-action-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "test-step-functions-start-state-machine-action-stack.assets" + ], + "metadata": { + "/test-step-functions-start-state-machine-action-stack/TopicRule/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRule40A4EA44" + } + ], + "/test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRuleTopicRuleActionRole246C4F77" + } + ], + "/test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687" + } + ], + "/test-step-functions-start-state-machine-action-stack/SM/Role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SMRole49C19C48" + } + ], + "/test-step-functions-start-state-machine-action-stack/SM/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "SM934E715A" + } + ], + "/test-step-functions-start-state-machine-action-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/test-step-functions-start-state-machine-action-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "test-step-functions-start-state-machine-action-stack" + }, + "statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "statemachineintegtestDefaultTestDeployAssert1FAFBF55": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json", + "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": [ + "statemachineintegtestDefaultTestDeployAssert1FAFBF55.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": [ + "statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets" + ], + "metadata": { + "/state-machine-integtest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/state-machine-integtest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "state-machine-integtest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets.json new file mode 100644 index 0000000000000..01ac59326df3b --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.assets.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.assets.json new file mode 100644 index 0000000000000..45d844b26d5a3 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "32.0.0", + "files": { + "2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab": { + "source": { + "path": "test-step-functions-start-state-machine-action-stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.template.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.template.json new file mode 100644 index 0000000000000..bc0bcb456a503 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/test-step-functions-start-state-machine-action-stack.template.json @@ -0,0 +1,146 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "StepFunctions": { + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + }, + "StateMachineName": { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "SM934E715A" + } + ] + } + ] + } + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "states:StartExecution", + "Effect": "Allow", + "Resource": { + "Ref": "SM934E715A" + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "SMRole49C19C48": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "SM934E715A": { + "Type": "AWS::StepFunctions::StateMachine", + "Properties": { + "RoleArn": { + "Fn::GetAtt": [ + "SMRole49C19C48", + "Arn" + ] + }, + "DefinitionString": "{\"StartAt\":\"Hello\",\"States\":{\"Hello\":{\"Type\":\"Wait\",\"Seconds\":10,\"End\":true}}}" + }, + "DependsOn": [ + "SMRole49C19C48" + ], + "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/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/tree.json b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/tree.json new file mode 100644 index 0000000000000..725cc452d850c --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.js.snapshot/tree.json @@ -0,0 +1,318 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "test-step-functions-start-state-machine-action-stack": { + "id": "test-step-functions-start-state-machine-action-stack", + "path": "test-step-functions-start-state-machine-action-stack", + "children": { + "TopicRule": { + "id": "TopicRule", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule", + "children": { + "Resource": { + "id": "Resource", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IoT::TopicRule", + "aws:cdk:cloudformation:props": { + "topicRulePayload": { + "actions": [ + { + "stepFunctions": { + "stateMachineName": { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "SM934E715A" + } + ] + } + ] + }, + "roleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + } + } + } + ], + "awsIotSqlVersion": "2016-03-23", + "sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iot.CfnTopicRule", + "version": "0.0.0" + } + }, + "TopicRuleActionRole": { + "id": "TopicRuleActionRole", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole", + "children": { + "ImportTopicRuleActionRole": { + "id": "ImportTopicRuleActionRole", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/ImportTopicRuleActionRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "states:StartExecution", + "Effect": "Allow", + "Resource": { + "Ref": "SM934E715A" + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "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/aws-iot-alpha.TopicRule", + "version": "0.0.0" + } + }, + "Hello": { + "id": "Hello", + "path": "test-step-functions-start-state-machine-action-stack/Hello", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions.Wait", + "version": "0.0.0" + } + }, + "SM": { + "id": "SM", + "path": "test-step-functions-start-state-machine-action-stack/SM", + "children": { + "Role": { + "id": "Role", + "path": "test-step-functions-start-state-machine-action-stack/SM/Role", + "children": { + "ImportRole": { + "id": "ImportRole", + "path": "test-step-functions-start-state-machine-action-stack/SM/Role/ImportRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "test-step-functions-start-state-machine-action-stack/SM/Role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "test-step-functions-start-state-machine-action-stack/SM/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::StepFunctions::StateMachine", + "aws:cdk:cloudformation:props": { + "roleArn": { + "Fn::GetAtt": [ + "SMRole49C19C48", + "Arn" + ] + }, + "definitionString": "{\"StartAt\":\"Hello\",\"States\":{\"Hello\":{\"Type\":\"Wait\",\"Seconds\":10,\"End\":true}}}" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions.CfnStateMachine", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_stepfunctions.StateMachine", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "test-step-functions-start-state-machine-action-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "test-step-functions-start-state-machine-action-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "state-machine-integtest": { + "id": "state-machine-integtest", + "path": "state-machine-integtest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "state-machine-integtest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "state-machine-integtest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.52" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "state-machine-integtest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "state-machine-integtest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "state-machine-integtest/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.2.52" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.ts b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.ts new file mode 100644 index 0000000000000..97e17998f700d --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/integ.step-functions-start-state-machine.ts @@ -0,0 +1,26 @@ +import * as iot from '@aws-cdk/aws-iot-alpha'; +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import * as actions from '../../lib'; +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'test-step-functions-start-state-machine-action-stack'); + +const topicRule = new iot.TopicRule(stack, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT * FROM 'device/+/data'", + ), +}); + +const stateMachine = new sfn.StateMachine(stack, 'SM', { + definitionBody: sfn.DefinitionBody.fromChainable(new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) })), +}); + +topicRule.addAction(new actions.StepFunctionsStateMachineAction(stateMachine)); + +new integ.IntegTest(app, 'state-machine-integtest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/step-functions-start-state-machine.test.ts b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/step-functions-start-state-machine.test.ts new file mode 100644 index 0000000000000..bb36c683fe2df --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions-alpha/test/step-functions/step-functions-start-state-machine.test.ts @@ -0,0 +1,161 @@ +import { Template, Match } from 'aws-cdk-lib/assertions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as iot from '@aws-cdk/aws-iot-alpha'; +import * as cdk from 'aws-cdk-lib'; +import * as actions from '../../lib'; +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; + +test('Default state machine action action', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stateMachine = new sfn.StateMachine(stack, 'SM', { + definitionBody: sfn.DefinitionBody.fromChainable(new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) })), + }); + + // WHEN + topicRule.addAction( + new actions.StepFunctionsStateMachineAction(stateMachine), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + StepFunctions: { + StateMachineName: { + 'Fn::Select': [6, + { + 'Fn::Split': [ + ':', + { Ref: 'SM934E715A' }, + ], + }], + }, + RoleArn: { + 'Fn::GetAtt': ['MyTopicRuleTopicRuleActionRoleCE2D05DA', 'Arn'], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'states:StartExecution', + Effect: 'Allow', + Resource: { + Ref: 'SM934E715A', + }, + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + +test('can use imported state machine', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stateMachine = sfn.StateMachine.fromStateMachineArn(stack, 'SM', 'arn:aws:states:us-east-1:111122223333:stateMachine:existing-state-machine'); + + // WHEN + topicRule.addAction( + new actions.StepFunctionsStateMachineAction(stateMachine), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ StepFunctions: { StateMachineName: 'existing-state-machine' } }), + ], + }, + }); +}); + +test('can set executionNamePrefix', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const stateMachine = new sfn.StateMachine(stack, 'SM', { + definitionBody: sfn.DefinitionBody.fromChainable(new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) })), + }); + + // WHEN + topicRule.addAction( + new actions.StepFunctionsStateMachineAction(stateMachine, { + executionNamePrefix: 'my-prefix', + }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ StepFunctions: { ExecutionNamePrefix: 'my-prefix' } }), + ], + }, + }); +}); + +test('can set role', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); + const stateMachine = new sfn.StateMachine(stack, 'SM', { + definitionBody: sfn.DefinitionBody.fromChainable(new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) })), + }); + + // WHEN + topicRule.addAction( + new actions.StepFunctionsStateMachineAction(stateMachine, { role }), + ); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ StepFunctions: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], + }); +});