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. 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 ## Resource constraints
| Data type | Component | Summary | Description | Type | Length | Pattern | Required | | 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),
},
);