Skip to content

Commit bd86993

Browse files
authored
feat(iot): add action to start Step Functions State Machine (#26059)
Add new IoT topic rule action to send IoT messages to Step Functions State Machines. Closes #17698. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent a21b099 commit bd86993

15 files changed

+960
-1
lines changed

packages/@aws-cdk/aws-iot-actions-alpha/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,24 @@ const topicRule = new iot.TopicRule(this, 'TopicRule', {
163163
});
164164
```
165165

166+
## Start Step Functions State Machine
167+
168+
The code snippet below creates an AWS IoT Rule that starts a Step Functions State Machine
169+
when it is triggered.
170+
171+
```ts
172+
const stateMachine = new stepfunctions.StateMachine(this, 'SM', {
173+
definitionBody: stepfunctions.DefinitionBody.fromChainable(new stepfunctions.Wait(this, 'Hello', { time: stepfunctions.WaitTime.duration(Duration.seconds(10)) })),
174+
});
175+
176+
new iot.TopicRule(this, 'TopicRule', {
177+
sql: iot.IotSql.fromStringAsVer20160323("SELECT * FROM 'device/+/data'"),
178+
actions: [
179+
new actions.StepFunctionsStateMachineAction(stateMachine),
180+
],
181+
});
182+
```
183+
166184
## Change the state of an Amazon CloudWatch alarm
167185

168186
The code snippet below creates an AWS IoT Rule that changes the state of an Amazon CloudWatch alarm when it is triggered:

packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export * from './lambda-function-action';
1111
export * from './s3-put-object-action';
1212
export * from './sqs-queue-action';
1313
export * from './sns-topic-action';
14+
export * from './step-functions-state-machine-action';
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as iam from 'aws-cdk-lib/aws-iam';
2+
import * as iot from '@aws-cdk/aws-iot-alpha';
3+
import * as stepfunctions from 'aws-cdk-lib/aws-stepfunctions';
4+
import { CommonActionProps } from './common-action-props';
5+
import { singletonActionRole } from './private/role';
6+
import { ArnFormat, Stack } from 'aws-cdk-lib';
7+
8+
/**
9+
* Configuration properties of an action for the Step Functions State Machine.
10+
*/
11+
export interface StepFunctionsStateMachineActionProps extends CommonActionProps {
12+
/**
13+
* Name of the state machine execution prefix.
14+
* 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.
15+
*
16+
* @see https://docs.aws.amazon.com/iot/latest/developerguide/stepfunctions-rule-action.html#stepfunctions-rule-action-parameters
17+
*
18+
* @default: None - Step Functions creates a unique name for each state machine execution if one is not provided.
19+
*/
20+
readonly executionNamePrefix?: string;
21+
}
22+
23+
/**
24+
* The action to put the record from an MQTT message to the Step Functions State Machine.
25+
*/
26+
export class StepFunctionsStateMachineAction implements iot.IAction {
27+
private readonly executionNamePrefix?: string;
28+
private readonly role?: iam.IRole;
29+
30+
/**
31+
* @param stateMachine The Step Functions Start Machine which shoud be executed.
32+
* @param props Optional properties to not use default
33+
*/
34+
constructor(private readonly stateMachine: stepfunctions.IStateMachine, props?: StepFunctionsStateMachineActionProps) {
35+
this.executionNamePrefix = props?.executionNamePrefix;
36+
this.role = props?.role;
37+
}
38+
39+
/**
40+
* @internal
41+
*/
42+
public _bind(rule: iot.ITopicRule): iot.ActionConfig {
43+
const role = this.role ?? singletonActionRole(rule);
44+
const stateMachineName = Stack.of(this.stateMachine).splitArn(this.stateMachine.stateMachineArn, ArnFormat.COLON_RESOURCE_NAME).resourceName;
45+
46+
if (!stateMachineName) {
47+
throw new Error(`No state machine name found in ARN: '${this.stateMachine.stateMachineArn}'`);
48+
}
49+
50+
role.addToPrincipalPolicy(new iam.PolicyStatement({
51+
actions: ['states:StartExecution'],
52+
resources: [this.stateMachine.stateMachineArn],
53+
}));
54+
55+
return {
56+
configuration: {
57+
stepFunctions: {
58+
stateMachineName,
59+
executionNamePrefix: this.executionNamePrefix,
60+
roleArn: role.roleArn,
61+
},
62+
},
63+
};
64+
}
65+
}

packages/@aws-cdk/aws-iot-actions-alpha/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
"@aws-cdk/aws-iot-alpha": "0.0.0",
103103
"@aws-cdk/aws-iotevents-alpha": "0.0.0",
104104
"@aws-cdk/aws-kinesisfirehose-alpha": "0.0.0",
105+
"@aws-cdk/aws-kinesisfirehose-destinations-alpha": "0.0.0",
105106
"aws-cdk-lib": "^0.0.0",
106107
"constructs": "^10.0.0"
107108
},

packages/@aws-cdk/aws-iot-actions-alpha/rosetta/default.ts-fixture

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// Fixture with packages imported, but nothing else
2-
import { Stack } from 'aws-cdk-lib';
2+
import { Stack, Duration } from 'aws-cdk-lib';
33
import { Construct } from 'constructs';
44
import * as actions from '@aws-cdk/aws-iot-actions-alpha';
55
import * as iot from '@aws-cdk/aws-iot-alpha';
66
import * as lambda from 'aws-cdk-lib/aws-lambda';
77
import * as s3 from 'aws-cdk-lib/aws-s3';
8+
import * as stepfunctions from 'aws-cdk-lib/aws-stepfunctions';
89

910
class Fixture extends Stack {
1011
constructor(scope: Construct, id: string) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"32.0.0"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "32.0.0",
3+
"testCases": {
4+
"state-machine-integtest/DefaultTest": {
5+
"stacks": [
6+
"test-step-functions-start-state-machine-action-stack"
7+
],
8+
"assertionStack": "state-machine-integtest/DefaultTest/DeployAssert",
9+
"assertionStackName": "statemachineintegtestDefaultTestDeployAssert1FAFBF55"
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
{
2+
"version": "32.0.0",
3+
"artifacts": {
4+
"test-step-functions-start-state-machine-action-stack.assets": {
5+
"type": "cdk:asset-manifest",
6+
"properties": {
7+
"file": "test-step-functions-start-state-machine-action-stack.assets.json",
8+
"requiresBootstrapStackVersion": 6,
9+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
10+
}
11+
},
12+
"test-step-functions-start-state-machine-action-stack": {
13+
"type": "aws:cloudformation:stack",
14+
"environment": "aws://unknown-account/unknown-region",
15+
"properties": {
16+
"templateFile": "test-step-functions-start-state-machine-action-stack.template.json",
17+
"validateOnSynth": false,
18+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
19+
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
20+
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab.json",
21+
"requiresBootstrapStackVersion": 6,
22+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
23+
"additionalDependencies": [
24+
"test-step-functions-start-state-machine-action-stack.assets"
25+
],
26+
"lookupRole": {
27+
"arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}",
28+
"requiresBootstrapStackVersion": 8,
29+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
30+
}
31+
},
32+
"dependencies": [
33+
"test-step-functions-start-state-machine-action-stack.assets"
34+
],
35+
"metadata": {
36+
"/test-step-functions-start-state-machine-action-stack/TopicRule/Resource": [
37+
{
38+
"type": "aws:cdk:logicalId",
39+
"data": "TopicRule40A4EA44"
40+
}
41+
],
42+
"/test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/Resource": [
43+
{
44+
"type": "aws:cdk:logicalId",
45+
"data": "TopicRuleTopicRuleActionRole246C4F77"
46+
}
47+
],
48+
"/test-step-functions-start-state-machine-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource": [
49+
{
50+
"type": "aws:cdk:logicalId",
51+
"data": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687"
52+
}
53+
],
54+
"/test-step-functions-start-state-machine-action-stack/SM/Role/Resource": [
55+
{
56+
"type": "aws:cdk:logicalId",
57+
"data": "SMRole49C19C48"
58+
}
59+
],
60+
"/test-step-functions-start-state-machine-action-stack/SM/Resource": [
61+
{
62+
"type": "aws:cdk:logicalId",
63+
"data": "SM934E715A"
64+
}
65+
],
66+
"/test-step-functions-start-state-machine-action-stack/BootstrapVersion": [
67+
{
68+
"type": "aws:cdk:logicalId",
69+
"data": "BootstrapVersion"
70+
}
71+
],
72+
"/test-step-functions-start-state-machine-action-stack/CheckBootstrapVersion": [
73+
{
74+
"type": "aws:cdk:logicalId",
75+
"data": "CheckBootstrapVersion"
76+
}
77+
]
78+
},
79+
"displayName": "test-step-functions-start-state-machine-action-stack"
80+
},
81+
"statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets": {
82+
"type": "cdk:asset-manifest",
83+
"properties": {
84+
"file": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets.json",
85+
"requiresBootstrapStackVersion": 6,
86+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
87+
}
88+
},
89+
"statemachineintegtestDefaultTestDeployAssert1FAFBF55": {
90+
"type": "aws:cloudformation:stack",
91+
"environment": "aws://unknown-account/unknown-region",
92+
"properties": {
93+
"templateFile": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json",
94+
"validateOnSynth": false,
95+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
96+
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
97+
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
98+
"requiresBootstrapStackVersion": 6,
99+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
100+
"additionalDependencies": [
101+
"statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets"
102+
],
103+
"lookupRole": {
104+
"arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}",
105+
"requiresBootstrapStackVersion": 8,
106+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
107+
}
108+
},
109+
"dependencies": [
110+
"statemachineintegtestDefaultTestDeployAssert1FAFBF55.assets"
111+
],
112+
"metadata": {
113+
"/state-machine-integtest/DefaultTest/DeployAssert/BootstrapVersion": [
114+
{
115+
"type": "aws:cdk:logicalId",
116+
"data": "BootstrapVersion"
117+
}
118+
],
119+
"/state-machine-integtest/DefaultTest/DeployAssert/CheckBootstrapVersion": [
120+
{
121+
"type": "aws:cdk:logicalId",
122+
"data": "CheckBootstrapVersion"
123+
}
124+
]
125+
},
126+
"displayName": "state-machine-integtest/DefaultTest/DeployAssert"
127+
},
128+
"Tree": {
129+
"type": "cdk:tree",
130+
"properties": {
131+
"file": "tree.json"
132+
}
133+
}
134+
}
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "32.0.0",
3+
"files": {
4+
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
5+
"source": {
6+
"path": "statemachineintegtestDefaultTestDeployAssert1FAFBF55.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"Parameters": {
3+
"BootstrapVersion": {
4+
"Type": "AWS::SSM::Parameter::Value<String>",
5+
"Default": "/cdk-bootstrap/hnb659fds/version",
6+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
7+
}
8+
},
9+
"Rules": {
10+
"CheckBootstrapVersion": {
11+
"Assertions": [
12+
{
13+
"Assert": {
14+
"Fn::Not": [
15+
{
16+
"Fn::Contains": [
17+
[
18+
"1",
19+
"2",
20+
"3",
21+
"4",
22+
"5"
23+
],
24+
{
25+
"Ref": "BootstrapVersion"
26+
}
27+
]
28+
}
29+
]
30+
},
31+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
32+
}
33+
]
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "32.0.0",
3+
"files": {
4+
"2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab": {
5+
"source": {
6+
"path": "test-step-functions-start-state-machine-action-stack.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "2a9785d0602d38f259eb92a63ba9ecfd1092a72904af9e5e265db24a5d7a78ab.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}

0 commit comments

Comments
 (0)