Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iot): add topic rule action for step functions state machine execution #21780

Closed
wants to merge 10 commits into from
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-iot-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Currently supported are:
- Publish messages on SNS topics
- Write messages into columns of DynamoDB
- Put messages IoT Events input
- Start the execution of a Step Functions state machine

## Republish a message to another MQTT topic

Expand Down Expand Up @@ -328,3 +329,30 @@ const topicRule = new iot.TopicRule(this, 'TopicRule', {
],
});
```

## Start the execution of a Step Functions state machine

The code snippet below creates an AWS IoT Rule that starts the execution of a Step Functions state machine:

```ts
import * as iam from '@aws-cdk/aws-iam';
import * as sfn from '@aws-cdk/aws-stepfunctions';

declare const role: iam.Role;

const stateMachine = new sfn.StateMachine(this, 'StateMachine', {
definition: new sfn.Pass(this, 'StartPass'),
});

const topicRule = new iot.TopicRule(this, 'TopicRule', {
sql: iot.IotSql.fromStringAsVer20160323(
"SELECT * FROM 'device/+/data",
),
actions: [
new actions.StepFunctionsAction(stateMachine, {
role: role, // optional property, defaults to a new role
executionNamePrefix: 'StateMachinePrefix', // optional property, defaults to nothing
}),
],
});
```
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iot-actions/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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-action';
62 changes: 62 additions & 0 deletions packages/@aws-cdk/aws-iot-actions/lib/step-functions-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as iam from '@aws-cdk/aws-iam';
import * as iot from '@aws-cdk/aws-iot';
import * as sfn from '@aws-cdk/aws-stepfunctions';
import { CommonActionProps } from './common-action-props';
import { singletonActionRole } from './private/role';

/**
* Configuration properties of an action for Step Functions
*/
export interface StepFunctionsActionProps extends CommonActionProps {
/**
* Specifies which prefix to append to the state machine execution.
*
* @default - no prefix
*/
readonly executionNamePrefix?: string;
}

/**
* The action to start the execution of a Step Functions state machine
*/
export class StepFunctionsAction implements iot.IAction {
private readonly role?: iam.IRole;
private readonly stateMachine: sfn.StateMachine;
private readonly executionNamePrefix?: string;

/**
* @param stateMachine The Step Functions state machine whose execution will be started.
* @param props Optional properties to not use default
*/
constructor(
stateMachine: sfn.StateMachine,
props: StepFunctionsActionProps = {},
) {
this.stateMachine = stateMachine;
this.role = props.role;
this.executionNamePrefix = props.executionNamePrefix;
}

/**
* @internal
*/
_bind(topicRule: iot.ITopicRule): iot.ActionConfig {
const role = this.role ?? singletonActionRole(topicRule);
role.addToPrincipalPolicy(
new iam.PolicyStatement({
actions: ['states:StartExecution'],
resources: [this.stateMachine.stateMachineArn],
}),
);

return {
configuration: {
stepFunctions: {
roleArn: role.roleArn,
stateMachineName: this.stateMachine.stateMachineName,
executionNamePrefix: this.executionNamePrefix,
},
},
};
}
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-iot-actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/aws-sns": "0.0.0",
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"case": "1.6.3",
"constructs": "^10.0.0"
Expand All @@ -118,6 +119,7 @@
"@aws-cdk/aws-s3": "0.0.0",
"@aws-cdk/aws-sns": "0.0.0",
"@aws-cdk/aws-sqs": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^10.0.0"
},
Expand All @@ -136,4 +138,4 @@
"tag": "next"
},
"private": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as iot from '@aws-cdk/aws-iot';
import * as sfn from '@aws-cdk/aws-stepfunctions';
import * as cdk from '@aws-cdk/core';
import * as actions from '../../lib';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I missed this before, but as a new test, this needs to use the IntegTest construct and then run it with the --update-on-failed flag.


const app = new cdk.App();

class TestStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const topicRule = new iot.TopicRule(this, 'TopicRule', {
sql: iot.IotSql.fromStringAsVer20160323("SELECT * FROM 'device/+/data'"),
});

const stateMachine = new sfn.StateMachine(this, 'StateMachine', {
definition: new sfn.Pass(this, 'StartPass'),
});

topicRule.addAction(new actions.StepFunctionsAction(stateMachine));
}
}

new TestStack(app, 'test-stack');
app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"20.0.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "20.0.0",
"testCases": {
"IntegrationTest/DefaultTest": {
"stacks": [
"test-stack"
],
"assertionStack": "IntegrationTest/DefaultTest/DeployAssert"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"test-stack": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "test-stack.template.json",
"validateOnSynth": false
},
"metadata": {
"/test-stack/TopicRule/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "TopicRule40A4EA44"
}
],
"/test-stack/TopicRule/TopicRuleActionRole/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "TopicRuleTopicRuleActionRole246C4F77"
}
],
"/test-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687"
}
],
"/test-stack/StateMachine/Role/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "StateMachineRoleB840431D"
}
],
"/test-stack/StateMachine/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "StateMachine2E01A3A5"
}
],
"/test-stack/Service-principalMap": [
{
"type": "aws:cdk:logicalId",
"data": "ServiceprincipalMap"
}
]
},
"displayName": "test-stack"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "20.0.0",
"files": {
"33ea9644d18ddb62bc89360ac3bad439c89f0f9665650f0548bd2f3106d02783": {
"source": {
"path": "test-stack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "33ea9644d18ddb62bc89360ac3bad439c89f0f9665650f0548bd2f3106d02783.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading