開幕宣言
この記事は estie 夏のブログ祭りの1日目の記事です。
8月ですね 🍉
今年は梅雨明けが早く、でもそのあとに雨が続いたり、またご時世もあったりしますが皆様いかがお過ごしでしょうか。
本格的な夏が始まったということで、estie 夏のブログ祭りを開催します!! 🏄🏄♀️🏄♂️
エンジニア、非エンジニア含め平日毎日ブログを更新していきます。テクニカルな話、カルチャーな話などなど更新されていきますので、皆さんチェックしてくださいね!
はじめに
普段はRuby on Rails や Python を使って開発をしている、アプリケーションエンジニアのえりりんです。
以前 Terraform でアプリの設定を変更する記事を書きました。
記事はこちら
アプリケーションエンジニアが Terraform 触ってみた|えりりん|note
あれから半年強経ってだいぶ Terraform に慣れてきたので1サービス管理できるようになった記事を書いて見ます。
管理対象の作成
AWS Step Functions というサービスはご存知でしょうか?
そうです。ワークフローサービスです。
処理の順番や条件を定義しておくと、再実行やタイムアウトを管理しながら実行してくれるものです。
AWS Management Console 上のGUIから以下のように処理したいサービスのパーツを順番に組み立てて設定ができます。
この組み立てた内容は JSON で定義されます。
では、これを Terraform で書いたらどうなるのでしょうか。
Terraform 編
今回書くコードは以下に push してありますので、よろしければ参考にしてみてください。
まずは repository を作成して Terraform が使える準備をします。
こちらを参考に書いていきます。
main.tf
erraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } # Configure the AWS Provider provider "aws" { region = "ap-northeast-1" } terraform { backend "s3" { bucket = "stepfunction-tf-state" key = "test.tfstate" region = "ap-northeast-1" } }
初期化します。
terraform init
AWS Step Functions を定義するリソースを用意します (23行目〜25行目)。
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } # Configure the AWS Provider provider "aws" { region = "ap-northeast-1" } terraform { backend "s3" { bucket = "stepfunction-tf-state" key = "test.tfstate" region = "ap-northeast-1" } } resource "aws_sfn_state_machine" "sfn_state_machine" { }
続いて AWS Management Console で行った設定を import します。
リソースによって import の仕方が違うので注意です。
AWS Step Functions の場合はどうかというと、以下のページを参照します。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine
一番下に import 時のコマンドが書いてありますね。
State Machines can be imported using the arn
とのことなので arn を指定して import します。
terraform import aws_sfn_state_machine.sfn_state_machine arn:aws:states:ap-northeast-1:XXXXXX:stateMachine:HelloWorld
import 成功です!🎉
main.tf の中身を埋めていきます。
state file から中身を参照しましょう。
terraform state show aws_sfn_state_machine.sfn_state_machine
# aws_sfn_state_machine.sfn_state_machine: resource "aws_sfn_state_machine" "sfn_state_machine" { arn = "arn:aws:states:ap-northeast-1: ... ... ... ... ... definition = jsonencode( { Comment = "A Hello World example demonstrating various state types of the Amazon States Language" StartAt = "Pass" States = { "Hello World" = { End = true Type = "Pass" } "Hello World example?" = { Choices = [ { BooleanEquals = true Next = "Yes" Variable = "$.IsHelloWorldExample" }, { BooleanEquals = false Next = "No" Variable = "$.IsHelloWorldExample" }, ] Comment = "A Choice state adds branching logic to a state machine. Choice rules can implement 16 different comparison operators, and can be combined using And, Or, and Not" Default = "Yes" Type = "Choice" } No = { Cause = "Not Hello World" Type = "Fail" } "Parallel State" = { Branches = [ { StartAt = "Hello" States = { Hello = { End = true Type = "Pass" } } }, { StartAt = "World" States = { World = { End = true Type = "Pass" } } }, ] Comment = "A Parallel state can be used to create parallel branches of execution in your state machine." Next = "Hello World" Type = "Parallel" } Pass = { Comment = "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines." Next = "Hello World example?" Type = "Pass" } "Wait 3 sec" = { Comment = "A Wait state delays the state machine from continuing for a specified time." Next = "Parallel State" Seconds = 3 Type = "Wait" } Yes = { Next = "Wait 3 sec" Type = "Pass" } } } ) }
と表示されるのでこの内容をそのまま main.tf に貼ります (24行目〜109行目)。
main.tf
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } # Configure the AWS Provider provider "aws" { region = "ap-northeast-1" } terraform { backend "s3" { bucket = "stepfunction-tf-state" key = "test.tfstate" region = "ap-northeast-1" } } resource "aws_sfn_state_machine" "sfn_state_machine" { definition = jsonencode( { Comment = "A Hello World example demonstrating various state types of the Amazon States Language" StartAt = "Pass" States = { "Hello World" = { End = true Type = "Pass" } "Hello World example?" = { Choices = [ { BooleanEquals = true Next = "Yes" Variable = "$.IsHelloWorldExample" }, { BooleanEquals = false Next = "No" Variable = "$.IsHelloWorldExample" }, ] Comment = "A Choice state adds branching logic to a state machine. Choice rules can implement 16 different comparison operators, and can be combined using And, Or, and Not" Default = "Yes" Type = "Choice" } No = { Cause = "Not Hello World" Type = "Fail" } "Parallel State" = { Branches = [ { StartAt = "Hello" States = { Hello = { End = true Type = "Pass" } } }, { StartAt = "World" States = { World = { End = true Type = "Pass" } } }, ] Comment = "A Parallel state can be used to create parallel branches of execution in your state machine." Next = "Hello World" Type = "Parallel" } Pass = { Comment = "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines." Next = "Hello World example?" Type = "Pass" } "Wait 3 sec" = { Comment = "A Wait state delays the state machine from continuing for a specified time." Next = "Parallel State" Seconds = 3 Type = "Wait" } Yes = { Next = "Wait 3 sec" Type = "Pass" } } } ) name = "HelloWorld" tags = {} tags_all = {} type = "STANDARD" logging_configuration { include_execution_data = false level = "OFF" } tracing_configuration { enabled = false } }
これで AWS Step Functions を Terraform 管理することができました!
しかし、AWS Step Functions の定義の JSON 部分もうちょっと綺麗に書けないかなと考えました。
リファクタ編
Terraform にはいくつかの function がありますが、その中に templatefile という function があります。
公式ドキュメントはこちら
これを使って JSON 部分を別ファイルに切り出してみましょう。
JSON部分を別ファイルにします。
definition.tftpl
{ "Comment": "A Hello World example demonstrating various state types of the Amazon States Language", "StartAt": "Pass", "States": { "Pass": { "Comment": "A Pass state passes its input to its output, without performing work. Pass states are useful when constructing and debugging state machines.", "Type": "Pass", "Next": "Hello World example?" }, "Hello World example?": { "Comment": "A Choice state adds branching logic to a state machine. Choice rules can implement 16 different comparison operators, and can be combined using And, Or, and Not", "Type": "Choice", "Choices": [ { "Variable": "$.IsHelloWorldExample", "BooleanEquals": true, "Next": "Yes" }, { "Variable": "$.IsHelloWorldExample", "BooleanEquals": false, "Next": "No" } ], "Default": "Yes" }, "Yes": { "Type": "Pass", "Next": "Wait 3 sec" }, "No": { "Type": "Fail", "Cause": "Not Hello World" }, "Wait 3 sec": { "Comment": "A Wait state delays the state machine from continuing for a specified time.", "Type": "Wait", "Seconds": 3, "Next": "Parallel State" }, "Parallel State": { "Comment": "A Parallel state can be used to create parallel branches of execution in your state machine.", "Type": "Parallel", "Next": "Hello World", "Branches": [ { "StartAt": "Hello", "States": { "Hello": { "Type": "Pass", "End": true } } }, { "StartAt": "World", "States": { "World": { "Type": "Pass", "End": true } } } ] }, "Hello World": { "Type": "Pass", "End": true } } }
main.tf で上記のファイルを読むようにします (24行目〜27行目)。
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } } # Configure the AWS Provider provider "aws" { region = "ap-northeast-1" } terraform { backend "s3" { bucket = "stepfunction-tf-state" key = "test.tfstate" region = "ap-northeast-1" } } resource "aws_sfn_state_machine" "sfn_state_machine" { definition = templatefile( "definition.tftpl", {} ) name = "HelloWorld" role_arn = "arn:aws:iam::XXX:role/service-role/StepFunctions-HelloWorld-role" tags = {} tags_all = {} type = "STANDARD" logging_configuration { include_execution_data = false level = "OFF" } tracing_configuration { enabled = false } }
外部ファイルに変数を渡すこともできます。
main.tf(抜粋)
resource "aws_sfn_state_machine" "sfn_state_machine" { definition = templatefile( "definition.tftpl", { wait_sec = 5 } ) name = "HelloWorld" role_arn = "arn:aws:iam::XXX:role/service-role/StepFunctions-HelloWorld-role" tags = {} tags_all = {} type = "STANDARD" logging_configuration { include_execution_data = false level = "OFF" } tracing_configuration { enabled = false } }
definition.tftpl(抜粋)
"Yes": { "Type": "Pass", "Next": "Wait ${wait_sec} sec" },
上記のように templatefile を用いることにより wait_sec の値だけ変えた AWS Step Functions を複数作成するなど、同一ファイルを用いて複数のインフラを用意する保守性の高い Terraform コードが書けます!
まとめ
AWS Management Console で作成した AWS Step Functions を Terraform で管理し、種類を増やしたい要望にも耐えられるコードが書けました!
2日目は『estieの開発チームは、ビジネスメンバーと共に価値創出する』です。 お楽しみに✨✨✨