AWS Step Functions楽しいですね!!
今まではAWSの各種サービスを使ってLambda間を繋いだり、失敗を適切に処理してリトライをするようにしていましたが、Step Functionsがきたことによってその辺りの手間が一気に楽になりました!!!
もうこれはバリバリ使うしかないですよ!!
と、言いたいところですが、2016年12月現在ではCloudFormationに対応していないため、CloudFormationのラッパーのSAMでも利用することができません。
せっかく、SAMで複数のLambdaやAPI Gatewayを定義できるようになったのに、そこにStep Functionsを組み込むことができないのは開発フローに組み込むことができないためとても面倒くさいです。
そこで、ないなら作ればいいじゃない!ということでCloudFormationのLambda-backed Custom Resourcesを使って、SAMを使った開発フローに組み込んでしまいます。
Lambda-backed Custom Resources
AWSTemplateFormatVersion: "2010-09-09"
Resources:
StepFunctionsCustomResource:
Type: "AWS::Lambda::Function"
Properties:
Handler: index.handler
Role: !GetAtt StepFunctionsCustomResourceRole.Arn
Code:
ZipFile: |
"use strict"
require('child_process').execSync('export HOME=/tmp && npm install aws-sdk@2.7.13', {cwd: '/tmp'});
let AWS = require('/tmp/node_modules/aws-sdk/');
let sf = new AWS.StepFunctions();
let response = require('cfn-response');
exports.handler = (event, context, callback) => {
Promise.resolve().then(() => {
switch (event.RequestType) {
case "Create":
return new Promise((resolve, reject) => {
let params = {
name: event.ResourceProperties.Name,
definition: JSON.stringify(event.ResourceProperties.Definition, (k, v) => v === 'true' ? true : v === 'false' ? false : v),
roleArn: event.ResourceProperties.RoleArn
};
sf.createStateMachine(params, (err, data) => err ? reject(err) : resolve(data));
});
case "Update":
return new Promise((resolve, reject) => {
let params = {
stateMachineArn: `arn:aws:states:${process.env.AWS_REGION}:${context.invokedFunctionArn.split(":")[4]}:stateMachine:${event.ResourceProperties.Name}`
};
sf.deleteStateMachine(params, (err, data) => err ? reject(err) : resolve(data));
}).then(() => {
return new Promise((resolve, reject) => {
let params = {
name: event.ResourceProperties.Name,
definition: JSON.stringify(event.ResourceProperties.Definition, (k, v) => v === 'true' ? true : v === 'false' ? false : v),
roleArn: event.ResourceProperties.RoleArn
};
sf.createStateMachine(params, (err, data) => err ? reject(err) : resolve(data));
});
});
case "Delete":
return new Promise((resolve, reject) => {
let params = {
stateMachineArn: `arn:aws:states:${process.env.AWS_REGION}:${context.invokedFunctionArn.split(":")[4]}:stateMachine:${event.ResourceProperties.Name}`
};
sf.deleteStateMachine(params, (err, data) => err ? reject(err) : resolve(data));
});
default:
throw new Error(`Unkown RequestType: '${event.RequestType}'`);
}
}).then((data) => {
let responseData = {
stateMachineArn: data.stateMachineArn
};
response.send(event, context, response.SUCCESS, responseData);
}).catch((err) => {
let responseData = {
Error: err.toString()
};
response.send(event, context, response.FAILED, responseData);
});
};
Runtime: nodejs4.3
Timeout: 30
StepFunctionsCustomResourceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
Policies:
-
PolicyName: StepFunctionsCustomResourceRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*:*"
-
Effect: "Allow"
Action:
- "states:*"
Resource: "*"
-
Effect: "Allow"
Action:
- "iam:PassRole"
Resource: "*"
StepFunctionsCustomResourceInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles:
-
Ref: "StepFunctionsCustomResourceRole"
Outputs:
StepFunctionsCustomResourceArn:
Value: !GetAtt StepFunctionsCustomResource.Arn
実際にプロジェクトに組み込んでる例はこちらです。
基本的にはLambda-backed Custom ResourcesでAWS SDKを使いStep Functionsを作成、破棄するだけになります。
しかし、ちょっと面倒臭い話で2016年12月現在にランタイムでNode 4.3を選択すると、デフォルトでインストールされているAWS SDKはv2.6.9になります。
これを最新のSDKに入れ替えないとStep Functionsが使えないため
require('child_process').execSync('export HOME=/tmp && npm install aws-sdk@2.7.13', {cwd: '/tmp'});
で/tmp
ディレクトリ以下にSDKをインストールし
let AWS = require('/tmp/node_modules/aws-sdk/');
でインストールしたSDKを読み込む必要があります。
このあたり、もう少し待てば改善されると思いますので最新のSDKがインストールされしだいこの処理は削除します。
また、これはどこの問題かわかっていないのですが、Lambda-backed Custom Resourcesに渡されるプロパティの値はすべて文字列になります。
Step Functionsに渡すDefinition
ではEnd
などの値がboolean値になるため、このままでは作成時にエラーになってしまいます。
そこで、パラメーターの作成時に"true"
をtrue
に、"false"
をfalse
に変換してあげる必要があります。
let params = {
name: event.ResourceProperties.Name,
definition: JSON.stringify(event.ResourceProperties.Definition, (k, v) => v === 'true' ? true : v === 'false' ? false : v),
roleArn: event.ResourceProperties.RoleArn
};
数値でも同様のことがありそうですが、まだ自分が必要になっていないのでそこまで作っていません。
SAMでStep Functionsを定義する
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: A starter AWS Lambda function.
Parameters:
StepFunctionsCustomResourceArn:
Type: String
Description: Role Arn of StepFunctions Custom Resource
LambdaExectionRoleArn:
Type: String
Description: Role Arn of Lambda execution
Resources:
MyFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: src/index.handler
Runtime: nodejs4.3
CodeUri: .
Description: A starter AWS Lambda function.
MemorySize: 1536
Timeout: 10
Role:
Ref: LambdaExectionRoleArn
StepFunctions:
Type: 'Custom::StepFunctions'
Properties:
ServiceToken:
Ref: StepFunctionsCustomResourceArn
Name: test-step
RoleArn: arn:aws:iam::125043710017:role/service-role/StatesExecutionRole-us-east-1
Definition:
Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
StartAt: HelloWorld
States:
HelloWorld:
Type: Task
Resource: !GetAtt MyFunction.Arn
End: true
実際にプロジェクトに組み込んでる例はこちらです。
先ほど作ったLambda-backed Custom ResourcesのARNを受け取り、実際にSAMの定義ファイルにType: 'Custom::StepFunctions'
として組み込みます。
簡単ですね!!!
おわりに
早くSAMで正式サポートしてくれればこのあたりの面倒の処理を全て削除できるので早くサポートしてほしいですね!
今回、リンクで貼ったプロジェクトの各種解説に関してはこちらの記事を参照してください。
というわけでStep Functionsで遊ぶ作業に戻ります!!!