Terragruntで特定のディレクトリのみプロバイダーを追加したいときの書き方
お疲れさまです。とーちです。
今回の記事は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を見ていたらもっと良い方法があることを知りました。
ここでは、Terraformのオーバーライド機能を活用する方法を紹介しますが、上記のissueにはまた別のアプローチを使ってる例もあるのでぜひ見てみてください。
Terraformのオーバーライド機能とは
Terraformはplan
やapply
実行時、ディレクトリ内の.tf
ファイルを全て結合して処理します。通常は異なるファイル間で同じオブジェクトを定義するとエラーになりますが、_override.tf
で終わるファイルは特別な処理がされます。
処理の順序は以下のようになります
- まず通常の
.tf
ファイルを処理(辞書順) - その後で
_override.tf
ファイルを処理 - オーバーライドファイルで定義された各ブロックについて、対応する既存のオブジェクトを探し、内容をマージ
詳しくは以下のページをご参照ください。
上記の仕組みを活かしたのが以下の方法です。
基本設定ファイル
まず、全環境で共通して使用する基本設定を定義します。
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つのアプローチを紹介しました
- includeファイルの分離アプローチ
- Terraformのオーバーライド機能を使用するアプローチ
個人的にはTerraformのオーバーライド機能を使用するアプローチ
がスマートで良いかなと思います。
以上、とーちでした。