👍

Step FunctionsでMapを使用して配列を順次ループする

2024/10/13に公開

AWS Step Functionsでループ処理は簡単に書けないものと思っていましたが、Mapを使用して順番にループ処理することができました。Mapステートは「動的並列処理」のために使用されるステートですが、直列(?)の反復処理にも普通に使えました

簡単なサンプル

入力としてJSON配列を受け取り、要素をループし、それぞれ10秒待つだけの簡単なステートマシンを作成してみます。

CloudFormationテンプレート

配列を1つずつ処理するには、Mapステートで"MaxConcurrency": 1と指定します。

AWSTemplateFormatVersion: 2010-09-09
Description: Create a simple loop state machine

Resources:
  LoopMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: LoopMachine
      StateMachineType: STANDARD
      RoleArn: !GetAtt LoopMachineRole.Arn
      LoggingConfiguration:
        Level: 'OFF'
      TracingConfiguration:
        Enabled: false
      EncryptionConfiguration:
        Type: AWS_OWNED_KEY
      Definition:
        {
          "Comment": "A simple loop state machine",
          "StartAt": "IterateJsonArray",
          "States": {
            "IterateJsonArray": {
              "Type": "Map",
              "ItemProcessor": {
                "ProcessorConfig": {
                  "Mode": "INLINE"
                },
                "StartAt": "Wait10Seconds",
                "States": {
                  "Wait10Seconds": {
                    "Type": "Wait",
                    "Seconds": 10,
                    "End": true
                  }
                }
              },
              "End": true,
              "MaxConcurrency": 1
            }
          }
        }

  LoopMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - states.amazonaws.com
            Action:
              - sts:AssumeRole

実行結果

以下の要素数4の配列を入力として実行してみます。

[1, "two", {"number": 3}, "四"]

実行結果は以下のとおりとなりました。配列の順番どおりに1つずつ実行され、各イテレーションで10秒ずつ待つため全体で40秒掛かったことが確認できました。

少し実践的なサンプル

より実践的な例を次に示します。

最初のステートで起動中のEC2インスタンス一覧を取得し、それをループ処理して各インスタンスを停止してみます。

CloudFormationテンプレート

AWSTemplateFormatVersion: 2010-09-09
Description: Create a simple loop state machine

Resources:
  LoopMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: LoopMachine
      StateMachineType: STANDARD
      RoleArn: !GetAtt LoopMachineRole.Arn
      LoggingConfiguration:
        Level: 'OFF'
      TracingConfiguration:
        Enabled: false
      EncryptionConfiguration:
        Type: AWS_OWNED_KEY
      Definition:
        {
          "Comment": "A simple loop state machine",
          "StartAt": "DescribeInstances",
          "States": {
            "DescribeInstances": {
              "Type": "Task",
              "Resource": "arn:aws:states:::aws-sdk:ec2:describeInstances",
              "Parameters": {
                "Filters": [
                  {
                    "Name": "instance-state-name",
                    "Values": [
                      "running"
                    ]
                  }
                ]
              },
              "OutputPath": "$.Reservations[*].Instances[*]",
              "Next": "ForInstanceInInstances"
            },
            "ForInstanceInInstances": {
              "Type": "Map",
              "ItemProcessor": {
                "ProcessorConfig": {
                  "Mode": "INLINE"
                },
                "StartAt": "StopInstance",
                "States": {
                  "StopInstance": {
                    "Type": "Task",
                    "Resource": "arn:aws:states:::aws-sdk:ec2:stopInstances",
                    "Parameters": {
                      "InstanceIds.$": "States.Array($.InstanceId)"
                    },
                    "End": true
                  }
                }
              },
              "End": true,
              "MaxConcurrency": 1
            }
          }
        }

  LoopMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - states.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: EC2AccessPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ec2:StopInstances
                Resource:
                  - '*'

解説(メモ)

  • ParametersFiltersInstanceIds:
  • "OutputPath": "$.Reservations[*].Instances[*]":
    • describeInstancesアクションで以下のような入れ子の配列が返されるため、これを平坦化します。

      {
        "Reservations": [
          {
            "Instances": [
              ...
            ],
            ...
          },
          ...
        ]
      }
      
    • 該当するインスタンスがないときはタスク結果が{"Reservations": []}になりますが、その場合でもエラーにならず[]という期待する出力が得られました。

  • "InstanceIds.$": "States.Array($.InstanceId)":

実行結果

省略。

想像どおり、起動中のインスタンスがすべて停止されました。

次のステップ

本記事で示した例では、簡単のためリトライやエラー処理を省略しています。例えば、ループの途中で失敗があった場合、既定ではその時点でステートマシン全体が失敗となるため、これを防ぎたい場合はエラーのキャッチを実装する必要があります。

Discussion