2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

単一のTerraform Rootディレクトリで複数環境を制御する方法

Last updated at Posted at 2022-12-18

はじめに

この記事は terraform Advent Calendar 2022 の19日目です。

IaCツールの1つであるTerraformのディレクトリ構成をどうするかについては、Terraformを利用しているエンジニアであれば一度は考えたことがあるのではないでしょうか。
私も、2年前のAdvent Calendarにて投稿させていただきました。 (記事リンク

さて、Terraformのディレクトリ構成で検討するべき項目として、「複数環境(Production環境とStaging環境など)が存在する状況でのRootディレクトリの構成」が挙げられるかと思います。
これについて、
「Workspace機能は使うべきではない」
「Production環境とStaging環境はディレクトリで分けるのが良さそう」
「でもProduction環境とStaging環境のコード差異の吸収がめんどくさく、何ならちょこちょこ事故る
...みたいな記事が散見されておりました。

しかし、結局の所 「ディレクトリを分けずに環境差分を表現し実行できれば一番」 だと思いませんか?
私はそうしてしまいたいです。
というわけで、なんとか実現できないかを検討してみました。

本記事では、 -backend-config-var-file の2つのoptionを活用して、単一のTerraform Rootディレクトリで複数環境を制御する方法を示します。

前提条件

以下を前提条件とします(違う文脈の場合は適宜読み替えたり応用したりしてください)

  • prd環境とstg環境の2環境を単一のTerraform Rootディレクトリで表現したい
  • Terragrunt は使わない

結論

Terragrunt使うほうが楽な気がするけどまだ検証は終わっていない

素のTerraformだけで頑張る場合、ディレクトリ構成を以下のようにします:

(repository)
├─ root # 本文で ${TERRAFORM_ROOT} を指す
│   ├─ envs
│   │   ├─ prd.tfbackend # backendの設定として渡す値
│   │   ├─ prd.tfvars # variableとして渡す値
│   │   ├─ stg.tfbackend
│   │   └─ stg.tfvars
│   ├─ state.tf
│   ├─ variables.tf
│   ├─ ...
:   :

Terraform Initでは以下のコマンドを叩きます:

cd ${TERRAFORM_ROOT}
terraform init -backend-config=envs/${ENV}.tfbackend

Terraform Plan/Applyでは以下のコマンドを叩きます:

# applyの場合
cd ${TERRAFORM_ROOT}
terraform apply -var-file=envs/${ENV}.tfvars

サンプルコードは、リンク先のGitRepositoryを参照してください。
(サンプルコードは動作可能なようにlocal backendを利用しています。 解説においては実戦を想定しs3 backendを利用しています)
hkak03key/advent-calendar-sample-code//terraform-2022-root-dir

解説

外部ファイルを利用した変数設定と、その制約

Terraformを利用したことがあれば、 「実行時に外部から変数を注入したい」 という気持ちに駆られたことがあるかと思います。
これは、 variable を適宜定義し terraform apply -var-file=${FILE_PATH} で可能です。

この流れで 「外部から変数注入可能なら、backendについても変数で制御したい」 と思ったことがあると思います。
そして、こんなコードを書くわけです:

${TERRAFORM_ROOT}/state.tf
terraform {
  backend "s3" {
    bucket         = "${var.account_name}-${var.env}-terraform-backend"
    key            = "terraform.tfstate"
    region         = "ap-northeast-1"
    dynamodb_table = "${var.account_name}-${var.env}-terraform-backend"
  }
}

このときのTerraform Initの実行結果はこちら:

bash
$ terraform init
Initializing the backend...
╷
│ Error: Variables not allowed
│
│   on state.tf line 3, in terraform:
│    3:     bucket         = "${var.account_name}-${var.env}-terraform-backend"
│
│ Variables may not be used here.
...

そして 「はーーーーーマジかーーーーーーーーーーー」 ってなるところまでお約束だと思っています。

このように、Backendの設定には変数を利用できないという悲しい現実があります。
そして、「Production環境とStaging環境はディレクトリで分けよう」といったノウハウに繋がっていきました。(私が携わった環境もそうなってるものが大半です)

-backend-config optionの活用による、 backendに対しての外部ファイルを利用した変数設定

しかし、Production環境とStaging環境でディレクトリが分かれているのは、正直あまり嬉しくはありません。
そして、ドキュメントを読むと、 -backend-config なるoptionが存在します。
これを利用して、外部からbackendの設定を与えることが可能です。

利用方法を以下に示します:

state.tf の定義

まず、 ${TERRAFORM_ROOT}/state.tf にはbackendとして何を利用するかのみ記述します。
backendのパラメータは記述しないことに注意してください:

${TERRAFORM_ROOT}/state.tf
terraform {
  backend "s3" {
    # 本section内に記述する設定は .tfbackend ファイルに記載
    # initは以下のコマンドで実行する必要がある: 
    # `terraform init -backend-config={{.tfbackend ファイルパス}}`
  }
}

${ENV}.tfbackend の定義

次に、 ${TERRAFORM_ROOT}/envs/${ENV}.tfbackend に、hcl形式でbackendのパラメータを記述します。
ここではprd環境を想定します:

${TERRAFORM_ROOT}/envs/prd.tfbackend
bucket         = "some-product-prd-terraform-backend"
key            = "terraform.tfstate"
region         = "ap-northeast-1"
dynamodb_table = "some-product-prd-terraform-backend"

terraform init の実行

最後に、コマンド terraform init -backend-config=envs/${ENV}.tfbackend を実行します。

bash
cd ${TERRAFORM_ROOT}
terraform init -backend-config=envs/prd.tfbackend

コマンドが完了すれば成功です。

──

stg環境のbackendを設定したい場合は、 ${TERRAFORM_ROOT}/envs/stg.tfbackend を準備し実行します。
このとき、既に異なる環境で terraform init されている場合は -reconfigure optionが必要になるので注意してください。

bash
cd ${TERRAFORM_ROOT}
# 既に terraform init した環境と異なる環境について terraform init する場合は -reconfigure optionを与える
terraform init -backend-config=envs/stg.tfbackend -reconfigure

まとめ

単一のTerraform Rootディレクトリで、複数環境の terraform init が可能であることを示しました。
そして、 terraform init 時に -backend-config optionを、 terraform apply 時に -var-file optionを利用することで、単一のTerraform Rootディレクトリで複数環境のDeployが可能です。

一方で、課題もいくつかあります:

  • 設定ファイルが .tfbackend .tfvars の2種類がどうしても必要
    • どうしようもないけどダサい
  • Route53のような、 「保持する環境すべての中で唯一であるべきリソース」 を管理する場合は、単一のTerraform Rootディレクトリではかえって都合が悪い
    • こういうものについてはディレクトリを分けて対応するべき

そもそも、 Terragrunt のようなツールも出ており(Advent Calenderでも取り上げられていました: Terragrunt 導入で Terraform コードを DRY にしてみた)、まだ完全に検証は終わってないのですが恐らくいい感じにしてくれそうなので 「素のTerraformでここまで頑張るのか?」 というのは 「それは、そう」 という感じかもしれません。
しかし、「素のTerraformで頑張るとこんな感じになるよ」ということは心の片隅にあっても良いと思っており、Terraformのディレクトリ構成を決定する上での一つのヒントになれば幸いです。

──

僕らのTerraformのディレクトリ構成の旅はまだまだ続く。

宣伝

その1: YouTube出ました

データ分析系のサービスの中でも特に激アツな Snowflake のコミュニティである Snowflake Japanユーザーグループ のYouTubeに、DevOps回でお呼ばれしたので出演してきました!

DevOpsということでTerraformのお話も盛り上がってるので、是非ご覧ください!
https://www.youtube.com/watch?v=Hd4WWHmjjDw

その2: お仕事のご相談など受け付けております

個人事業主を副業でやってるので、Terraformの活用やデータ分析に関する技術支援、その他何かご用命があればご相談ください!

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?