複数ステップの注文ワークフローを構築する

AWSBeginner
オンラインで実践に進む

はじめに

出荷処理では、注文を保存してから、完了した注文のサマリーを読み取る必要があります。これらのタスクをワークフローで接続し、実行履歴を実際の業務結果と比較します。

先に Lambda から DynamoDB を読み書きする と、その前提となる IAM ロールとログのラボを完了してください。この独立した VM は、ワーカーとテーブルを提供します。ワークフローは自分で作成します。EventBridge のルーティングは別の学習経路です。

認定試験との関連

このラボでは、次の試験トピックに関連する実践的な演習を行います。

ワークフローによるワーカー呼び出しを認可する

このステップでは、提供された業務ワーカーを確認し、Step Functions 用の独立した実行ロールを作成します。

ワークフローとワーカーの権限

ワークフローロールは関数を呼び出します。独立した Lambda ロールがテーブル操作を実行します。

Terminal の隣で AWS View を使い、CLI クエリをこのラボの実際のリソースと結果と比較します。提供された参照データを保持してください。

ワークフローはタスクと判断を接続します。ステートマシンはそれらの状態を定義し、実行は特定の入力でその定義を動かします。この新しい VM は、ワーカー関数と独立した注文・診断・参照テーブルを提供します。ステートマシンやワークフローロールはまだありません。ワーカーは注文を受け取り、数量と合計を書き込み、後でサマリーを読み取れます。その Lambda 実行ロールは、ワークフロー自体の操作とは別の、これらのテーブル操作をすでに認可しています。

プロジェクトディレクトリから始めます。シェルの代入は返された識別子を保存します。--query はレスポンスフィールドを選択し、--output text は再利用可能な文字列を生成します。

cd /home/labex/project
WORKER_NAME=labex-ev03-worker
WORKER_ARN=$(aws lambda get-function-configuration \
  --function-name labex-ev03-worker \
  --query FunctionArn \
  --output text)
aws lambda get-function-configuration \
  --function-name labex-ev03-worker \
  --query '{Name:FunctionName,Role:Role,Runtime:Runtime,Timeout:Timeout}'
aws stepfunctions list-state-machines

ワーカーは Python3.12 と独自の Lambda ロールを使い、ステートマシン一覧は空です。Step Functions には自身の実行ロールが必要です。信頼ポリシーは Step Functions サービスによるそのロールの引き受けを許可し、権限ポリシーは、生成されたセッションがこのワーカーだけを呼び出すことを許可します。引用符付きのヒアドキュメントは JSON をそのまま書き込み、file:// はそれをリクエストに読み込みます。

cat > workflow-trust.json <<'JSON'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "states.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
JSON
ROLE_ARN=$(aws iam create-role \
  --role-name labex-ev03-workflow-role \
  --assume-role-policy-document file://workflow-trust.json \
  --query Role.Arn \
  --output text)

通常の権限ドキュメントを書き込みます。シェルは $WORKER_ARN を挿入し、この権限付与を提供されたワーカーに限定します。

cat > workflow-invoke.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "$WORKER_ARN"
    }
  ]
}
EOF
aws iam put-role-policy \
  --role-name labex-ev03-workflow-role \
  --policy-name InvokeWorker \
  --policy-document file://workflow-invoke.json
aws iam get-role-policy --role-name labex-ev03-workflow-role --policy-name InvokeWorker

ポリシーには 1 つの正確な関数 ARN があります。Step Functions は、ワーカーの DynamoDB 権限を受け取りません。ワーカーは、独立した Lambda ロールを使ってそれらの呼び出しを行います。認可の検証を実行してください。

判断と 2 つの実際のタスクを定義する

このステップでは、数量を検証し、注文を保存して、実際のサマリーを読み取る注文ワークフローを定義します。

入力、保存結果、サマリー

StoreOrder は実際のペイロードを saved.result に保存します。ReadSummary はその注文 ID を選択し、完了したサマリーを読み取ります。

Amazon States Language(ASL) は、JSON のステートマシン定義です。StartAt は最初の状態を選択します。Choice は、条件が一致するとその分岐を進み、それ以外は Default を進みます。Task はサービスを呼び出します。Next は状態を接続し、End: true は成功として終了し、Fail はエラーで終了します。

最適化された Lambda タスク統合は、arn:aws:states:::lambda:invoke を使います。Parameters は関数引数を提供します。.$ で終わるキーは JSONPath を評価し、$ は現在の入力全体を選択します。Lambda 統合は、メタデータと Payload を返します。ResultSelector はそのペイロードだけを保持し、ResultPath は元の注文入力を保持しながら saved の下へ挿入します。次のタスクは、保存された注文 ID を選択します。その OutputPath は、実際のサマリーペイロードだけを返します。

定義をそのまま書き込み、その後 jq --arg でワーカーのプレースホルダーを置き換えます。

cat > workflow-template.json <<'JSON'
{
  "StartAt": "CheckQuantity",
  "States": {
    "CheckQuantity": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.quantity",
          "NumericGreaterThan": 0,
          "Next": "StoreOrder"
        }
      ],
      "Default": "Rejected"
    },
    "StoreOrder": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "WORKER_NAME",
        "Payload.$": "$"
      },
      "ResultSelector": {
        "result.$": "$.Payload"
      },
      "ResultPath": "$.saved",
      "Next": "ReadSummary"
    },
    "ReadSummary": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "WORKER_NAME",
        "Payload": {
          "stage": "summary",
          "id.$": "$.saved.result.id"
        }
      },
      "OutputPath": "$.Payload",
      "End": true
    },
    "Rejected": {
      "Type": "Fail",
      "Error": "OrderRejected",
      "Cause": "Quantity must be positive"
    }
  }
}
JSON
jq --arg worker "$WORKER_NAME" '.States.StoreOrder.Parameters.FunctionName=$worker | .States.ReadSummary.Parameters.FunctionName=$worker' workflow-template.json > workflow.json
MACHINE_ARN=$(aws stepfunctions create-state-machine \
  --name labex-ev03-orders \
  --type STANDARD \
  --role-arn "$ROLE_ARN" \
  --definition file://workflow.json \
  --query stateMachineArn \
  --output text)
aws stepfunctions describe-state-machine \
  --state-machine-arn "$MACHINE_ARN" \
  --query '{Name:name,Role:roleArn,Definition:definition}'

レスポンスはステートマシンとワークフローロールを示し、定義は両方のタスクで提供されたワーカーを指定します。注文はまだ処理されていません。Terminal の隣の AWS View をクリックし、実際のステートマシン定義と空の実行・注文一覧を確認します。定義の検証を実行してください。

業務結果と拒否される実行を確認する

このステップでは、有効な注文、無効な数量、呼び出し権限のない実行を試します。

start-execution は非同期作業を開始し、実行 ARN を返します。以下の回数を限定したシェルループは、実行中でなくなるまで 2 秒ごとに状態を読み取ります。$(...) はコマンド出力を取得し、seq はループ回数を提供し、break は条件が一致したときにループを終了します。ループ後も RUNNING の場合は、先へ進む前にサービスを確認してください。

EXECUTION_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name valid-order \
  --input '{"id":"workflow-order","quantity":3}' \
  --query executionArn \
  --output text)
for attempt in $(seq 1 60); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$EXECUTION_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done
aws stepfunctions describe-execution \
  --execution-arn "$EXECUTION_ARN" \
  --query '{Status:status,Output:output}'
aws stepfunctions get-execution-history \
  --execution-arn "$EXECUTION_ARN" \
  --query 'events[].type'
aws dynamodb get-item \
  --table-name labex-ev03-orders \
  --key '{"id":{"S":"workflow-order"}}' \
  --query Item

Step Functions 公式コンソールの実行一覧例

公式コンソールは、実行名と状態を時間情報とともに表示します。その名前とワークフローは、このラボの Standard ステートマシンとは異なります。自分の CLI 状態を履歴と保存データと比較し、このラボの結果には AWS View を使ってください。

出典:AWS Step Functions。

状態は SUCCEEDED で、出力には注文 ID、total_cents:850、completed:true が含まれます。実際の DynamoDB アイテムは数量 3、合計 850 です。履歴には、注文保存とサマリー読み取りの 2 つの TaskSucceeded イベントがあります。実行状態だけでは業務結果を証明できません。両方を比較してください。

数量 0 で Choice 分岐をテストします。

INVALID_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name invalid-order \
  --input '{"id":"invalid-order","quantity":0}' \
  --query executionArn \
  --output text)
for attempt in $(seq 1 30); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$INVALID_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done
aws stepfunctions describe-execution \
  --execution-arn "$INVALID_ARN" \
  --query '{Status:status,Error:error}'
aws dynamodb get-item \
  --table-name labex-ev03-orders \
  --key '{"id":{"S":"invalid-order"}}' \
  --query Item

状態は OrderRejected を伴う FAILED で、invalid-order アイテムはありません。Choice がワーカー呼び出し前に拒否しました。

次に、ワークフローの Invoke ポリシーだけを削除します。ワーカーのロールとデータ権限は独立したままです。操作担当者には引き続き実行開始が許可されていますが、ワークフローロールはタスクを呼び出せません。

aws iam delete-role-policy --role-name labex-ev03-workflow-role --policy-name InvokeWorker
DENIED_ARN=$(aws stepfunctions start-execution \
  --state-machine-arn "$MACHINE_ARN" \
  --name denied-order \
  --input '{"id":"denied-order","quantity":2}' \
  --query executionArn \
  --output text)
for attempt in $(seq 1 30); do
  STATUS=$(aws stepfunctions describe-execution \
    --execution-arn "$DENIED_ARN" \
    --query status \
    --output text)
  if test "$STATUS" != RUNNING; then break; fi
  sleep 2
done
aws stepfunctions describe-execution \
  --execution-arn "$DENIED_ARN" \
  --query '{Status:status,Error:error}'
aws dynamodb get-item \
  --table-name labex-ev03-orders \
  --key '{"id":{"S":"denied-order"}}' \
  --query Item
aws iam put-role-policy \
  --role-name labex-ev03-workflow-role \
  --policy-name InvokeWorker \
  --policy-document file://workflow-invoke.json

拒否された実行は、呼び出し時にアクセス拒否エラーで失敗します。注文は作成せず、ワーカータスクも実行しません。意図するポリシーは復元されます。AWS View には、実際の成功したサマリーと両方の失敗が、1 件の保存された注文の隣に表示されます。

以下の例は、実際の成功したサマリー、拒否された実行、1 件の業務注文を示します。

AWS View に実際のワークフローサマリーと失敗した実行が表示される

実行の検証を行ってください。

ワークフローリソースと合成結果を削除する

このステップでは、提供された準備リソースを保持しながら、自分の完了したステートマシン、ワークフローロール、注文、ログを削除します。

3 つの実行はすべて終了しています。ステートマシンの削除は、アクティブなステートマシン一覧からそれを取り除きます。ロールを削除する前に所有するロールポリシーを削除し、その後、合成注文と実行によって作成されたワーカーロググループを削除します。

aws stepfunctions delete-state-machine --state-machine-arn "$MACHINE_ARN"
aws iam delete-role-policy --role-name labex-ev03-workflow-role --policy-name InvokeWorker
aws iam delete-role --role-name labex-ev03-workflow-role
aws dynamodb delete-item \
  --table-name labex-ev03-orders \
  --key '{"id":{"S":"workflow-order"}}'
aws dynamodb delete-item \
  --table-name labex-ev03-attempts \
  --key '{"id":{"S":"workflow-order"}}'
aws logs delete-log-group --log-group-name /aws/lambda/labex-ev03-worker

成功する一覧取得で、残るものを証明します。

aws stepfunctions list-state-machines
aws iam list-roles --query 'Roles[].RoleName'
aws dynamodb scan --table-name labex-ev03-orders --query Items
aws logs describe-log-groups --query logGroups
aws dynamodb scan --table-name labex-ev03-reference --query Items

アクティブなステートマシン、注文、ロググループはありません。提供されたワーカーロールだけが残り、参照アイテムは変わりません。提供されたワーカーとテーブルは保持してください。準備時の所有範囲は、自分が作成したワークフローやリソースと異なります。ネットワークエラーや認証エラーは削除の証拠になりません。

このラボで作成した通常のファイルを削除します。

rm -f workflow-trust.json workflow-invoke.json workflow-template.json workflow.json

AWS View には、空のステートマシン・実行・注文と、保持された参照リソースが表示されます。VM を終了する前に、後片付けの検証を実行してください。

まとめ

範囲を限定したワークフロー実行ロールを作成し、Choice と 2 つの実際の Lambda タスクを接続して、実際の結果を次のタスクへ渡し、実行履歴を業務データと比較しました。無効な入力と呼び出し権限の不足では、注文は生成されませんでした。提供された準備リソースを保持しながら、所有するワークフローリソースと合成結果を削除しました。

次のユニットでは、一時的な失敗の再試行と、永続的なエラーの明示的な処理を追加します。