🦍

Terraformでファイルの差分を検知する

2024/06/30に公開

はじめに

最近ちょろっとTerraformでLambdaを構築しました。
その際にコードが変更されたら差分検知してデプロイしたくて試行錯誤したので、備忘録としてそのやり方を残します。

サンプルのリポジトリを用意しているので、全体像を知りたい方はこちらも参照してください。

https://github.com/skanehira/terraform-detect-diff-example

環境

Treraformは1.9.0を使っています。
また、ステート管理はs3にバケットを用意したのと、同時applyできないようにdynamodbを使っています。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.54.1"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6.2"
    }
  }

  required_version = ">= 1.8.5"

  backend "s3" {
    profile        = "techlead"
    region         = "ap-northeast-1"
    bucket         = "xxxx"
    dynamodb_table = "xxxx"
    key            = "terraform.tfstate"
    encrypt        = true
  }
}

差分検知について

サンプルリポジトリの構成をもとに説明します。

$ tree -L 1
.
├── LICENSE
├── README.md
├── compose.yaml
├── lambda
└── terraform

今回はLambdaのコードがあるディレクトリ配下のファイル一覧をfileset()で取得してさらにregexall()を使って拡張子がgosummodのファイルのみを絞りこみます。

terraform/lambda.tf
locals {
  binary_path  = "build/bootstrap"
  archive_path = "build/handler.zip"
}

locals {
  target_files = [
    for file in fileset("../lambda", "**") : file if length(regexall(".*\\.(go|sum|mod)$", file)) != 0
  ]
}

これらのファイルのハッシュ値を結合して、terraform変数長の上限を回避するためさらにハッシュ値を取得します。
これで対象のファイルに変更があれば、ハッシュ値が変わります。

terraform/lambda.tf
locals {
  concatenated_hashes = sha256(join("", [for file in local.target_files : filesha256(join("", ["../lambda/", file]))]))
}

terraform_data.triggers_replaceを使って、算出したハッシュ値concatenated_hashesが変わったら、terraform_dataリソースを再評価させています。
これによりバイナリが変わり、depends_onで依存を指定してるarchive_fileaws_lambda_functionも再評価され、ステートが更新されデプロイされます。

terraform/lambda.tf
resource "terraform_data" "function_binary" {
  triggers_replace = [
    local.concatenated_hashes,
  ]

  provisioner "local-exec" {
    working_dir = "../lambda"
    environment = {
      GOOS        = "linux"
      GOARCH      = "amd64"
      CGO_ENABLED = "0"
      GOFLAGS     = "-trimpath"
    }
    command = "go build -mod=readonly -ldflags='-s -w' -o ${local.binary_path} ."
  }
}

data "archive_file" "example" {
  depends_on = [
    terraform_data.function_binary
  ]
  type        = "zip"
  source_file = "../lambda/${local.binary_path}"
  output_path = "../lambda/${local.archive_path}"
}

resource "aws_lambda_function" "example" {
  depends_on = [
    terraform_data.function_binary
  ]
  filename         = "../lambda/${local.archive_path}"
  function_name    = "example"
  role             = aws_iam_role.lambda_execution.arn
  handler          = "handler"
  runtime          = "provided.al2"
  source_code_hash = data.archive_file.example.output_base64sha256
  timeout          = "900"
}

まとめ

正直このやり方では、ファイル数が多くなるとハッシュ値計算時間が伸びるので、あんまり行けていないなと感じています。
調べてもあんまり情報がなかったのでひとまずこれでやっていくんですが、もしこうした方がよいよって方がいたら是非コメントください。

Discussion