[AWS Step Functions] AWS Service API 呼び出しに使用する CallAwsService で、自動的に追加されるパーミッションについて確認してみました
1 はじめに
CX 事業本部のデリバリー部の平内(SIN)です。
AWS Step Functions(以下、Step Functions)では、ワークフローから 200 を超える AWS のサービス(9,000 を超えるアクション)を直接呼び出すことができます。
その他の AWS のサービスを呼び出す
そして、AWS CDK(以下、CDK) では、そのためのコンストラクタとしてCallAwsServiceが用意されています。
class CallAwsService (construct)
CallAwsServiceでは、コールする API に必要なパーミッショッも併せて生成してくれますが、今回は、その内容について確認してみました。
2 自動的に生成されるポリシー
S3 バケットから、get-objectで、ファイルをダウンロードするとしたら、以下のようなコードになります。
import * as cdk from "aws-cdk-lib"; import { Construct } from "constructs"; import { aws_stepfunctions, aws_stepfunctions_tasks } from "aws-cdk-lib"; export class SampleStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const bucketName = "work-2023-02-24"; const keyName = "sample.txt"; const getObject = new aws_stepfunctions_tasks.CallAwsService( this, "GetObject", { service: "s3", action: "getObject", parameters: { Bucket: bucketName, Key: keyName, }, iamResources: [`arn:aws:s3:::${bucketName}/*`], } ); new aws_stepfunctions.StateMachine(this, "StateMachine", { stateMachineName: "myStateMachine", definition: getObject, }); } }
上記を使用して作成された Step Functions を、AWS コンソールで見た様子です。
そして、ロールを確認してみると次のようになっています。
{ "Version": "2012-10-17", "Statement": [ { "Action": "s3:getObject", "Resource": "arn:aws:s3:::work-2023-02-24/*", "Effect": "Allow" } ] }
こちらは、CallAwsServiceの必須パラメータである、 service、action、iamResourcesから生成されています。
3 アクション名が、ポリシーと一致しない場合 (iamAction)
生成されるポリシーは、serviceとactionの組み合わせで決まりますが、その組み合わせで対応できない場合もあります。
例)ListObjectsV2 に必要なポリシーは、s3:ListBucket
参考:ListObjects なんていう S3 のアクションは存在しない
残念ながら、以下のコードで生成される StepFunctions は Access Denied となります。
const listObjectsV2 = new aws_stepfunctions_tasks.CallAwsService( this, "ListObjectsV2", { service: "s3", action: "listObjectsV2", parameters: { Bucket: bucketName, }, iamResources: [`arn:aws:s3:::${bucketName}`], } );
この原因は、生成されたポリシーが以下のようになっているためです。
{ "Version": "2012-10-17", "Statement": [ { "Action": "s3:listObjectsV2", "Resource": "arn:aws:s3:::work-2023-02-24", "Effect": "Allow" } ] }
対策としては、パラメータとしてのiamActionを追加します。
const listObjectsV2 = new aws_stepfunctions_tasks.CallAwsService( this, "ListObjectsV2", { service: "s3", action: "listObjectsV2", parameters: { Bucket: bucketName, }, iamResources: [`arn:aws:s3:::${bucketName}`], iamAction: "s3:ListBucket", // <= この行を追加 } );
iamActionが指定されると、生成されるポリシーは、iamActionとiamResourcesの組み合わせとなります。
Access Deniedが解消されていることが確認できます。
4 追加のポリシーが必要な場合(additionalIamStatements)
action、service、iamActionで対応できない場合、additionalIamStatementsを使用します。
例として・・・
rekognition:detectTextを使用すると、画像の中からテキストを検出することができます。
バケットに桜の画像を配置します。
以下のコードで StepFunctions を構成します。
const bucketName = "work-2023-02-24"; const keyName = "sample.jpg"; const detectText = new aws_stepfunctions_tasks.CallAwsService( this, "DetectText", { service: "rekognition", action: "detectText", iamResources: ["*"], parameters: { Image: { S3Object: { Bucket: bucketName, Name: keyName, }, }, }, additionalIamStatements: [ new aws_iam.PolicyStatement({ actions: ["s3:getObject"], resources: [`arn:aws:s3:::${bucketName}/*`], }), ], } );
実行すると、DetectedText に、Cherry blossomsが検出されていることが確認できます。
rekognition:detectTextでは、S3 バケットにアクセスするための追加のポリシーが必要ですが、これを追加しているのが、additionalIamStatementsです。
先のコードで生成されたポリシーは、以下のようになっています。
action及び、serviceの組み合わせに追加して、additionalIamStatementsに列挙したポリシーも追加されています。
additionalIamStatementsを追加しなかった場合は、S3 へアクセスできなかったことが原因で失敗します。
5 参考: GUI からの操作では、ポリシー追加はされない
AWS コンソールからステートマシンを作成する場合、GUI から簡単に操作できます。
しかし、この場合、AWS サービス API を使用するためのパーミッションは、別途設定する必要があります。
代わりに、xray に関するパーミッションが追加されていることが確認できます。
6 参考: CustomState では、ポリシー追加はされない
パラメータを全て JSON で設定できる、低レベルのコンストラクタであるCustomStateでも、同じように記述できますが、こちらは、ポリシーを自動的に追加することはありません。
class CustomState (construct)
const getObject = new aws_stepfunctions.CustomState(this, "GetObject", { stateJson: { Type: "Task", Resource: "arn:aws:states:::aws-sdk:s3:getObject";, Parameters: { Bucket: bucketName, Key: keyName, }, }, });
AWS サービス API を使用する場合、現時点で、CustomStateが必要になることはないと思います。
7 最後に
今回は、CallAwsService コンストラクで生成されるパーミションについて確認してみました。
複雑な処理になると、まだまだ Lambda での実装に軍配が上がりそうですが、2,3 の API サービスの組み合わせだけで要件が完了してしまうような場合、CallAwsServiceで簡単に書けることの魅力は捨て難いですよね。
[感謝] 桜の画像は、Pixabayのものを利用させて頂きました。 https://pixabay.com/ja/photos/%e3%83%94%e3%83%b3%e3%82%af-%e6%a1%9c-%e3%83%95%e3%83%a9%e3%83%af%e3%83%bc%e3%82%ba-%e6%94%af%e5%ba%97-324175/