Terragruntで特定のディレクトリのみプロバイダーを追加したいときの書き方

Terragruntで特定のディレクトリのみプロバイダーを追加したいときの書き方

Terragruntで複数プロバイダーを扱う際に詰まったこととその解決策を紹介します
Clock Icon2025.02.20

お疲れさまです。とーちです。

今回の記事はTerragruntを使ってる方向けの小ネタです。

特定のディレクトリでのみ追加のプロバイダーが必要になった場合の解決方法をご紹介します。

想定読者

この記事は以下のような方を想定しています

  • Terragruntを使っていて、ある程度使い方にも慣れている
  • terragrunt plan/apply するディレクトリを分けて管理している
  • AWS以外の追加プロバイダー(DatadogやSysdigなど)が一部のディレクトリでのみ必要

課題:特定ディレクトリでのみ必要なプロバイダー

Terragruntで複数環境の設定を管理していて、以下のような課題に出くわしました

  • providers設定等の共通部分はterragruntで共通化している
  • ただし、特定のディレクトリでのみ追加のプロバイダーが必要
  • 共通のterraform.tfに全プロバイダーを書くと、不要なディレクトリでも読み込まれてしまう

特に、GitHubActions等のCI/CDパイプラインでTerraformを実行している場合、不要なプロバイダーの読み込みによってデプロイ時間が増加してしまいます。これは避けたいところです。

解決策1:includeファイルの分離アプローチ

最初の解決策として、includeするファイルを分けるアプローチを紹介します。

今回想定しているディレクトリ構成は以下のような形です。

.
├── envs/                          # 環境ごとの設定を格納するディレクトリ
│   └── dev/                     # 特定の環境(例:開発環境、本番環境)
│       ├── env_var.hcl           # 環境変数の定義
│       ├── dev.hcl             # 環境固有のterragrunt設定
│       │
│       ├── notifications/        # 追加のプロバイダー(今回はSysdigプロバイダーを使用)が必要なリソース用
│       │   ├── main.tf          # リソース定義
│       │   └── terragrunt.hcl
│       │
│       └── network/             # AWSプロバイダーのみ必要なリソース用
│           ├── main.tf          # リソース定義
│           └── terragrunt.hcl
│
└── shared/                       # 共通設定ファイルを格納するディレクトリ
    ├── providers.tf             # プロバイダー設定
    └── terraform.tf             # Terraform設定

設定ファイルの分割

terragruntでは個別のディレクトリ(上記構成でいうところのnetworkやnotifications)にterragrunt.hclを置き、プロジェクトのルートディレクトリ等に共通となる設定が書かれたterragruntファイルを置く構成が多いと思います。

上記の例では、個別のディレクトリにあるterragrunt.hclから、dev.hclをincludeで読み込んでます。

このincludeで読み込むファイルを以下の3つに分割し、各ディレクトリでは必要なほうのファイルをincludeするというのがこのアプローチになります。

  • _providers.tfをgenerateするためのproviders.hcl
  • _terraform.tfをgenerateするためのterraform.hcl
  • その他共通設定をまとめたdev.hcl

providers.hclとterraform.hclはデフォルトで使うプロバイダーを記載したもの追加プロバイダーを記載したものの二種類を用意します。

デフォルト用providers.hcl(providers_default.hcl)

こちらは追加のプロバイダーが必要がない場合にincludeするファイルです

generate "provider" {
  path = "_providers.tf"
  if_exists = "overwrite_terragrunt"
  contents = <<EOT
provider "aws" {
  region = "ap-northeast-1"
}
EOT
}

Sysdigプロバイダー追加用providers.hcl(providers_aws_and_sysdig.hcl)

Sysdigプロバイダーを使用するディレクトリ用に、AWS+Sysdig両方のプロバイダー設定を作成します。

generate "provider" {
  path = "_providers.tf"
  if_exists = "overwrite_terragrunt"
  contents = <<EOT
provider "aws" {
  region = "ap-northeast-1"
}

provider "sysdig" {
  sysdig_secure_url       = "https://***"
  sysdig_secure_api_token = jsondecode(ephemeral.aws_secretsmanager_secret_version.sysdig_secret_params_secret_id.secret_string)["sysdig_api_token"]
}
EOT
}

デフォルト用terraform.hcl(terraform_default.hcl)

providers.hclと同様に2種類のファイルを用意します。

こちらはAWSのみを使用するディレクトリ用です。

generate "version" {
  path      = "_terraform.tf"
  if_exists = "overwrite_terragrunt"
  contents  =  <<EOT
terraform {
  required_version = ">= 1.10.3"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.82.2"
    }
  }
}
EOT
}

Sysdigプロバイダー追加用terraform.hcl(terraform_aws_and_sysdig.hcl)

Sysdigを使用する環境向けに、追加のプロバイダー要件を含めた設定です。

generate "version" {
  path      = "_terraform.tf"
  if_exists = "overwrite_terragrunt"
  contents  =  <<EOT
terraform {
  required_version = ">= 1.10.3"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.82.2"
    }
    sysdig = {
      source = "sysdiglabs/sysdig"
      version = ">= 1.46.0"
    }
  }
}
EOT
}

各ディレクトリでの使用方法

実際の使用方法について、具体的なケースごとに説明します。

AWSプロバイダーのみ必要なディレクトリの設定

AWS機能のみを使用するディレクトリでは、以下のように providers_default.hclとterraform_default.hcl をincludeします。

include "dev" {
  path = find_in_parent_folders("dev.hcl")
}

include "providers" {
  path = find_in_parent_folders("shared/providers_default.hcl")
}

include "terraform" {
  path = find_in_parent_folders("shared/terraform_default.hcl")
}

Sysdigプロバイダーも必要なディレクトリの設定

Sysdigプロバイダーが必要なディレクトリでは、以下のように _aws_and_sysdig.hcl が末尾にくるファイルをincludeします。

include "dev" {
  path = find_in_parent_folders("dev.hcl")
}

include "providers" {
  path = find_in_parent_folders("shared/providers_aws_and_sysdig.hcl")
}

include "terraform" {
  path = find_in_parent_folders("shared/terraform_aws_and_sysdig.hcl")
}

required_versionやaws用のrequired_providersが2箇所で定義されてしまうのがイマイチですが、一応このようにすれば、特定のディレクトリでのみ追加プロバイダーを使うといったことが可能です。

解決策2:Terraformのオーバーライド機能を使用する方法

以下のissueを見ていたらもっと良い方法があることを知りました。

https://github.com/gruntwork-io/terragrunt/issues/2470

ここでは、Terraformのオーバーライド機能を活用する方法を紹介しますが、上記のissueにはまた別のアプローチを使ってる例もあるのでぜひ見てみてください。

Terraformのオーバーライド機能とは

Terraformはplanapply実行時、ディレクトリ内の.tfファイルを全て結合して処理します。通常は異なるファイル間で同じオブジェクトを定義するとエラーになりますが、_override.tfで終わるファイルは特別な処理がされます。

処理の順序は以下のようになります

  1. まず通常の.tfファイルを処理(辞書順)
  2. その後で_override.tfファイルを処理
  3. オーバーライドファイルで定義された各ブロックについて、対応する既存のオブジェクトを探し、内容をマージ

詳しくは以下のページをご参照ください。

https://developer.hashicorp.com/terraform/language/files/override

上記の仕組みを活かしたのが以下の方法です。

基本設定ファイル

まず、全環境で共通して使用する基本設定を定義します。

shared/providers.tf:

provider "aws" {
  region = "ap-northeast-1"
}

shared/terraform.tf:

terraform {
  required_version = ">= 1.10.3"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.82.2"
    }
  }
}

Sysdig用の追加設定

Sysdigプロバイダーが必要な環境向けに、オーバーライド設定を作成します。

shared/providers_sysdig.tf:

provider "sysdig" {
  sysdig_secure_url       = "https://us2.app.sysdig.com"
  sysdig_secure_api_token = jsondecode(ephemeral.aws_secretsmanager_secret_version.sysdig_secret_params_secret_id.secret_string)["sysdig_api_token"]
}

provider設定は、下記のように重複が許されているブロックであるためオーバーライド機能を使わなくとも、設定が可能となっています。

provider "aws" {
# AWS用の設定

provider "sysdig" {
# Sysdig用の設定

shared/terraform_add_sysdig.tf:

terraform {
  required_providers {
    sysdig = {
      source = "sysdiglabs/sysdig"
      version = ">= 1.46.0"
    }
  }
}

それに対して、required_providersブロック(及びterraformブロック)は重複が許されません。そのため、オーバーライド機能でマージしたい部分のみを書いたコードをshared/terraform_add_sysdig.tfとして置いておきます。

共通設定を記載するdev.hcl

なお、共通設定を記載するdev.hclは以下の形です。

ポイント

  • 全環境で必要な基本設定(terraform.tf、providers.tf)のみをinclude
  • terraform_add_sysdig.tfなどの特定の環境でのみ必要な設定は含めない
dev.hcl
locals {
  env = "poc"
  system = "containersec"
  domain = null
}

remote_state {
  backend = "s3"
  generate = {
    path      = "_backend.tf"
    if_exists = "overwrite_terragrunt"
  }
  config = {
    bucket = "******"
    region = "ap-northeast-1"
    key            = "******"
    # dynamodb_table = "terraform-state-lock"
  }
}
inputs = {
  env = local.env
  system      = local.system
  domain = local.domain
}

generate "provider" {
  path = "_providers.tf"
  if_exists = "overwrite_terragrunt"
  contents = file("../../shared/providers.tf")
}

generate "version" {
  path      = "_terraform.tf"
  if_exists = "overwrite_terragrunt"
  contents  = file("../../shared/terraform.tf")
}

generate "variables" {
  path      = "_variables.tf"
  if_exists = "overwrite_terragrunt"
  contents  = file("./env_var.hcl")
}

各ディレクトリでの使用方法

AWSのみ使用するディレクトリ

基本設定のみを使用する場合は、シンプルな設定で済みます。

network/terragrunt.hcl

include "dev" {
  path = find_in_parent_folders("dev.hcl")
}

Sysdigも使用するディレクトリ

Sysdigも必要なディレクトリでは、generate設定を追加することで追加のファイルを生成します。

notifications/terragrunt.hcl

include {
  path = find_in_parent_folders("dev.hcl")
}

generate "provider_sysdig" {
  path = "_providers_sysdig.tf"
  if_exists = "overwrite_terragrunt"
  contents = file("../../../shared/providers_sysdig.tf")
}

generate "version_add_sysdig" {
  path      = "_terraform_sysdig_override.tf"
  if_exists = "overwrite_terragrunt"
  contents  = file("../../../shared/terraform_add_sysdig.tf")
}

上記の通り、通常は、required_providersブロック(及びterraformブロック)は重複が許されないので、エラーになるところですが、_override.tf というファイル名で生成することで、terraformのオーバーライド機能により、うまくマージされ実行することができます。

まとめ

Terragruntで複数プロバイダーを効率的に管理する方法として、以下の2つのアプローチを紹介しました

  1. includeファイルの分離アプローチ
  2. Terraformのオーバーライド機能を使用するアプローチ

個人的にはTerraformのオーバーライド機能を使用するアプローチ がスマートで良いかなと思います。

以上、とーちでした。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.