前提(困ったこと)
プロダクト開発でAWSのサービスを中心に組み上げていく中で、
・API Gateway
・StepFunctions
・Lambda
の3つを使うことになったが、
API Gateway + StepFunctions
と
API Gateway + Lambda
で微妙にcontextの内容が違ったので難儀したケースがあった。
使用サービス
・Lambda
・StepFunctions
・API Gateway
・Keycloak(AWS外)
どうしてこうなった
最初はLambda中心に開発しており、StepFunctionsもいつかは使おうと考えてはいつつも後回しにしていた。
ある程度落ち着いたタイミングで、StepFunctionsを使ってこれまで作ったLambdaを組み合わせて実装を進めていたが、想定していた挙動にならない。
具体的にはAPI GatewayのAuthorizerにLambdaを使って、そのLambda内で外部のKeycloakを呼んでcontextのauthorizerに含めるという仕組みにしていたのだが、これをStepFunctionsに渡るとauthorizerの内容が消えてしまうというものだった。
"requestContext": {
"context": "{resourceId=4blf96, authorizer=, resourcePath=/xxxxx/xxxxx, httpMethod=POST, extendedRequestId=xxxxxxx, requestTime=07/Feb/2024:03:32:07 +0000, path=/xxxxx/xxxxx, protocol=HTTP/1.1, requestOverride=,}",
(authorizerの値に何も入らない)
そもそもStepFunctionsはcontextは取りづらい仕様(取ることを想定してない?)みたいだったので、いくつかの制約があるのは理解していた。ただ、カスタムしたcontextが完全に消されるとは思っていなかった。
どう対処したか
API Gatewayからcontextのauthorizerが取れないなら、そもそも取れるLambdaで取った方が速い。
ということで
API Gateway
> コントロール用Lambda
> boto3でStepFunctions呼び出し
> 実際に動かしたいLambda(StepFunctionsで呼ばれる)
に変更し、StepFunctionsの引数にauthorizerを編集して渡すことにした。
Lambdaのコードに書くと以下のようになる。
client = boto3.client('stepfunctions')
response = client.start_execution(
stateMachineArn = <実行するStepFunctionsのARN>,
input = json.dumps(input_json) # 入力データ
)
input_jsonにはLambdaのcontextに含まれるauthorizerを編集して設定。
引数のinputには文字列を渡す必要があるのでjson.dumpsを挟む。
StepFunctionsのARNはSAMのtemplate.yamlに書いてあるのでそこから取得。
振り返り
boto3からのStepFunctionsの実行自体は通常の仕様としては問題ないが、果たしてこういう実装でいいのかというものは残る。
当初はStepFunctionsを使わない想定でLambdaを組んでいたので、既存資産の活用(という名の手抜き)をうまくやるには結果としてこの方法になった。
とはいえStepFunctionsを最初から使う想定であればこのような実装にはなっていないので、AWSのそれぞれのサービスの良さはちゃんと踏まえた上で設計をすべき。
でも「どうしてもトンチを効かせて、StepFunctionsをうまく使いたいのだ」みたいな場合には、少なからず役に立ってもらいたいなとは思う。
あとboto3は便利。便利だけど、使いすぎ注意というのもあるかも(コードが煩雑になる)。