terragruntについて

# terragruntとは

docs: https://terragrunt.gruntwork.io/

Terragruntは、Terraformのラッパーとして機能するオープンソースのツールです。Terraformのコードを管理し、構成を簡素化するために使用されます。Terragruntは、Terraformの機能を拡張し、コードの再利用性、メンテナンス性、構成の一貫性を向上させることを目的としています。

Terragruntの主な特徴と利点は以下の通りです：

1. コードの再利用：
    - Terragruntでは、Terraformのコードをモジュール化し、再利用可能な構成要素として管理できます。
    - 複数のプロジェクトや環境間でコードを共有し、重複を減らすことができます。
2. 設定の継承：
    - Terragruntでは、設定ファイル（`terragrunt.hcl`）を使用して、共通の設定を定義できます。
    - 子モジュールは、親の設定を継承し、必要に応じてオーバーライドできます。
3. DRY（Don't Repeat Yourself）原則の適用：
    - Terragruntを使用すると、Terraformコードの重複を最小限に抑えることができます。
    - 共通の設定、バックエンド構成、プロバイダ設定などを一元管理できます。
4. マルチ環境管理：
    - Terragruntでは、異なる環境（開発、ステージング、本番など）ごとに設定を管理できます。
    - 環境固有の値をTerragruntの設定ファイルで定義し、コードを再利用できます。
5. フック（Hooks）：
    - Terragruntでは、Terraformのライフサイクル（init、plan、applyなど）にフックを追加できます。
    - フックを使用して、カスタムスクリプトの実行、検証、通知などを行うことができます。
6. リモートステート管理：
    - Terragruntは、Terraformのリモートステート管理を簡素化します。
    - バックエンド設定を一元管理し、複数のTerraformモジュール間でステートを共有できます。

Terragruntを使用する場合、プロジェクトのルートに`terragrunt.hcl`ファイルを作成し、共通の設定を定義します。サブディレクトリには、Terraformのコードと、必要に応じて追加の`terragrunt.hcl`ファイルを配置します。

以下は、Terragruntの設定ファイルの例です：

```bash
# terragrunt.hcl

terraform {
  source = "path/to/terraform/module"
}

inputs = {
  region = "us-west-2"
  environment = "staging"
}
```

この設定ファイルでは、Terraformモジュールのソースパスを指定し、`inputs`ブロックでモジュールに渡す変数を定義しています。

# なぜterraformではなくterragruntを使うのか

Terragruntを使用する主な理由は、Terraformのコードを管理し、構成を簡素化するためです。以下に、TerraformではなくTerragruntを使用する利点を詳しく説明します。

1. コードの再利用と構成の継承：
    - Terraformでは、コードの再利用や構成の継承が限定的です。
    - Terragruntを使用すると、コードをモジュール化し、再利用可能な構成要素として管理できます。
    - 共通の設定を親の`terragrunt.hcl`ファイルで定義し、子モジュールで継承できます。
    - これにより、コードの重複が減り、メンテナンス性が向上します。
2. DRY原則の適用：
    - Terraformでは、同じ設定を複数の場所で繰り返し定義する必要があることがあります。
    - Terragruntでは、共通の設定、バックエンド構成、プロバイダ設定などを一元管理できます。
    - 設定の重複を最小限に抑え、コードの保守性を高めることができます。
3. マルチ環境管理の簡素化：
    - Terraformでは、異なる環境ごとに別々のコードを管理する必要があります。
    - Terragruntでは、環境固有の設定を`terragrunt.hcl`ファイルで定義し、コードを再利用できます。
    - 環境ごとの差分を最小限に抑え、コードの一貫性を保つことができます。
4. フックによるカスタムロジックの実行：
    - Terraformでは、カスタムロジックの実行が制限されています。
    - Terragruntでは、フックを使用して、Terraformのライフサイクルにカスタムスクリプトを追加できます。
    - 検証、通知、追加のリソース作成などのタスクを実行できます。
5. リモートステート管理の簡素化：
    - Terraformでは、リモートステートの設定を各モジュールで繰り返し定義する必要があります。
    - Terragruntでは、バックエンド設定を一元管理し、複数のモジュール間でステートを共有できます。
    - リモートステートの設定を簡素化し、一貫性を保つことができます。
6. 学習曲線が緩やか：
    - Terragruntは、Terraformの上に構築されているため、Terraformの知識を活かすことができます。
    - Terraformの構文や概念をそのまま使用でき、追加の学習コストが最小限に抑えられます。

Terragruntは、Terraformのコードを管理し、構成を簡素化するための強力なツールですが、すべてのユースケースに適しているわけではありません。プロジェクトの規模、複雑さ、チームの要件に基づいて、TerraformとTerragruntのどちらを使用するかを評価する必要があります。

小規模なプロジェクトや単一の環境での作業には、Terraformだけで十分な場合があります。しかし、コードの再利用性、メンテナンス性、構成の一貫性が重要な場合は、Terragruntを使用することで大きなメリットが得られます。

# terragruntの使い方

Terragruntの使い方を以下にステップバイステップで説明します。

1. Terragruntのインストール：
    - Terragruntは、Go言語で書かれた単一のバイナリファイルです。
    - 公式ウェブサイト（[https://terragrunt.gruntwork.io/）からお使いのオペレーティングシステム用のバイナリをダウンロードします。](https://terragrunt.gruntwork.io/%EF%BC%89%E3%81%8B%E3%82%89%E3%81%8A%E4%BD%BF%E3%81%84%E3%81%AE%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E7%94%A8%E3%81%AE%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)
    - ダウンロードしたバイナリを`PATH`の通ったディレクトリに移動します。
2. プロジェクト構造の作成：
    - Terragruntを使用するプロジェクトのディレクトリ構造を作成します。
    - プロジェクトのルートに`terragrunt.hcl`ファイルを作成し、共通の設定を定義します。
    - サブディレクトリには、Terraformのコードと、必要に応じて追加の`terragrunt.hcl`ファイルを配置します。
3. `terragrunt.hcl`ファイルの作成：
    - プロジェクトのルートに`terragrunt.hcl`ファイルを作成します。
    - このファイルで、Terraformのバックエンド設定、プロバイダ設定、共通の変数などを定義します。
    
    ```bash
    terraform {
      backend "s3" {
        bucket = "my-terraform-state"
        key    = "path/to/my/terraform.tfstate"
        region = "us-west-2"
      }
    }
    
    inputs = {
      aws_region = "us-west-2"
    }
    ```
    
    1. Terraformモジュールの作成：
    - サブディレクトリに、Terraformのコードを配置します。
    - 必要に応じて、モジュール固有の`terragrunt.hcl`ファイルを作成し、モジュール固有の設定を定義します。
    
    ```bash
    terraform {
      source = "path/to/module"
    }
    
    include {
      path = find_in_parent_folders()
    }
    
    inputs = {
      instance_type = "t2.micro"
    }
    ```
    
    1. Terragruntコマンドの実行：
        - Terragruntコマンドを使用して、Terraformのアクションを実行します。
        - `terragrunt init`：Terraformの初期化を行います。
        - `terragrunt plan`：実行計画を表示します。
        - `terragrunt apply`：変更を適用します。
        - `terragrunt destroy`：リソースを削除します。
    2. 複数の環境の管理：
        - 異なる環境（開発、ステージング、本番など）ごとにディレクトリを作成します。
        - 各環境ディレクトリに、環境固有の`terragrunt.hcl`ファイルを配置し、環境固有の設定を定義します。
        - 共通の設定は、プロジェクトのルートまたは共通ディレクトリの`terragrunt.hcl`ファイルで定義します。
    
    Terragruntを使用する際は、プロジェクトの構造と設定ファイルの階層に注意してください。`terragrunt.hcl`ファイルを適切に配置し、設定の継承と上書きを活用することで、コードの再利用性と管理性が向上します。
    
    また、Terragruntには、フック、依存関係管理、変数の暗号化など、他にも多くの機能があります。これらの機能の使い方は、プロジェクトの要件に応じて異なります。
    

# 実際に使ってみた例

以下は、Terragruntを使用して、AWSにVPCとEC2インスタンスを作成する例です。

プロジェクト構造：

```bash
└── infrastructure/
    ├── terragrunt.hcl
    ├── vpc/
    │   ├── main.tf
    │   ├── outputs.tf
    │   ├── terragrunt.hcl
    │   └── variables.tf
    └── ec2/
        ├── main.tf
        ├── outputs.tf
        ├── terragrunt.hcl
        └── variables.tf
```

プロジェクトのルートに`terragrunt.hcl`ファイルを作成します：

```bash
remote_state {
  backend = "s3"
  config = {
    bucket         = "my-terraform-state"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-west-2"
    encrypt        = true
    dynamodb_table = "my-lock-table"
  }
}

inputs = {
  aws_region = "us-west-2"
  environment = "staging"
}
```

`vpc`ディレクトリに`terragrunt.hcl`ファイルを作成します：

```bash
include {
  path = find_in_parent_folders()
}

terraform {
  source = "terraform-aws-modules/vpc/aws"
  version = "~> 2.0"
}

inputs = {
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  azs  = ["us-west-2a", "us-west-2b"]
}
```

`ec2`ディレクトリに`terragrunt.hcl`ファイルを作成します：

```bash
include {
  path = find_in_parent_folders()
}

terraform {
  source = "terraform-aws-modules/ec2-instance/aws"
  version = "~> 2.0"
}

dependency "vpc" {
  config_path = "../vpc"
}

inputs = {
  name          = "my-ec2-instance"
  instance_type = "t2.micro"
  vpc_security_group_ids = [dependency.vpc.outputs.default_security_group_id]
  subnet_id              = dependency.vpc.outputs.public_subnets[0]
}
```

`vpc`ディレクトリと`ec2`ディレクトリに、Terraformのコードを配置します。

Terragruntコマンドを実行します：

```bash
cd infrastructure/vpc
terragrunt init
terragrunt apply

cd ../ec2
terragrunt init
terragrunt apply
```

この例では、`vpc`ディレクトリでVPCを作成し、`ec2`ディレクトリでEC2インスタンスを作成しています。`terragrunt.hcl`ファイルを使用して、リモートステートの設定、モジュールの指定、変数の定義、依存関係の管理を行っています。

`include`ブロックを使用して、親ディレクトリの設定を継承しています。これにより、AWS領域や環境などの共通設定を一元管理できます。

`dependency`ブロックを使用して、VPCとEC2インスタンス間の依存関係を定義しています。これにより、VPCが作成された後にEC2インスタンスが作成されるようになります。

この例は、Terragruntを使用してインフラストラクチャをモジュール化し、設定を管理する方法を示しています。実際のプロジェクトでは、要件に応じてより複雑な構成やカスタマイズが必要になる場合があります。

## terraformのコードの配置例

Terraformのコードを配置するとは、Terraformで管理するインフラストラクチャのリソース定義や設定をファイルとして用意することを指します。これらのファイルは、`.tf`または`.tf.json`の拡張子を持ち、Terraformの構文に従って記述されます。

以下は、`vpc`ディレクトリと`ec2`ディレクトリに配置するTerraformのコード例です。

`vpc`ディレクトリ：

[main.tf](http://main.tf/)

```bash
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = var.name
  cidr = var.cidr

  azs             = var.azs
  private_subnets = var.private_subnets
  public_subnets  = var.public_subnets

  enable_nat_gateway = var.enable_nat_gateway
  single_nat_gateway = var.single_nat_gateway
}
```

1. `variables.tf`
    
    ```
    variable "name" {}
    variable "cidr" {}
    variable "azs" {}
    variable "private_subnets" {}
    variable "public_subnets" {}
    variable "enable_nat_gateway" {}
    variable "single_nat_gateway" {}
    
    ```
    
2. `outputs.tf`
    
    ```
    output "vpc_id" {
      value = module.vpc.vpc_id
    }
    output "public_subnets" {
      value = module.vpc.public_subnets
    }
    output "default_security_group_id" {
      value = module.vpc.default_security_group_id
    }
    
    ```
    

`ec2`ディレクトリ：

1. `main.tf`
    
    ```
    module "ec2_instance" {
      source = "terraform-aws-modules/ec2-instance/aws"
    
      name          = var.name
      instance_type = var.instance_type
      key_name      = var.key_name
    
      vpc_security_group_ids = [var.vpc_security_group_id]
      subnet_id              = var.subnet_id
    
      tags = var.tags
    }
    
    ```
    
2. `variables.tf`
    
    ```
    variable "name" {}
    variable "instance_type" {}
    variable "key_name" {}
    variable "vpc_security_group_id" {}
    variable "subnet_id" {}
    variable "tags" {}
    
    ```
    
3. `outputs.tf`
    
    ```
    output "instance_id" {
      value = module.ec2_instance.id
    }
    output "public_ip" {
      value = module.ec2_instance.public_ip
    }
    
    ```
    
    これらのコードは、Terraformの`module`を使用して、VPCとEC2インスタンスを作成しています。`variables.tf`ファイルで変数を定義し、`main.tf`ファイルでモジュールを呼び出し、変数を渡しています。`outputs.tf`ファイルでは、モジュールの出力値を定義しています。
    
    実際のプロジェクトでは、これらのコードファイルをバージョン管理システム（Gitなど）で管理し、チームメンバー間で共有します。Terragruntを使用する場合は、これらのコードファイルを適切なディレクトリ構造に配置し、`terragrunt.hcl`ファイルで設定を管理します。