
AWS Step Functionsでタスク毎に別アカウントのロールにAssumeRole(スイッチロール)出来るようになりました!
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
お疲れさまです。とーちです。
最近は AWS re:Invent 2022 の開催が近いせいかすごい数のアップデートが AWS から発表されてますね。
そんなアップデートの中でも個人的にオッと思ったアップデートである AWS Step Functions のクロスアカウントアクセスに関するアップデートをご紹介したいと思います。
かんたんまとめ
- ステートマシン内のタスク単位で別アカウントのロールに AssumeRole できるようになった
 - リソースベースポリシーをサポートしていない別アカウントのリソースでもステートマシンから呼び出せるようになった
 
今までのステートマシンでのクロスアカウントアクセスのやり方(リソースベースポリシー方式)
便宜上、この記事では今までの Step Functions でのクロスアカウントアクセスの方法をリソースベースポリシー方式と呼ぶことにします。
前提として以下のような構成があるとします。
今まで、アカウント A のステートマシンからアカウント B の Lambda を呼び出すには以下の方法しかありませんでした。
- アカウント B の Lambda のリソースベースポリシーを変更し、アカウント A のリソース(ステートマシンやステートマシンに付与する IAM ロール等)からの Lambda 呼び出しを許可する
 - アカウント A のステートマシンに付与する IAM ロールにアカウント B の Lambda を呼び出せる権限を付与する
 - ステートマシンの定義の中でアカウント B の Lambda を呼び出すタスクを定義する
 
上記の IAM 権限周りも含めて絵にすると以下のような感じです。

上記の通り、呼び出し先アカウントのサービスがリソースベースポリシーをサポートしていることが前提となっています。リソースベースポリシーは全ての AWS サービスでサポートされているわけではないので、例えば別アカウントにある DynamoDB をステートマシンから直接操作することは出来ないようになっていました。
今回のアップデートにより可能となったクロスアカウントアクセスのやり方(スイッチロール方式)
便宜上、この記事ではアップデートにより追加された Step Functions のクロスアカウントアクセスの方法をスイッチロール方式と呼ぶことにします。
今回のアップデートによりステートマシン内のタスク単位で別 IAM ロールへの AssumeRole(スイッチロール)が出来るようになりました。 そのため、以下のようなやり方でも別アカウントのサービスを呼び出せるようになりました。
- アカウント B にアカウント B の Lambda を呼び出す権限を持つ IAM ロール(以後 LambdaInvokeRole)を作成する
 - LambdaInvokeRole の信頼ポリシーにアカウント A のステートマシンのロール(以後 StateMRole)Arn を追加する
 - StateMRole に LambdaInvokeRole に"sts:AssumeRole"出来るポリシーを追加する
 - ステートマシンの定義の中でアカウント B の Lambda を呼び出すタスクを定義する。この際、新しく追加されたCredentialsというフィールドを使って、LambdaInvokeRole の Arn を指定する
 
やってみた
実際にステートマシンからスイッチロール方式でクロスアカウントアクセスを試してみました。 作成する構成は以下の通りです。
ステートマシンのフローはこんな感じにしました。テスト用なので意味のある処理ではないんですが、別アカウントで Lambda を動かした後、DynamoDB テーブルを作成するというフローになっています。 リソースベースポリシーを使えないサービスをステートマシンから動かしてみたくて DynamoDB を入れてみました。
ステートマシンの ASL(Amazon States Language)
ステートマシンの ASL は以下のようになります。
{
  "Comment": "A Hello World example demonstrating various state types of the Amazon States Language",
  "StartAt": "Lambda Invoke",
  "States": {
    "Lambda Invoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "OutputPath": "$.Payload",
      "Parameters": {
        "Payload.$": "$",
        "FunctionName": "arn:aws:lambda:ap-northeast-1:<アカウントBのID>:function:hello-world"
      },
      "Credentials": {
        "RoleArn": "arn:aws:iam::<アカウントBのID>:role/LambdaInvokeRole"
      },
      "Next": "CreateTable"
    },
    "CreateTable": {
      "Type": "Task",
      "End": true,
      "Parameters": {
        "AttributeDefinitions": [
          {
            "AttributeName": "Name",
            "AttributeType": "S"
          }
        ],
        "KeySchema": [
          {
            "AttributeName": "Name",
            "KeyType": "HASH"
          }
        ],
        "TableName": "MyData",
        "BillingMode": "PAY_PER_REQUEST"
      },
      "Resource": "arn:aws:states:::aws-sdk:dynamodb:createTable",
      "Credentials": {
        "RoleArn": "arn:aws:iam::<アカウントBのID>:role/DynamoRole"
      }
    }
  }
}
ポイントとしては 13 行目と 38 行目の Credentials という箇所です。このフィールドが今回のアップデートで追加されていまして、タスク毎にそのタスクを実行する権限をもった IAM ロールを指定出来るようになっています。
また、各 IAM ロールの設定は以下の通りとなります。ポイントとなる箇所だけ抜粋してみました。
ステートマシンにつける IAM ロール(StateMRole) 関連
ステートマシンにつける IAM ロールに付与したポリシーは以下の通りです。別アカウントのロールに AssumeRole する権限のみがついています。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::<アカウントBのID>:role/LambdaInvokeRole",
                "arn:aws:iam::<アカウントBのID>:role/DynamoRole"
            ]
        }
    ]
}
Lambda 実行のための IAM ロール(LambdaInvokeRole) 関連
LambdaInvokeRole は ステートマシンにつけた IAM ロールから AssumeRole した上で使用します。IAM ポリシーとしてはアカウント B 上の Lambda を Invoke する権限のみがついています。
{
    "Version": "2012-10-17",
    "Id": "default",
    "Statement": [
        {
            "Sid": "myid01",
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:ap-northeast-1:<アカウントBのID>:function:hello-world"
        }
    ]
}
また、信頼ポリシーは以下の通りとなっています。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<アカウントAのID>:role/service-role/StepFunctions-HelloWorld-role-5bddc095"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": "arn:aws:states:ap-northeast-1:<アカウントAのID>:stateMachine:HelloWorld"
                }
            }
        }
    ]
}
ここのポイントとしては、Principal としてステートマシンに付与したロールを信頼していることと、追加で、Condition により更に特定のステートマシンからしか AssumeRole 出来ないようになっていることです。
この Condition の記載はステートマシンから別アカウントのサービスを呼び出すという点においては必須ではないのですが、「混乱した代理問題」を回避するために入れることを推奨されています。
「混乱した代理問題」については弊社下記ブログをご参照ください。
DynamoDB テーブル作成のための IAM ロール(DynamoRole) 関連
DynamoRole は ステートマシンにつけた IAM ロールから AssumeRole した上で使用します。IAM ポリシーとしてはアカウント B 上で DynamoDB テーブルを作成できる権限のみがついています。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "dynamodb:CreateTable",
            "Resource": "arn:aws:dynamodb:*:<アカウントBのID>:table/MyData"
        }
    ]
}
DynamoRole の信頼ポリシーは LambdaInvokeRole の信頼ポリシーとまったく同じなので割愛します
実際に動かしてみる
上記の設定で、アカウント A 上のステートマシンを動かしてみました。
ちゃんと動きましたね。別アカウント上で DynamoDB テーブルが作成されたことと Lambda が正常に終了していることも確認できました。
各タスクの結果も以下のように表示されています。別アカウント上の DynamoDB テーブルを作りましたが TableArn もちゃんと出力として返ってきていることが分かります。
どちらのクロスアカウントアクセス方法を使うのがいいのか
ステートマシンから起動する別アカウントのサービスがリソースベースポリシーをサポートしていないものである場合は、スイッチロール方式でいいと思います。
それ以外の場合はどちらが優れているというものでもないので、ケースバイケースかと思います。やりやすい方法でやるのが良いでしょう。
さいごに
AWS Step Functions のクロスアカウントアクセスに関するアップデートのご紹介でした。
Step Functions はどんどん便利になっていて頼もしいですね。今後も使用する場面は増えてきそうな予感なので、
アップデートを見逃さないようにしたいところです。
以上、とーちでした。












