みなさんこんにちは!
クルトンです。
今回は、以前書いたブログの内容をTerraformを使ってIaC化していきます。
処理内容は上記ブログと同じく、次のようなものを作成します。
- S3へファイルアップロード
- EventBridgeが起動
- Lambdaでアップロードされたファイル名をprintする(Pythonコード)
Terraformの環境構築について
リソース作成についてはTerraformを使用しています。Terraformを使ってのリソース作成をした事が無い方は、環境構築から始めてみてください。
参考になるのが次のブログです。
Mac環境お使いの方向け
Windows環境お使いの方向け
MFA設定しているIAMを使いたい方へ
Terraformにおいて、MFAを設定しているIAMを使いたい方は次のブログが参考になります。(リンク先はMac環境で設定していますがaws-vaultはWindowsでも使えます。)
作成するファイルについて
フォルダ構成としては次のようになります。
.
├── lambda_function.py
└── main.tf
main.tf
ファイルでAWSリソース作成に必要な情報を書きます。lambda_function.py
ではLambda関数のコード内容を記述します。
それでは早速、lambda_function.py
から作成していきます。
lambda_function.pyを作成
以前マネコンから作成した時と同じコードを書いています。発火条件にしている内容を受け取って処理できているか、print関数でCloudWatch logsへ出力して確認します。
import json
def lambda_handler(event, context):
print(event)
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
次に main.tf
を作成していきます。
main.tfを作成
まずは各AWSリソース作成前に必要な設定やlocalsブロックを使った変数の定義をします。prefixとlambda_funcへお好きなお名前をご記入ください。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
archive = {
source = "hashicorp/archive"
version = "2.4.0"
}
}
required_version = ">= 1.2.0"
}
locals {
prefix = "お好きな名前をご記入ください" # どのリソースを作ったか分かりやすくするために、先頭文字列を設定
# S3
bucket_name = "${local.prefix}-bucket"
# EventBridge
event_rule = "${local.prefix}-event-rule"
eventbridge_service_role = "${local.prefix}-eventbridge-service-role"
# Lambda
lambda_service_role_name = "${local.prefix}-lambda-service-role"
lambda_file_name = "lambda_function"
lambda_func = "お好きな名前をご記入ください"
lambda_runtime = "python3.10"
entry_point = "lambda_handler"
output_source_dir = "archive/${local.lambda_file_name}.zip"
}
次の3つについて記述します。
- S3
- Lambda
- EventBridge
S3の設定
- aws_s3_bucket
- S3バケットの設定をします。名前の設定をすればOKです。
- aws_s3_object
- フォルダ作成をします。
- aws_s3_bucket_notification
- コメントにも書いておりますが、EventBridgeとS3を連携させるために必要な設定をします。
# S3バケット作成
resource "aws_s3_bucket" "bucket" {
bucket = local.bucket_name
}
resource "aws_s3_object" "input_folder" {
bucket = aws_s3_bucket.bucket.id
key = "input-file/"
}
# EventBridgeへ通知を送るのに必要
resource "aws_s3_bucket_notification" "bucket_notification" {
bucket = aws_s3_bucket.bucket.bucket
eventbridge = true
}
Lambdaの設定
- aws_iam_policy
- dataの方では、CloudWatchへ書き込みをするために必要なAWS定義済みの権限を指定し、resouceの方でdataで設定した内容を割り当てるようにしています。
- aws_iam_role_policy_attachment
- aws_iam_policyで
AWSLambdaBasicExecutionRole
を付与したポリシーを作成しているので、Lambda用に作っているIAMロールへ割り当てています。
- aws_iam_policyで
- aws_iam_policy_document
- サービスロールとしての設定が必要で、
identifiers = ["lambda.amazonaws.com"]
という設定が必要です。
- サービスロールとしての設定が必要で、
- aws_iam_role
- サービスロール本体です。aws_iam_policy_documentで設定した内容を渡してIAMロール作成を行ないます。
- archive_file
- ローカルで作った
lambda_function.py
ファイルを使ってzipファイルを作成します。作成場所はoutput_path
で定義した場所になります。
- ローカルで作った
- aws_cloudwatch_log_group
- どのロググループにログを残すか明示的に示します。
- aws_lambda_permission
- CloudWatchログへLambdaがログ情報を渡せるように、許可を出すために設定します。
- aws_lambda_function
- Lambda関数の設定を行ないます。今回は、archive_fileで作成したzipファイルを使ってLambda関数のコードを設定しております。
- コンテナを使う場合は
image_uri
を、S3にあるコードを使う場合はs3_bucket
を、ローカルマシンにあるファイルを使う場合はfilename
で設定します。(今回はfilenameを使っています。)
data "aws_iam_policy" "iam_policy_AWSLambdaBasicExecutionRole" {
arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_policy" "iam_policy_AWSLambdaBasicExecutionRole" {
name = local.lambda_service_role_name
policy = data.aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole.policy
}
resource "aws_iam_role_policy_attachment" "lambda_policy" {
role = aws_iam_role.iam_role_for_lambda.name
policy_arn = aws_iam_policy.iam_policy_AWSLambdaBasicExecutionRole.arn
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "iam_role_for_lambda" {
name = "iam_for_lambda"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
data "archive_file" "function_info" {
type = "zip"
source_file = "${local.lambda_file_name}.py"
output_path = local.output_source_dir
}
resource "aws_cloudwatch_log_group" "cloudwatch_log" {
name = "/aws/lambda/${local.lambda_func}"
}
resource "aws_lambda_permission" "logging" {
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.test_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.eventbridge_rule.arn
}
resource "aws_lambda_function" "test_lambda" {
function_name = local.lambda_func
role = aws_iam_role.iam_role_for_lambda.arn
filename = data.archive_file.function_info.output_path
handler = "${local.lambda_file_name}.${local.entry_point}"
runtime = local.lambda_runtime
source_code_hash = data.archive_file.function_info.output_base64sha256
depends_on = [aws_iam_role_policy_attachment.lambda_policy, aws_cloudwatch_log_group.cloudwatch_log]
}
ここで注意が必要なのがresource "aws_lambda_function"のhandlerです。ファイル名(拡張子なし).関数名
を記載する必要があります。拡張子を入れてAWSリソース作成をしても、エラーが出ないため、気を付ける必要があります。
EventBridgeの設定
- aws_cloudwatch_event_rule
- 指定フォルダでファイルアップロードのイベントが起きた場合に起動するよう設定しています。
- aws_cloudwatch_event_target
- EventBridge起動後にどのAWSサービスを動かすのか設定します。
- 前回Terraformを使ったブログと同じように設定するとエラーが発生します。
role_arn
を取り除く必要があります。
- aws_iam_policy_document
- EventBridgeを動かすために
identifiers = ["events.amazonaws.com"]
を設定する必要があります。
- EventBridgeを動かすために
- aws_iam_role
aws_iam_policy_document
を渡して作成する、IAMロール本体の設定をします。
resource "aws_cloudwatch_event_rule" "eventbridge_rule" {
name = local.event_rule
description = "S3の特定フォルダ配下にファイルアップロードしたときに、Lambdaを起動するルールをIaC化"
event_pattern = jsonencode({
"detail-type" : ["Object Created"],
"source" : ["aws.s3"],
"detail" : {
"bucket" : {
"name" : [local.bucket_name]
},
"object" : {
"key" : [{
"prefix" : "input-file/"
}]
}
}
})
}
resource "aws_cloudwatch_event_target" "eventbridge_target" {
rule = aws_cloudwatch_event_rule.eventbridge_rule.name
arn = aws_lambda_function.test_lambda.arn
input_transformer {
input_paths = {
"input_bucket_name" : "$.detail.bucket.name",
"input_s3_key" : "$.detail.object.key"
}
input_template = <<TEMPLATE
{"Parameters": {"input_bucket_name":"<input_bucket_name>", "input_s3_key":"<input_s3_key>"}}
TEMPLATE
}
# role_arn = aws_iam_role.eventbridge_service_role.arn # 設定するとエラーが発生する
}
data "aws_iam_policy_document" "eventbridge_policy" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["events.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "eventbridge_service_role" {
name = local.eventbridge_service_role
assume_role_policy = data.aws_iam_policy_document.eventbridge_policy.json
}
リソース aws_cloudwatch_event_target
へrole_arn
を設定したままterraform apply
コマンドを実行すると、次のようなエラーが発生する可能性があります。
Error: creating EventBridge Target (リソース名): ValidationException: RoleArn is not supported for target arn:aws:lambda:リージョン名:アカウントID:function:関数名.
上記のようなエラーが出た場合は、role_arn
が設定されていないかご確認ください。設定されている場合は削除するとエラーが解決します。
2つのファイル作成後にAWSリソースの作成
次のコマンドをCLIで実行し、実際にAWSリソースを作成します。
terraform init
- tfファイルを見ながら必要な処理をする。(providerで使われているものをインストールするなど。)
terraform apply
- 実際にAWSリソースの作成をする
本ブログでは動作確認したファイルを記載しているので、2つのコマンドでOKです。もしtfファイルを新たに作る場合は、以下のコマンドを実行しながら進める事になるかと思います。
terraform fmt
- tfファイルのコード整形をしてくれる
terraform validate
- 実行するtfファイルで書き方に問題がないか確認してくれる
terraform plan
- tfファイルで作成するリソースがどういったものになるか確認できる
AWSリソースが作成されると次の表記がされます。
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
AWSリソースの作成が完了したら、実際に動かしてみましょう!
実際に動かしてみた
AWSリソース作成が完了した後に、動作確認をマネージメントコンソールでやってみます。
まずはS3を開き、作成したフォルダへファイルアップロードをします。
ファイルアップロード完了後に作成したLambdaのページを開きます。関数の名前で検索してください。
Lambdaの関数名のページにおいて、モニタリングと書かれているタブをクリック後に現れる、CloudWatch ログを表示ボタンをクリックしてください。
CloudWatchログのストリームを確認すると、次のようにS3にアップロードしたファイル名などが受け取れている事を確認できます。
{'Parameters': {'input_bucket_name': '<EventBridgeの発火条件となっているバケット名>', 'input_s3_key': '<発火条件にしたフォルダ名>/<アップロードしたファイル名>'}}
終わりに
今回は、以前マネージメントコンソールで作成した内容を、Terraformを使って作成してみました。EventBridgeとLambdaの連携について、Terraformではどのように記述をするのか知りたかったので良い勉強になりました。
他のAWSリソースをTerraformで作成する方法についても、ブログ化してみたいです。
今回はここまで。
それでは、また!