Terragrunt で躓いたところとその解決方法

この記事は terraform Advent Calendar 2023の12日目です

https://qiita.com/advent-calendar/2023/terraform

# Terragrunt を使って Terraform のリソースを管理

最近 O'Reilly Japan から [詳解 Terraform 第3版](https://www.oreilly.co.jp/books/9784814400522/) が出ました。

その中で Terragrunt がおすすめされていたので、Terraform リソースの管理にはあまり困っていないものの個人的に導入してみることにしました。

いくつか躓いたポイントがあったため、他の方の参考になればと思い記事を書くことにしました。

なお導入にあたって次の記事を参考にさせていただきました。

https://qiita.com/ssc-ksaitou/items/c3eedd46a5eb04d731cc

執筆者の方に感謝するとともに、補足のような記事を書ければと思います。

Terragrunt がどのようなものかは上記の記事や[公式ドキュメント](https://terragrunt.gruntwork.io/docs/)を参考にしてください。

## 躓きポイント

### 環境ごとの remote state の設定

先ほど参照させていただいている記事だと全環境に対して同じ S3 バケットで .tfstate ファイルを管理する記述になっていましたが、 .tfstate ファイルを環境ごとに別バケットで管理したいため、その方法を調べました。

結論としては各環境ディレクトリに `terragrunt.hcl` を置けばよいです。

```terraform:envs/staging/terragrunt.hcl
remote_state {
  backend = "gcs"
  config = {
    bucket  = "tfstate_bucket"
    prefix  = path_relative_to_include()
    project = "gcp-project-staging"
  }
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite"
  }
}
```

モジュールの呼び出し側では次のように書くことで上記のファイルの設定を引き継ぐ事ができます。

```terraform:envs/staging/modA/terragrunt.hcl
include {
  path = find_in_parent_folders()
}
```


### map 型の変数の参照
`staging/env.hcl` にて map 型の変数を次のように定義したところエラーがでました。
```terraform
locals {
  map_variable = {
    variable = 'value'
  }
}
```

エラーを見るとどうも
```
  {
    variable = 'value'
  }
```
を単なる文字列として読み込んでいたようです。
このエラーを解消するには module の variables.tf 内で型を定義する必要があります。

:::note info
実行できる例
```terraform:modules/modA/variables
variable "map_variable" {
  description = "map 型の変数"
  type        = map(any)
}
```
:::

:::note alert
だめな例
```terraform:modules/modA/variables
variable "map_variable" {
  description = "map 型の変数"
}
```
:::

型の定義を怠けていたのがバレてしまいました。
ちゃんと型を定義しましょう。

### provider の設定の EOF 中での var の使用
これは躓いたわけではないのですが、以下の定義中の var を使えるか疑心暗鬼になりつつ実行したら普通に使えました。

```terraform 
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.8.0"
    }
  }
}
provider "google" {
  project = var.project_id
  region  = var.region
}
EOF
}
```

EOF で囲んでいるため、 var で定義している project_id と region に値が入らないかと思いましたが、 `.terragrunt-cache` 内で生成されている `provider.tf` で普通に `var.project_id` と `var.region` が使えているわけですね。当たり前ですが実行するまでちょっと悩みました。

```terraform:envs/staging/modA/.terragrunt-cache/hash/provider.tf
# Generated by Terragrunt. Sig: 
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.8.0"
    }
  }
}
provider "google" {
  project = var.project_id
  region  = var.region
}
```

### terragrunt import の使い方

module に対して `terraform import` を実行するときは次のように書きます。
`terraform import module.modA.service_name.resource_name resource_id`
同じように terragrunt import を実行してもうまくいきませんでしたが、単に module.modA の部分を消して実行することでうまくいきました。

:::note info
実行できる例
```
terragrunt import service_name.resource_name resource_id
```
:::

:::note alert
だめな例
```
terragrunt import module.modA.service_name.resource_name resource_id
```
:::


# まとめ

Terragrunt は便利ですが、まだまだ日本語の技術記事が少ない印象です。この記事が Terragrunt を使い始めたばかりの方に役立つと幸いです。

また Terragrunt は2023年12月現在の最新 version が `0.54.0` と若いサービスです。
今後 Terragrunt そのものも、活用事例も発展していくことを楽しみにしています。