AWS Lambdaを定期的に実行するためにEventBridgeを設定する [Terraformを使用]

## 要約
最終的なソースコード全体は以下のリンクからご確認いただけます。

https://github.com/mochi-yu/qiita-material/tree/main/lambda-with-event-bridge-by-terraform

## 関連記事
Go言語で記述したスクリプトを、**AWSのLambdaにTerraformを用いてデプロイする**機会があり、その過程の試行錯誤を整理して一連の記事にまとめました。

この記事はそのうちの3本目として、GoのスクリプトをAWS EventBridgeから定期実行する構成をデプロイしてみたいと思います。

- [zipファイルを用いてGo言語の処理をAWS Lambdaにデプロイする](https://qiita.com/mochi_2225/items/1b645f87148899f1bc09)
- [Dockerイメージを用いてGo言語の処理をAWS Lambdaにデプロイする](https://qiita.com/mochi_2225/items/000ee604b3a274b6b62d)
- **EventBridgeからAWS Lambdaを定期実行する処理をデプロイする**（この記事）

## 概要
AWS Lambdaの使い道は色々ありますが、その1つにバッチ処理のように**定期的に実行したい処理を行う**というのもあるかと思います。例えば定時にEC2のインスタンスが起動しているのかを確認したり、毎時ヘルスチェックをしたり、といった用途です。

そこでこの記事では、**Amazon EventBridgeをトリガーに定期的に処理が実行されるLambda関数を、Terraformでデプロイする**ということを目指します。なお、Lambda関数をTerraformでデプロイするというのは、上記にあげた記事で説明していますので、こちらをご参照ください。

## 実装
今回のディレクトリ構造は以下の様になっています。以前までのLambda関数の記事からの続きで、**Dockerを用いてデプロイする方法**を利用したいと思います。

`cloud_watch.tf`、`event-bridge-assume-role.json`、`event-bridge.tf`のファイルが追加されて、`lambda.tf`を一部修正しました。

```
.
├── .gitignore
├── docker
│   └── Dockerfile
├── image_digest.txt
├── src
│   ├── go.mod
│   ├── go.sum
│   └── main.go
└── terraform
    ├── .gitignore
    ├── .terraform
    ├── .terraform.lock.hcl
    ├── cloud_watch.tf
    ├── ecr.tf
    ├── event-bridge-assume-role.json
    ├── event-bridge.tf
    ├── lambda-assume-role.json
    ├── lambda.tf
    ├── main.tf
    └── terraform.tfstate
```

### ログを見られるように設定
初めに、**Lambda関数の実行ログをCloudWatch Logsに記録**しておき、後から見られるように設定しておきます。本当に定期的に実行されているのかを確認するためです。

まず以下の様に**Lambdaのロールに`AWSLambdaBasicExecutionRole`のポリシーを追加**します。このポリシーは[公式のドキュメント](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/monitoring-cloudwatchlogs.html)にもあるように、CloudWatchへのログ書き込みの最低限の権限が用意されています。

```diff_terraform:./terraform/lambda.tf（一部抜粋）
resource "aws_iam_role" "lambda_role" {
  name = "role-for-test_lambda"
  assume_role_policy = file("lambda-assume-role.json")
+ managed_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ]
}
```

それから、CloudWatchのロググループについてもTerraformで定義しておきます。念の為ログの保持期間(`retention_in_days`)も3日と短めに設定しておきましょう。

```terraform:./terraform/cloud_watch.tf
resource "aws_cloudwatch_log_group" "test_lambda" {
  name              = "/aws/lambda/${aws_lambda_function.test_lambda.function_name}"
  retention_in_days = 3
  skip_destroy      = false
}
```

### EventBridgeのIAMの設定
続いて、**定期的にLambdaを呼び出し実行するEventBridge**の権限を設定しておきます。欲しい権限は「**対象のLambdaを実行する**」権限です。以下のようにactionとして`"lambda:InvokeFunction"`を、resourcesとして`terraform.tf`で定義している`aws_lambda_function.test_lambda`のarnを指定します。

```terraform:./terraform/event-bridge.tf
################## IAMの設定 ##################
resource "aws_iam_role" "event_bridge" {
  name = "role-for-test_lambda-event_bridge"
  assume_role_policy = file("event-bridge-assume-role.json")
}

resource "aws_iam_role_policy" "event_bridge" {
  name = "role_policy-for-test_lambda-event_bridge"
  role = aws_iam_role.event_bridge.name
  policy = data.aws_iam_policy_document.event_bridge.json
}

data "aws_iam_policy_document" "event_bridge" {
  statement {
    effect = "Allow"

    actions = [
      "lambda:InvokeFunction",
    ]

    resources = [
      aws_lambda_function.test_lambda.arn,
    ]
  }
}
```

```json:./terraform/event-bridge-assume-role.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "scheduler.amazonaws.com"
      },
      "Sid": "",
      "Effect": "Allow"
    }
  ]
}
```

### EventBridge本体の設定

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule

最後に**EventBridge本体の設定**をしていきます。**定期実行**のタイミングは[**`cron`式で設定**](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-cron-expressions.html)することができます。Linuxに慣れている人なら取っ付きやすいですね。以下の例なら`schedule_expression`に`"cron(0/3 * * * ? *)"`と設定し、**3分おきにLambda関数が実行**されるようにしています。

:::note warn
確認しやすいように実行間隔を短くしています。実行し過ぎによる課金などにはご注意ください。
:::

どのLambda関数を実行するかを以下の`target`ブロックに書いていきます。実行対象のLambdaのと、実行ためのIAMロール（`role_arn`）の`arn`を指定しています。これらの`arn`は、今まで定義してきたリソースの属性を参照しています。

```terraform:./terraform/event-bridge.tf
################## EventBridgeの本体 ##################
resource "aws_scheduler_schedule" "test_lambda" {
  name       = "test_lambda-event_bridge"

  schedule_expression          = "cron(0/3 * * * ? *)"
  schedule_expression_timezone = "Asia/Tokyo"

  flexible_time_window {
    mode = "OFF"
  }

  target {
    arn      = aws_lambda_function.test_lambda.arn
    role_arn = aws_iam_role.event_bridge.arn
  }
}
```

### 動作確認
これで`terraform apply`として適用すると、以下のログのように3分おきに関数が実行され、その様子がログとして出力されていることが確認できました。

![event_bridge-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/3da27c7e-151f-8945-0a71-166ac73b8890.png)

### 参考

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/monitoring-cloudwatchlogs.html