TerraformでAWS Lambdaをデプロイする方法

# やりたいこと
・TerraformでAWS Lambdaをデプロイしたい
・ローカルでlambdaソースを編集したらAWSへ連動できるか検証したい

# 前提
・AWSアカウント作成済み
・AWS IAMユーザーを作成し、access_keyとsecret_keyを発行済み
・Terraformインストール済み
・AWS CLIインストール済み
・VSCODEインストール済み
　（お好みのエディターで大丈夫）
・Pythonインストール済み
　（今回はpython3.9でlambadaを作ってみたいですが、極簡単なソースコードなのでバージョンどうてもいいかと思う）

# 環境
```
$ terraform -v 
Terraform v1.5.5
on windows_amd64
+ provider registry.terraform.io/hashicorp/aws v5.13.1

$ python -V
Python 3.9.13
```
# 構築リソース
・AWS Lambda function
・AWS IAM role
・AWS IAM policy

# ディレクトリ構成
必要最小限のソース構成ですが

```
root
  ┣━ lambda
  ┃  ┗━ test
  ┃     ┗━ src
  ┃        ┣━ test_terraform.py
  ┃        ┗━ test_terraform.zip     　　   
  ┣━ main.tf
  ┣━ variables.tf
  ┗━ lambda.tf
```
:::note info
※補足
・lambdaフォルダに複数lambda functionのソースコードを入れる予定でtestフォルダで階層を掘ってますが、適宜階層を変えてもらって大丈夫
・test_terraform.zipはterraform実行で自動で作られたやつで、自分で用意する必要がない（後述説明）
:::

# ソースの中身
### main.tf
・main.tfには基本、全体共通で重要な設定、例えば「AWS プロバイダの設定」を書く
・lambda関数実行時、cloudWatchへログを書き込むのに権限が必要、今回は三つの権限を付与してあげたいと思う
・今回は便宜上そのIAM設定を一緒にmain.tfに入れた。別途iam.tfで分けて作っても大丈夫

```ruby:main.tf
# AWS プロバイダの設定
provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.region
}

# lambda用Roleの設定
resource "aws_iam_role" "lambda_iam_role" {
  name = "terraform_lambda_iam_role"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
POLICY
}

# lambda用Policyの作成
resource "aws_iam_role_policy" "lambda_access_policy" {
  name   = "terraform_lambda_access_policy"
  role   = aws_iam_role.lambda_iam_role.id
  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:CreateLogGroup",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    }
  ]
}
POLICY
}

```
### variables.tf
・variables.tfに全ソース共通の変数を書く
・リージョンは東京にしたが、適宜変更して大丈夫
・access_keyとsecret_keyは事前に発行したやつを使用

```ruby:variables.tf
variable "region" {
  default = "ap-northeast-1"
}

variable "access_key" {
  default = "自分のaccess_key"
}

variable "secret_key" {
  default = "自分のsecret_key"
}
```
### lambda.tf
・terraformのdataで、ローカルにあるpythonのソースのパスを教えてあげて、後でterraform plan実行時にzip化してくれて便利！
・terraform apply時は、source_code_hash（さっき作ったソースのzipファイル）をlambdaにアップしてくれる

```ruby:lambda.tf
# ローカルにあるlambdaのソースコード
data "archive_file" "test_terraform" {
  type        = "zip"
  source_dir  = "lambda/test/src"
  output_path = "lambda/test/src/test_terraform.zip"
}

# AWSへ作るlambda function
resource "aws_lambda_function" "test_terraform" {
  function_name    = "test_terraform"
  filename         = data.archive_file.test_terraform.output_path
  source_code_hash = data.archive_file.test_terraform.output_base64sha256
  runtime          = "python3.9"
  role             = aws_iam_role.lambda_iam_role.arn
  handler          = "test_terraform.handler"
}
```
### test_terraform.py
・どうでもいいテストの内容ですwww

```ruby:test_terraform.py
def handler(event, context):
    print('test')

    return()
```

# Terraformでlambda functionを新規作成
念のため初期化してあげよう

```
terraform init
```
これが出たら成功かな
```ruby:実行結果
Terraform has been successfully initialized!
```
次にterraformの実行計画を確認しよう
```
terraform plan
```
新規ファイル、もしくは増分か差分がある場合、こういうふうに結果が出てくる
```ruby:実行結果
Plan: 4 to add, 0 to change, 0 to destroy.
```
terraform planを実行後、zip化したpythonのファイルが出てくるはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/02a4585a-2596-1bfd-08f8-7ea5a15368ca.png)
そして、AWSへデプロイしよう！
```
terraform apply
```
ちゃんと成功したそうです！
今回は新規作成なので、「4 added」と表示してますが、今後修正分を反映するときは、changed（増分差分の反映）かdestroyed（削除）となるはず。
```ruby:実行結果
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
```
AWS側を確認すると、AWS functionが立派にできてます！ちゃんとpython3.9になってますね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f670c45b-61f6-6bb8-ff44-309bf36ee954.png)
AWS functionのソースを見てみると、zipファイルがちゃんとアップされ、ソースの中身は無事書き込まれた！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/afd0e34c-0273-2dac-f7d0-2fa08d863e81.png)
そして、lambda functionの持ってるIAM権限を確認すると、さっき作ってあげた三つの権限はちゃんと付与されてます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/ec02e42e-51d1-75a5-dc50-d22b143cbf8d.png)
ついてに、IAMも確認しよう！
lambdaへ付与したいロールは作られて、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/b74ab701-b99f-ec74-0db8-8eba4499a506.png)
cloudWatchへログを書き込むための三つの権限を持ってるpolicyも作られた！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/6e4c7fe8-fcdc-26ef-10c7-9542dc0a588a.png)
最後、AWSで一回lambda functionを実行したうえ、cloudWatchを確認しよう！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/30307974-3c72-cdb7-5bdf-1d39b99bdfd5.png)
lambda function専用のロググループが作られて、さっき実行した記録も残ってますね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/c6177b10-fa54-2121-28c0-6eb54d0c7ab4.png)

# ローカルでlambdaのソースコードを編集
先ほどterraformで作られたlambada functionはちゃんとterraformの管理配下になってるか知りたいので、少しtest_terraform.pyを修正してみよう
```ruby:test_terraform.py
def handler(event, context):
    print('test again')

    return()
```
そして、もう一度コマンドを実行
```
terraform plan
```
一つ変更があるよって、教えてくれた
```ruby:実行結果
Plan: 0 to add, 1 to change, 0 to destroy.
```
この時点でzipも作り直してくれたようです（日付が更新された）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/89308685-65bd-83e6-1153-6588e03cde7b.png)

では変更した分をAWSへデプロイしようと、
```
terraform apply
```
一個変更分をデプロイしたって、言ってますね
```ruby:実行結果
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
```
最後、AWS側を確認すると、ソースコードをちゃんと最新化してくれた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4bc9dc7d-efa4-8ba0-02ea-e1c0b381bf16.png)

# 終わりに
お疲れ様です！今回のやりたいことが無事終わった！
・TerraformでAWS Lambdaをデプロイしたい　
　⇒デプロイできた
・ローカルでlambdaソースを編集したらAWSへ連動できるか検証したい　
　⇒連動できる、terraform planで管理されてる