Lambda+Glue+Step Functionsの構成をTerraformでデプロイしてみた
データ事業本部のueharaです。
今回は、Lambda+Glue+Step Functionsの構成をTerraformでデプロイしてみたいと思います。
はじめに
これまで、Lambda+Glue+Step Functionsといった比較的軽量なETLでありがちな構成を
- Serverless Framework
- AWS SAM
- AWS CDK
でそれぞれデプロイしてみるというブログを公開しました。
今回は上記のブログでもデプロイを実施した以下の構成をTerraformを用いてデプロイしたいと思います。
なお、構成図の内S3は既にAWS環境に存在するものとして以下説明を扱います。
フォルダ構成
今回のフォルダ構成は以下の通りです。
.
├── glue_scripts
│ └── test_glue.py
├── handler
│ └── test_func.py
└── main.tf
※この段階で terraform init
はしていないので .terraform
や .terraform.lock.hcl
は表示されておりません。
各ファイルの構成
glue/test_glue.py
こちらは前回/前々回と同じく、Glueを動かすためのサンプルスクリプトとなるため、スリープするだけで特段処理はしないものになります。
import sys
import time
def main(argv):
print("start")
# sleep 5 minutes
time.sleep(300)
print("end")
main(sys.argv)
handler/test_func.py
Lambdaで動かすPythonスクリプトになります。こちらも検証用なので特に処理はしません。
import time
def lambda_handler(event, context):
print("start")
# sleep 1 minute
time.sleep(60)
print("end")
return {"message": "success"}
main.tf
こちらが今回メインとなるLambda+Glue+Step Functionsの構成を記載したファイルです。
簡単化のため1つの .tf
ファイルに記載をまとめています。
provider "aws" {
region = "ap-northeast-1"
}
# Lambda Function用のZIPファイル作成
data "archive_file" "lambda_zip" {
type = "zip"
source_dir = "${path.module}/handler"
output_path = "${path.module}/lambda_function.zip"
}
# Lambda Function Role
resource "aws_iam_role" "lambda_role" {
name = "uehara-terraform-test-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
# Lambda基本実行ポリシーのアタッチ
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
role = aws_iam_role.lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# Lambda Function
resource "aws_lambda_function" "my_lambda" {
function_name = "uehara-terraform-test-lambda"
role = aws_iam_role.lambda_role.arn
handler = "test_func.lambda_handler"
runtime = "python3.9"
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
timeout = 180
memory_size = 128
architectures = ["x86_64"]
}
# glue-scriptsディレクトリ内のファイルリストを取得
locals {
glue_script_files = fileset("${path.module}/glue_scripts", "**/*.py")
}
# すべてのGlueスクリプトをS3にアップロード
resource "aws_s3_object" "glue_scripts" {
for_each = { for f in local.glue_script_files : f => f }
bucket = "cm-da-uehara"
key = "glue-scripts/${each.value}"
source = "${path.module}/glue_scripts/${each.value}"
}
# Glue Job Role
resource "aws_iam_role" "glue_role" {
name = "uehara-terraform-test-glueJob-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "glue.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
# Glue Service Roleポリシーのアタッチ
resource "aws_iam_role_policy_attachment" "glue_service_role" {
role = aws_iam_role.glue_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
}
# S3 Full Accessポリシーのアタッチ
resource "aws_iam_role_policy_attachment" "glue_s3_access" {
role = aws_iam_role.glue_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
# Glue Job
resource "aws_glue_job" "my_glue_job" {
name = "uehara-terraform-test-glueJob"
role_arn = aws_iam_role.glue_role.arn
glue_version = "3.0"
max_capacity = 0.0625
max_retries = 0
execution_property {
max_concurrent_runs = 3
}
command {
name = "pythonshell"
script_location = "s3://cm-da-uehara/glue-scripts/test_glue.py"
python_version = "3.9"
}
default_arguments = {
"library-set" = "analytics"
}
}
# Step Functions Role
resource "aws_iam_role" "step_functions_role" {
name = "uehara-terraform-test-sf-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "states.ap-northeast-1.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
# Lambda実行ポリシーのアタッチ
resource "aws_iam_role_policy_attachment" "step_functions_lambda_role" {
role = aws_iam_role.step_functions_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaRole"
}
# Glue実行ポリシーのアタッチ
resource "aws_iam_role_policy_attachment" "step_functions_glue_role" {
role = aws_iam_role.step_functions_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
}
# Step Functions State Machine
resource "aws_sfn_state_machine" "my_step_functions" {
name = "uehara-terraform-test-sf"
role_arn = aws_iam_role.step_functions_role.arn
definition = jsonencode({
Comment = "Test Step Functions"
StartAt = "InvokeLambda"
States = {
InvokeLambda = {
Type = "Task"
Resource = aws_lambda_function.my_lambda.arn
Next = "InvokeGlueJob"
}
InvokeGlueJob = {
Type = "Task"
Resource = "arn:aws:states:::glue:startJobRun.sync"
Parameters = {
JobName = aws_glue_job.my_glue_job.name
}
End = true
}
}
})
}
# EventBridge Role
resource "aws_iam_role" "eventbridge_role" {
name = "uehara-terraform-test-eventbridge-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "events.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
# EventBridge Rule
resource "aws_cloudwatch_event_rule" "s3_event_rule" {
name = "uehara-terraform-test-sf-event"
description = "Rule to trigger Step Functions on S3 object creation"
event_pattern = jsonencode({
source = ["aws.s3"]
"detail-type" = ["Object Created"]
detail = {
bucket = {
name = ["cm-da-uehara"]
}
object = {
key = [{
prefix = "tmp/"
}]
}
}
})
}
# EventBridgeがStep Functionsを実行するためのポリシー
resource "aws_iam_policy" "eventbridge_to_sfn_policy" {
name = "eventbridge-to-sfn-policy"
description = "Allow EventBridge to start Step Functions execution"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "states:StartExecution"
Resource = aws_sfn_state_machine.my_step_functions.arn
}
]
})
}
# ポリシーをEventBridgeロールにアタッチ
resource "aws_iam_role_policy_attachment" "eventbridge_to_sfn_attachment" {
role = aws_iam_role.eventbridge_role.name
policy_arn = aws_iam_policy.eventbridge_to_sfn_policy.arn
}
# EventBridge Target
resource "aws_cloudwatch_event_target" "step_functions_target" {
rule = aws_cloudwatch_event_rule.s3_event_rule.name
arn = aws_sfn_state_machine.my_step_functions.arn
role_arn = aws_iam_role.eventbridge_role.arn
}
冒頭で紹介したブログの内容と同じように、S3バケット(上記だと cm-da-uehara
)に /tmp
プレフィックスを持つオブジェクトが作成されるとEventBridge経由でStep Functionsが起動するというものになっています。
それぞれの記述内容については、コメントで記載している通りです。
LambdaやGlueのスクリプトをデプロイするための処理も、同じく main.tf
の中に記載をしております。
デプロイ
ファイルの準備ができたので、デプロイを行います。
まず、初期化を行います。
$ terraform init
実行後 Terraform has been successfully initialized!
と表示されていれば成功です。
初期化が完了したら、以下コマンドでデプロイを行います。
$ terraform apply --auto-approve
※注:AWSアカウントでMFA認証を有効化している場合は、以下を参考に ./aws/config
を設定しておいて下さい。
デプロイが完了すると、以下のようなStep Functionsが確認できるかと思います。
実行
指定したS3バケットに /tmp
プレフィックスを持つオブジェクトをPutすると、EventBridge経由でStep Functionsが起動するかと思います。
LambdaとGlueを実行するそれぞれのステートが以下のように正常終了していれば成功です。
最後に
今回は、Lambda+Glue+Step Functionsの構成をTerraformでデプロイしてみました。
参考になりましたら幸いです。