Terragruntってのがあるらしい

# この記事について
記載しているのはすべてTerragruntの公式にある内容です。(役に立つか知りませんが、githubで[sandbox](https://github.com/naruse666/terragrunt-sandbox)を作りました。)
どう使うかではなく、***どのように使えるのか***に焦点を当てています。

# Terragruntって何？
## 概要
[Terragrunt](https://terragrunt.gruntwork.io/)とは、[Terraform](https://www.terraform.io/)のラッパーツールで公式にも出てきますが`DRY(Don't Repeat Yourself)`を謳っています。
要するにterraformのめんどくさいことをこいつにやらせよう！

`terragrunt.hcl`というファイルを扱います。terraformを触ったことある人なら大丈夫です。
terraformとのバージョン対応表なんかも公開されています。
[Supported Terraform Versions](https://terragrunt.gruntwork.io/docs/getting-started/supported-terraform-versions/).

terraformのようにコマンドを実行できる。`terragrunt (apply | plan ....)`

## 導入への不安
- [Considerations for CI/CD Pipelines](https://terragrunt.gruntwork.io/docs/features/keep-your-terragrunt-architecture-dry/#considerations-for-cicd-pipelines)で触れられているように一般的なケースに現在対応できていない。回避策はあるが、余計なトイルが発生する気が。
- [Terraform Registry](https://registry.terraform.io/)をモジュールとして利用する場合には問題ないが、プライベートGitリポジトリを利用する場合のガバナンスコストが未知。
- 既存のプロダクトに導入するにはメリットが少なすぎる。新規プロジェクトの方が導入する価値がある。
- `run-all ...`コマンドというのがあるがCI/CDの際にそれぞれの出力を取得したい場合、一工夫必要。

# 何ができる？
一例にすぎませんがTerragruntで実現できるDRYをいくつか記載します。
## Backend
真っ先に紹介されています。terraformでバックエンドを構成するのは結構めんどくさいですよね。
ルートの`terragrunt.hcl`に定義を加えるだけで子のバックエンドを構成する設定ができます。
バックエンドの実態がない場合(初回作成時)には自らそのバックエンドリソースを作成します。

以下のようなディレクトリ構造だとします。
```
stage
├── terragrunt.hcl <- こいつ
├── frontend-app
│   ├── main.tf
│   └── terragrunt.hcl
└── mysql
    ├── main.tf
    └── terragrunt.hcl
```
ルートの`terragrunt.hcl`が以下のように構成されていると、子に`backend.tf`を生成し、ディレクトリ構造と同じようにtfstateのパスを作ります。
(今回の例では`frontend-app/terraform.tfstate`, `mysql/terraform.tfstate`が生成されます。)
```terraform
# stage/terragrunt.hcl
remote_state {
  backend = "s3"
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = {
    bucket = "my-terraform-state"

    key = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "my-lock-table"
  }
}
```
最終的なディレクトリ構造はこのようになります。
```
stage
├── terragrunt.hcl
├── frontend-app
│   ├── main.tf
│   ├── backend.tf
│   └── terragrunt.hcl
└── mysql
    ├── main.tf
    ├── backend.tf
    └── terragrunt.hcl
```
## Provider
terraformってプロバイダーの指定も面倒ですよね。
以下のように構成して、子に`provider.tf`を生成します。`generate`ブロックは直感的なので特に説明はいらないですね。
```terraform
# stage/terragrunt.hcl
generate "provider" {
  path = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents = <<EOF
provider "aws" {
  assume_role {
    role_arn = "arn:aws:iam::0123456789:role/terragrunt"
  }
}
EOF
}
```
## CLI Arguments
varfile(`.tfvars`)などいちいちコマンドに`-var-file=path`と打つのは面倒です！
そこでTerragruntに自動でくっつけてもらいましょう。

指定のコマンド実行時に自動で引数を指定してくれます。
```terraform
# terragrunt.hcl
terraform {
  extra_arguments "common_vars" {
    commands = ["plan", "apply"]

    arguments = [
      "-var-file=../../common.tfvars",
      "-var-file=../region.tfvars"
    ]
  }
}
```

# 個人的に紹介したい機能・コマンド
### Before and After Hooks
実行前後にトリガーされるコマンドを指定できます。使い方によっては便利かも。
```terraform
terraform {
  before_hook "before_hook" {
    commands     = ["apply", "plan"]
    execute      = ["echo", "Running Terraform"]
  }

  after_hook "after_hook" {
    commands     = ["apply", "plan"]
    execute      = ["echo", "Finished running Terraform"]
    run_on_error = true
  }
}
```
### graph-dependenciesコマンド
`terragrunt graph-dependencies`を実行してモジュールの依存関係を表せます。
(`terraform graph`や[inframap](https://github.com/cycloidio/inframap)のような感じ)

`terragrunt graph-dependencies |dot -Tpng > graph.png`のようにできます。
サンプル：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722539/2b2ffb5e-4758-9c84-c7f4-d285e70b729f.png)

### dependencyブロック
概念的にはterraformのmoduleと同じ。
```terraform
dependency "vpc" {
  config_path = "../vpc"
}

inputs = {
  vpc_id = dependency.vpc.outputs.vpc_id
}
```

# 参考link
[Terragrunt](https://terragrunt.gruntwork.io/)