feat: step functions example

This commit is contained in:
Michele Cereda
2025-11-11 11:36:19 +01:00
parent 2989062960
commit 5af7c75803
3 changed files with 357 additions and 0 deletions

View File

@@ -636,6 +636,52 @@ Such roles need to allow being assumed by the `states.amazonaws.com` Principal.
If wanting to send logs to CloudWatch, the execution role must be able to access the log group.
<details style='padding: 0 0 1rem 1rem'>
<summary>Example: get an RDS DB instance's information and pass it as a specific attribute
```json
{
"Comment": "Get an RDS DB instance's information and pass it as a specific attribute",
"StartAt": "Get RDS DB Instance information",
"States": {
"Get RDS DB Instance information": {
"Type": "Task",
"Arguments": {
"DbInstanceIdentifier": "some-rds-db-instance-identifier"
},
"Resource": "arn:aws:states:::aws-sdk:rds:describeDBInstances",
"End": true,
"Output": {
"RdsDbInstanceInformation": "{% $states.result.DbInstances[0] %}"
}
}
},
"QueryLanguage": "JSONata"
}
```
</details>
Unless one knows exactly what one is doing, prefer setting arguments from the service's Console to have _some_ level of
suggestions.
<details>
<summary>FIXME - using variables in DescribeDBSnapshots</summary>
```json
{
"DbInstanceIdentifier": "{% $states.input.ProdDBInstanceInfo.DbInstanceIdentifier %}"
}
```
```json
{
"AvailableDBSnapshots": "{% $states.result.DbSnapshots[Status='available'] %}"
}
```
</details>
## Resource constraints
| Data type | Component | Summary | Description | Type | Length | Pattern | Required |

92
knowledge base/jsonata.md Normal file
View File

@@ -0,0 +1,92 @@
# JSONata
> TODO
JSON query and transformation language.
<!-- Remove this line to uncomment if used
## Table of contents <!-- omit in toc -->
1. [TL;DR](#tldr)
1. [Further readings](#further-readings)
1. [Sources](#sources)
## TL;DR
```plaintext
# Concatenate strings
someAttribute & 'someSuffix'
'somePrefix' & someAttribute
'somePrefix' & someAttribute & 'someSuffix'
# Join strings
$join(['somePrefix', someAttribute, 'someSuffix'], '-')
# Filter array of objects by attribute's value
users[role = "admin"]
users[role = "admin" and name = "Alice"].name
# Filter events with timestamp value in the last week
events[$toMillis(timestamp) >= $toMillis($now()) - (60 * 60 * 7 * 24 * 1000)]
# Get a random value between 0 included and 1 excluded (0<=X<1)
$random()
# Get a random object from a list
# Lists are 0-indexed
users[$floor($random()*$count($users))]
```
<!-- Uncomment if used
<details>
<summary>Setup</summary>
```sh
```
</details>
-->
<!-- Uncomment if used
<details>
<summary>Usage</summary>
```sh
```
</details>
-->
<!-- Uncomment if used
<details>
<summary>Real world use cases</summary>
```sh
```
</details>
-->
## Further readings
- [Website]
- [Codebase]
### Sources
- [Documentation]
<!--
Reference
═╬═Time══
-->
<!-- In-article sections -->
<!-- Knowledge base -->
<!-- Files -->
<!-- Upstream -->
[Codebase]: https://github.com/jsonata-js/jsonata
[Documentation]: https://docs.jsonata.org/
[Website]: https://jsonata.org/
<!-- Others -->

View File

@@ -0,0 +1,219 @@
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const iamRole: pulumi.Output<aws.iam.GetRoleResult> = aws.iam.getRoleOutput({
name: 'SomeServiceRole',
});
// -----------------
/**
* Base type for Step Function states.
*
* States are elements in a state machine.\
* A state is referred to by its name, which can be any string, but which must be unique within the scope of the entire
* state machine.
*
* States take input from their own invocation or a previous state.\
* States can filter their input and manipulate the output that is sent to the next state.
*
* Refer <https://docs.aws.amazon.com/step-functions/latest/dg/workflow-states.html>.
*/
type StateMachineBaseState = {
Type: string;
Comment?: string;
Next?: string;
End?: boolean;
};
/**
* Choice state.\
* Enables conditional branching.
*/
interface StateMachineChoiceState extends StateMachineBaseState {
Type: "Choice";
Choices: Record<string, any>[];
Default: string;
};
/**
* Branch type for Parallel states.\
* Itself a smaller state machine.
*/
type StateMachineParallelBranch = {
StartAt: string;
States: Record<string, StateMachineStepState>;
}
/**
* Parallel state.\
* Runs other step states in parallel.
*/
interface StateMachineParallelState extends StateMachineBaseState {
Type: "Parallel";
Branches: StateMachineParallelBranch[];
};
/**
* Task state.\
* Runs a service integration or Lambda function.
*/
interface StateMachineTaskState extends StateMachineBaseState {
Type: "Task";
Resource: string;
Arguments?: Record<string, any>;
Output?: Record<string, any>;
};
/**
* Wait state.\
* Pauses execution for a fixed duration or until a specified time or date.
*/
interface StateMachineWaitState extends StateMachineBaseState {
Type: "Wait";
Seconds?: number;
Timestamp?: string;
};
/**
* Union type for Step Function states.
*/
type StateMachineStepState =
| StateMachineChoiceState
| StateMachineParallelState
| StateMachineTaskState
| StateMachineWaitState;
/**
* Generic State Machine definition.
*/
interface StateMachineDefinition {
Comment?: string;
QueryLanguage?: string;
StartAt: string;
States: Record<string, StateMachineStepState>;
};
// -----------------
const changeClonedDbInstancePassword: StateMachineTaskState = {
Type: "Task",
Resource: "arn:aws:states:::aws-sdk:rds:modifyDBInstance",
Arguments: {
DbInstanceIdentifier: "{% $states.input.ClonedDBInstance.DbInstanceIdentifier %}",
MasterUserPassword: "some-Secur3-Password",
ApplyImmediately: true,
},
End: true,
};
const checkClonedDbInstanceIsAvailable: StateMachineChoiceState = {
Type: "Choice",
Choices: [
{
Condition: "{% $states.input.ClonedDBInstance.DBInstanceStatus in ['available'] %}",
Next: "ChangeClonedDBInstancePassword",
},
],
Default: "WaitForClonedDBInstanceNextCheck",
};
const createClonedDbInstance: StateMachineTaskState = {
Type: "Task",
Resource: "arn:aws:states:::aws-sdk:rds:restoreDBInstanceToPointInTime",
Arguments: {
SourceDBInstanceIdentifier: "{% $states.input.ExistingDBInstanceInfo.DbInstanceIdentifier %}",
UseLatestRestorableTime: true,
TargetDBInstanceIdentifier: "{% $join([$states.input.ExistingDBInstanceInfo.DbInstanceIdentifier, 'clone'], '-') %}",
Engine: "postgres",
MultiAZ: false,
AvailabilityZone: "eu-west-1a",
DbSubnetGroupName: "default",
PubliclyAccessible: false,
VpcSecurityGroupIds: [],
DbParameterGroupName: "{% $states.input.ExistingDBInstanceInfo.DbParameterGroups[0].DbParameterGroupName %}",
OptionGroupName: "{% $states.input.ExistingDBInstanceInfo.OptionGroupMemberships[0].OptionGroupName %}",
StorageType: "gp3",
DedicatedLogVolume: false,
DbInstanceClass: "db.t4g.medium",
DeletionProtection: false,
AutoMinorVersionUpgrade: false,
},
Output: {
ClonedDBInstance: "{% $states.result.DbInstance %}",
},
Next: "GetClonedDBInstanceState",
};
const getClonedDbInstanceState: StateMachineTaskState = {
Type: "Task",
Resource: "arn:aws:states:::aws-sdk:rds:describeDBInstances",
Arguments: {
DbInstanceIdentifier: "{% $states.input.ClonedDBInstance.DbInstanceIdentifier %}",
},
Output: {
ClonedDBInstance: "{% $states.result.DbInstances[0] %}",
},
Next: "CheckClonedDBInstanceIsAvailable",
};
const getExistingDbInstanceInfo: StateMachineTaskState = {
Type: "Task",
Resource: "arn:aws:states:::aws-sdk:rds:describeDBInstances",
Arguments: {
DbInstanceIdentifier: "some-existing-rds-instance",
},
Output: {
ExistingDBInstanceInfo: "{% $states.result.DbInstances[0] %}",
},
Next: "ParallelZone",
};
const waitForClonedDbInstanceNextCheck: StateMachineWaitState = {
Type: "Wait",
Seconds: 60,
Next: "GetClonedDBInstanceState",
};
const parallelZone: StateMachineParallelState = {
Type: "Parallel",
Branches: [
{
StartAt: "CreateClonedDBInstance",
States: {
CreateClonedDBInstance: createClonedDbInstance,
GetClonedDBInstanceState: getClonedDbInstanceState,
CheckClonedDBInstanceIsAvailable: checkClonedDbInstanceIsAvailable,
WaitForClonedDBInstanceNextCheck: waitForClonedDbInstanceNextCheck,
ChangeClonedDBInstancePassword: changeClonedDbInstancePassword,
},
},
// FIXME: another branch here
],
End: true,
};
const stateMachineDefinition: StateMachineDefinition = {
QueryLanguage: "JSONata",
States: {
GetExistingDBInstanceInfo: getExistingDbInstanceInfo,
ParallelZone: parallelZone,
},
StartAt: "GetExistingDBInstanceInfo",
};
// pulumi.jsonStringify(stateMachineDefinition).apply(s => console.log(s))
const dbCloner_stateMachine: aws.sfn.StateMachine = new aws.sfn.StateMachine(
'dbCloner',
{
name: 'DBCloner',
roleArn: iamRole.arn,
loggingConfiguration: {
level: "OFF",
},
encryptionConfiguration: {
type: "AWS_OWNED_KEY",
},
publish: false,
definition: pulumi.jsonStringify(stateMachineDefinition),
},
);