この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS CDKでもAWS SAMのようにリソースを定義したい時もある
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKを使いたい。でも、AWS SAMのようにリソースを定義したいと思ったことはありますか? 私はあります。
例えば、AWS Step Functionsのステートマシンを定義するとき、AWS CDKでは以下のようにTypeScriptやPythonなどのプログラミング言語で定義する必要があります。
AWS CDKでAWS Step Functionsのステートマシンを定義する場合
import {
Stack,
StackProps,
Duration,
aws_lambda as lambda,
aws_stepfunctions as sfn,
aws_stepfunctions_tasks as tasks,
} from "aws-cdk-lib";
import { Construct } from "constructs";
declare const submitLambda: lambda.Function;
declare const getStatusLambda: lambda.Function;
export class AwsSamStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const submitJob = new tasks.LambdaInvoke(this, "Submit Job", {
lambdaFunction: submitLambda,
// Lambda's result is in the attribute `Payload`
outputPath: "$.Payload",
});
const waitX = new sfn.Wait(this, "Wait X Seconds", {
time: sfn.WaitTime.secondsPath("$.waitSeconds"),
});
const getStatus = new tasks.LambdaInvoke(this, "Get Job Status", {
lambdaFunction: getStatusLambda,
// Pass just the field named "guid" into the Lambda, put the
// Lambda's result in a field called "status" in the response
inputPath: "$.guid",
outputPath: "$.Payload",
});
const jobFailed = new sfn.Fail(this, "Job Failed", {
cause: "AWS Batch Job Failed",
error: "DescribeJob returned FAILED",
});
const finalStatus = new tasks.LambdaInvoke(this, "Get Final Job Status", {
lambdaFunction: getStatusLambda,
// Use "guid" field as input
inputPath: "$.guid",
outputPath: "$.Payload",
});
const definition = submitJob
.next(waitX)
.next(getStatus)
.next(
new sfn.Choice(this, "Job Complete?")
// Look at the "status" field
.when(sfn.Condition.stringEquals("$.status", "FAILED"), jobFailed)
.when(
sfn.Condition.stringEquals("$.status", "SUCCEEDED"),
finalStatus
)
.otherwise(waitX)
);
new sfn.StateMachine(this, "StateMachine", {
definition,
timeout: Duration.minutes(5),
});
}
}
管理したいステートマシンのワークフローが複雑でない場合は問題ありませんが、分岐やループ、並列処理が複数あると、定義するのがなかなか大変です。
一方、AWS SAMではステートマシンを定義する際に、ASL形式で記述されたワークフローを読み込むことができます。これにより、AWS Step Functions Workflow Studioで設計したワークフローを流用することが出来ます。
AWS SAMでAWS Step Functionsのステートマシンを定義する場合
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
StateMachineName:
Description: Please type the Step Functions StateMachine Name.
Type: String
Default: 'sfn-sam-app-statemachine'
LambdaFunctionName:
Description: Please type the Lambda Function Name.
Type: String
Default: 'sfn-sam-app-function'
Resources:
LambdaFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${LambdaFunctionName}
CodeUri: functions/hello_world/
Handler: app.lambda_handler
Runtime: python3.8
Timeout: 60
Role: !GetAtt LambdaFunctionRole.Arn
LambdaFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AWSStepFunctionsReadOnlyAccess
StateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Name: !Sub ${StateMachineName}
DefinitionUri: statemachine/sfn.asl.json
DefinitionSubstitutions:
LambdaFunction: !GetAtt LambdaFunction.Arn
Role: !GetAtt StateMachineRole.Arn
Logging:
Level: ALL
IncludeExecutionData: True
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt StateMachineLogGroup.Arn
StateMachineLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName : !Join [ "", [ '/aws/states/', !Sub '${StateMachineName}', '-Logs' ] ]
StateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
Action:
- sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaRole
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
抜粋 : Step Functionsの構築はAWS SAMを利用すると捗りそうです
そんなある時、AWS CDKでもAWS Step Functions Workflow Studioで設計したワークフローを読みこんで、ステートマシンを定義したいなと思い、AWS CDKのAPI Referenceを漁っていると、見つけてしまいました。
aws-cdk-lib.aws_sam module を
そこで今回は、aws-cdk-lib.aws_sam moduleを紹介します。
aws-cdk-lib.aws_sam module とは
2021/2/16時点の最新のAWS CDK v2.12.0 ではこのモジュールはプレビューです。ご注意ください
aws-cdk-lib.aws_sam moduleはAWS SAMのConstructライブラリーです。
v2.12.0 時点ではL2 Constructはなく、L1 Constructのみとなっています。そのため、基本的にはAWS SAMのテンプレートを書くときと同じような書き味になります。
L1 ConstructとAWS SAMのTypeとの対応は以下の通りです。
L1 Construct | AWS SAM Type |
---|---|
CfnApi | AWS::Serverless::Api |
CfnApplication | AWS::Serverless::Application |
CfnFunction | AWS::Serverless::Function |
CfnHttpApi | AWS::Serverless::HttpApi |
CfnLayerVersion | AWS::Serverless::LayerVersion |
CfnSimpleTable | AWS::Serverless::SimpleTable |
CfnStateMachine | AWS::Serverless::StateMachine |
やってみた
ワークフローの設計
それではaws-cdk-lib.aws_sam module
を使って、実際にステートマシンを作成してみます。
まずはワークフローの設計を行います。
Step Functionsのコンソールより、ステートマシン
- ステートマシン
の作成をクリックします。
ワークフローを視覚的に設計
を選択して、次へ
をクリックします。
AWS Step Functions Workflow Studioでステートマシンのワークフローを設計します。設計後は定義をクリックし、生成された定義(ASL形式)を控えておきます。
生成された定義は以下になります。
{
"Comment": "A description of my state machine",
"StartAt": "Pass 1",
"States": {
"Pass 1": {
"Type": "Pass",
"Next": "Wait"
},
"Wait": {
"Type": "Wait",
"Seconds": 1,
"Next": "Parallel 1"
},
"Parallel 1": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "Pass 2",
"States": {
"Pass 2": {
"Type": "Pass",
"Next": "Parallel 2"
},
"Parallel 2": {
"Type": "Parallel",
"Branches": [
{
"StartAt": "Pass 4",
"States": {
"Pass 4": {
"Type": "Pass",
"End": true
}
}
},
{
"StartAt": "Pass 5",
"States": {
"Pass 5": {
"Type": "Pass",
"End": true
}
}
}
],
"Next": "Pass 6"
},
"Pass 6": {
"Type": "Pass",
"End": true
}
}
},
{
"StartAt": "Pass 3",
"States": {
"Pass 3": {
"Type": "Pass",
"End": true
}
}
}
],
"Next": "Choice"
},
"Choice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$",
"IsPresent": true,
"Next": "Success"
}
],
"Default": "Fail"
},
"Success": {
"Type": "Succeed"
},
"Fail": {
"Type": "Fail"
}
}
}
AWS CDKでステートマシンをデプロイ
それでは、AWS CDKでステートマシンをデプロイします。
まず、先程生成された定義を保存します。今回は、./src/stepFunctions/workflow.asl.json
に保存しました。
.
├── .gitignore
├── .npmignore
├── README.md
├── bin
│ └── aws-sam.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── aws-sam-stack.ts
├── package-lock.json
├── package.json
├── src
│ └── stepFunctions
│ └── workflow.asl.json ←生成された定義のファイル
├── test
│ └── aws-sam.test.ts
└── tsconfig.json
次に、生成された定義を読み込むようにステートマシンを定義します。
定義する際は、API ReferenceとAWS::Serverless::StateMachineのドキュメントを確認しながら行います。
なお、生成された定義を読み込む際は、ローカルに存在する定義を直接読み込むことはできず、S3バケットに一度アップロードする必要があります。S3バケットに定義ファイルをアップロードする処理もAWS CDKで行います。
実際のコードは以下の通りです。
./lib/aws-sam-stack.ts
import {
Fn,
Stack,
StackProps,
aws_s3 as s3,
aws_s3_deployment as s3deploy,
aws_logs as logs,
aws_iam as iam,
aws_sam as sam,
} from "aws-cdk-lib";
import { Construct } from "constructs";
export class AwsSamStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const stackUniqueId = Fn.select(2, Fn.split("/", this.stackId));
// S3 buckets to store AWS Step Function Workflow
const sfnWorkflowBucket = new s3.Bucket(this, "SfnWorkflowBucket", {
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: new s3.BlockPublicAccess({
blockPublicAcls: true,
blockPublicPolicy: true,
ignorePublicAcls: true,
restrictPublicBuckets: true,
}),
});
// Deploy AWS Step Function Workflow
new s3deploy.BucketDeployment(this, "DeployFilesToSfnWorkflowBucket", {
sources: [
s3deploy.Source.asset("./src/stepFunctions/", {
exclude: [".DS_Store"],
}),
],
destinationBucket: sfnWorkflowBucket,
});
// CloudWatch Logs for State Machine Logs
const stateMachineLogGroup = new logs.LogGroup(
this,
"StateMachineLogGroup",
{
logGroupName: `/aws/vendedlogs/states/CfnStateMachine-${stackUniqueId}-Logs`,
retention: logs.RetentionDays.TWO_WEEKS,
}
);
// IAM Role for State Machine
const stateMachineIamRole = new iam.Role(this, "StateMachineIamRole", {
assumedBy: new iam.ServicePrincipal("states.amazonaws.com"),
managedPolicies: [
new iam.ManagedPolicy(this, "CloudWatchLogsDeliveryFullAccessPolicy", {
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ["*"],
actions: [
"logs:CreateLogDelivery",
"logs:GetLogDelivery",
"logs:UpdateLogDelivery",
"logs:DeleteLogDelivery",
"logs:ListLogDeliveries",
"logs:PutResourcePolicy",
"logs:DescribeResourcePolicies",
"logs:DescribeLogGroups",
],
}),
],
}),
new iam.ManagedPolicy(this, "XRayAccessPolicy", {
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ["*"],
actions: [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets",
],
}),
],
}),
],
});
// AWS Step Functions State Machine
new sam.CfnStateMachine(this, "CfnStateMachine", {
definitionSubstitutions: {
definitionSubstitutionsKey: "definitionSubstitutions",
},
definitionUri: {
bucket: sfnWorkflowBucket.bucketName,
key: "workflow.asl.json",
},
logging: {
destinations: [
{
cloudWatchLogsLogGroup: {
logGroupArn: stateMachineLogGroup.logGroupArn,
},
},
],
includeExecutionData: true,
level: "ALL",
},
name: "CfnStateMachine",
role: stateMachineIamRole.roleArn,
tags: {
Name: "CfnStateMachine",
},
tracing: {
enabled: true,
},
type: "STANDARD",
});
}
}
準備ができたら、npx cdk deploy
でステートマシンなど各種リソースをデプロイします。
Step Functionsのコンソールを確認すると、AWS Step Functions Workflow Studioで設計したワークフローのステートマシンが作成されたことが確認できます。
こちらのステートマシンを実行してみます。
エラーは発生せず、正常に終了しました。
X-Rayトレースマップも正常に確認できます。
CloudWatch Logsへのログ出力も正常にされていることが確認できます。
AWS CDKがますます便利になっていく
AWS CDKでもAWS SAMのようにリソースを定義したいという要望を叶えてくれるモジュール aws-cdk-lib.aws_sam module を紹介しました。
上手く使えばAWS CDKとAWS SAMの良いとこ取りのような形になるので、こちらのモジュールが早くGAになることを楽しみに待っています。
また、今回のデモで使ったコードは以下リポジトリにアップロードしています。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!