0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「チュートリアル: Amazon S3 トリガーを使用して Lambda 関数を呼び出す」をTerraformで実践!!

Posted at

はじめに

以前、AWS公式の「チュートリアル: Amazon S3 トリガーを使用して Lambda 関数を呼び出す」をGUIで一度行ったのですが、今回はこのチュートリアルをIaCの学習の一環として、Terraformで実践することにしました。

実際に作成していきます

providersの設定

Terraformなので、まずはどのクラウドを使用するかの設定を記述します。
ここでは、AWSを使用してリージョンは東京の「ap-northeast-1」を設定しています。

// providers.tf
provider "aws" {
  region = "ap-northeast-1"
}

[IAM] 配下

どこでリソースを作成するか決めたら次は、そのリソースを操作するための、ロールを作成します。

// role.tf
resource "aws_iam_role" "lambda_s3_trigger_role" {
    // "作成するリソース" "terraform内でのローカル名"
    
  name = "lambda-s3-trigger-role"
        //awsで作成されるリソース名
        
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Principal = {
          Service = "lambda.amazonaws.com"
          // Lambdaがこのロールを使えるようにする
        },
        Action = "sts:AssumeRole"
      }
    ]
  })
}

sts:AssumeRoleを使用する理由

  • LambdaはIAMユーザーを使えない → ロール必須
    LambdaはIAMユーザーを直接使えない
    代わりにIAMロールを引き受ける(Assume)ことで権限を得る
    そのために sts:AssumeRole が必要
  • AssumeRole- AssumeRole によって発行される認証情報は一時的なものだから漏洩のリスクが低い
  • ロール単位で「誰が何をしたか」を分離できるため

まずは、許可ポリシーを作成します。
今回は「CloudWatch」と「S3」に対して設定します。
※ ベストプラクティスにもある通り、最小限のアクセス権限の適用を目指します!

// policy.tf
resource "aws_iam_policy" "lambda_logging_s3" {
  name = "LambdaLoggingAndS3Access"
  description = "Allow Lambda to write logs and read from S3 buckets"
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
      Effect = "Allow",//CloudWatchLogsに対するログ出力権限(Lambdaからのログ送信を許可)
      Action = [
        "logs:PutLogEvents", //CloudWatchにイベント送信
        "logs:CreateLogGroup", //logを保存するためのグループが存在しなかったら作成
        "logs:CreateLogStream" //ログストリーム(ログの時系列チャネル)を作成する
      ],
      Resource = "arn:aws:logs:ap-northeast-1:[自分のアカウントID]:[リソース]" //[リージョン]:[アカウントID]:[リソース]を記載する
      },
      {
      Effect = "Allow",//S3オブジェクト取得権限(Lambdaでファイルを読むための権限)
      Action =[
        "s3:GetObject" //オブジェクトを取得
      ],
      Resource = "arn:aws:s3:::作成するS3/*" //取得する対象のS3配下全てを設定
      }
    ]
  })
}

実行するロールと許可するポリシーが作成出来たら、アタッチしていきます。
アタッチの際記述はterraformのローカル名ではなく、awsのリソース名を使用します。

// iam_policy_attachment.tf
resource "aws_iam_role_policy_attachment" "lambda_logging_attach" {
  role = aws_iam_role.lambda_s3_trigger_role.name
  policy_arn = aws_iam_policy.lambda_logging_s3.arn
}

今回、「role.tf」「policy.tf」「iam_policy_attachment.tf」をIAMというフォルダ配下に置いているので、「output.tf」を作成してモジュール化を行い、他でも使えるようにします。

// output.tf
output "lambda_s3_trigger_role_arn" {
  value = aws_iam_role.lambda_s3_trigger_role.arn
  // 作成したIAMロールのarnを「lambda_s3_trigger_role_arn」という名前で出力
}

[Lambda]

ロールとポリシーの作成完了したので、実際に動かすLambdaの構成と関数の中身を記述していきます。
こちらはチュートリアルにあった、pythonファイルを持ってきました。

# lambda_function.py

import json
import urllib.parse
import boto3

print('Loading function')

s3 = boto3.client('s3')


def lambda_handler(event, context):
    #print("Received event: " + json.dumps(event, indent=2))

    # Get the object from the event and show its content type
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    try:
        response = s3.get_object(Bucket=bucket, Key=key)
        print("CONTENT TYPE: " + response['ContentType'])
        print("S3イベント受信!!")
        return response['ContentType']
    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

作成したファイルはzip化しないとawsで読み込めないので、下記コマンドでzip化を行う

zip [zip化後の名前] [zip化したいファイル名]

次に、Lambdaを記述していきます

// Lambda.tf
module "iam" {
  source = "./IAM"
}
// output.ftで定義したロールを外部モジュールから読み込む

resource "aws_lambda_function" "s3_trigger_lambda" {
  function_name = "s3-trigger-lambda" //aws内での名前
  role          = module.iam.lambda_s3_trigger_role_arn //外部モジュールから読み込んだロール
  handler       = "lambda_function.lambda_handler" //.pyのファイル名.func名
  runtime       = "python3.12"

  filename      = "function.zip" //zip化したファイル名
  source_code_hash = filebase64sha256("function.zip")

  timeout       = 10
  // 最大実行時間を10秒に設定
}

[S3]

最後に、オブジェクトを入れるS3を作成します。
「aws_s3_bucket_public_access_block」はデフォルトでtrueですが、terrafromの管理では明記しておきます。

// S3.tf
resource "aws_s3_bucket" "test" {
  bucket = "S3バケット名"
}

resource "aws_s3_bucket_public_access_block" "test" {
  bucket                  = aws_s3_bucket.test.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

S3トリガーでLambdaを呼び出すので、呼び出しを明示的に許可するものを作成します。S3から呼び出されることを許可するLambda側の許可です。

principalとsource_arnを絞って指定することで最小限の権限で許可となります。

// lambda_parmisson.tf
resource "aws_lambda_permission" "allow_s3" {
  statement_id  = "AllowExecutionFromS3" //任意で一意な文字列
  action        = "lambda:InvokeFunction" //関数の実行
  function_name = aws_lambda_function.s3_trigger_lambda.function_name
  // terraformのローカル名.function_nameとすることで、Lambda.tfに記述のawsの名前が指定される
  principal     = "s3.amazonaws.com"// 呼び出し元のサービス(今回はS3)
  source_arn    = aws_s3_bucket.test.arn//さらに詳しくS3を指定
}

次はS3からLambdaに向けての通知を許可する設定を作成します。

// s3_bucket_notification.tf
resource "aws_s3_bucket_notification" "bucket_notify" {
  bucket = aws_s3_bucket.test.id

  lambda_function {
    lambda_function_arn = aws_lambda_function.s3_trigger_lambda.arn
    events              = ["s3:ObjectCreated:*"]
  }

  depends_on = [aws_lambda_permission.allow_s3] //明示的なリソースの依存関係を示す
}

Terraformのファイルの作成とLambdaのpythonファイルzip化が完了したので、terraformをapplyしていきます。

まずは「初期化」をしてセットアップしていきます

terraform init

次に作成したterraformファイルによって何のリソースが作成されるか確認します。
ここで「+ create」でリソースが表示されれば大丈夫です。

terraform plan

確認が完了したら、AWSにリソースを作成します。

terraform apply

「Do you want to perform these actions?」と聞かれるので、「yes」と回答します!

少し待つと
「Apply complete! Resources: 〇 added, 〇 changed, 〇 destroyed.」と表示されるので、これにて完了です。

terraformからroleを作成する際に、既に作成済みのroleと同じ名前を付けてしまっていて、エラーになった
対処法

terraform import aws_iam_role.lambda_s3_trigger_role_test test
// Terraform内識別名: lambda_s3_trigger_role_test
// AWS IAMロール名: test(既存リソース)

上記を行うことによって、被ってしまったrole名のものを新規作成するのではなく、既存のものをterraformで管理するようにした
こうすることでGUIや以前手動で作成したものまでterraformで管理することができる

参考:https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?