注目の記事

Terraform ベストプラクティスを整理してみました。

2022.08.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。クラメソのスジェです。
ほとんどのサービスにはベストプラクティス(=best practices)というのがあります。
そのサービスを利用する際、このベストプラクティスを守るとより効率的に性能を100%活用することができます。
もちろんterraformにもこのようなベストプラクティスがあります。
今回はこのベストプラクティスについて整理してみました。

参考資料

本記事は下記の資料を参考にして作成しました。

本記事ではほとんどのプロジェクトに活用できる程度の項目を紹介しています。つまり、ベストプラクティスについて軽く説明している感じなので、詳細な内容までは上記の資料をご参照ください。
また、紹介した資料以外にもベストプラクティスを調べてみたら、たくさんの資料がありますので、そちらもあわせて確認することをお勧めします。

読む前に

実際にベストプラクティスをプロジェクトに適用しようとしたら、意外と完璧に活用できない場合が多いです。
すべてのベストプラクティスを活用することはベストではありません。ベストプラクティスを理解してプロジェクトに適切なレベルで活用することが望ましいと思われます。
これを意識してプロジェクトに活用しましょう。

プロジェクトの構成

Terraform には多くの人が従う標準的な構成があります。
この標準的な構成は下記の通りです。

  • 最上位ディレクトリはサービスの構成が記載されているmodulesディレクトリと、各環境の構成が記載されているenvironmentsディレクトリに分けて管理
    -- {project name}/
       -- environments/
          -- dev/
             -- backend.tf
             -- main.tf
          -- prod/
             -- backend.tf
             -- main.tf
          -- ...other…
       -- modules/
          -- <service-name>/
             -- main.tf
             -- variables.tf
             -- outputs.tf
             -- provider.tf
             -- README.md
          -- ...other…
    # もしくは
    -- {project name}/
       -- environments/
          -- dev/
             -- main.tf
             -- backend.tf
             -- variables.tf
             -- provider.tf
             -- README.md
             -- outputs.tf
          -- prod/
             -- main.tf
             -- ...
          -- ...other…
       -- modules/
          -- <service-name>/
             -- main.tf
             -- variables.tf
             -- outputs.tf
           -- ...other…
  • モジュールは基本的にリソースが記載されたディレクトリのmain.tfに定義
  • 必要ならREADME.mdにモジュールの説明を記載(アウトプットなど)
  • 変数は variables.tfに定義
  • アウトプットは output.tfに定義
  • provider.tf, versions.tf などに継続的な管理のための内容を定義

小規模プロジェクトでは一つのディレクトリ(もしくはモジュール)で必要なファイルだけ(main.tf , variables.tf , README.mdなど)を作成した単純な構成もいいです。
でも規模が大きいプロジェクトや今後拡張される可能性が高いプロジェクトなら標準的な構成にしたほうが良いです。

本記事では各ファイルについて軽くに説明しましたが、GCPのドキュメントではもっと詳細な内容まで記載されてます。

また、コードを単一リポジトリ(mono repo)に保存するのか、複数のリポジトリ(multi repo)に保存するのかは確実な答えがないです。各仕方のメリットデメリットは hashicorp の記事をご参照ください。

モジュール

プロジェクトの成長に応じて、コードを他のチームも活用できるように再使用可能な構成で作成する必要があります。
一般的には所有権、責任、管理の楽さによってモジュールを分けます。

下記の項目が簡単に適用できる項目だと思います。

  • provider が定義されているモジュールは for_each, count, despends_on の引数が使えません。また、ほかのモジュールに呼び出される共有モジュールはproviderブロックを定義しないことをお勧めします。代わりにルートモジュールのrequire_providersブロックに必要な最小限のバージョン制限を定義します。
  • モジュール化されたリソースの参照は出力(outputs.tf)やterraform_remote_stateデータソースなどを活用します。
    また、モジュールの入力や出力、モジュールの説明はREADME.mdに文書化しましょう。terraform docsなどを活用すると楽に文書化ができます。
  • いろんな環境でモジュールを利用することになったら環境を(environments)を分けてステータス(tfstate)を管理したほうが良いです。もしくは terraform で提供しているworkspaces 機能を利用する方法もあります。
  • すべてのモジュールを自分で作成することもできますが、開発にかかる時間を節約する必要があれば共有モジュールを利用するのもいい方法です。
    利用できるモジュールはterraform registryで確認できます。

変数について

上記でお話ししましたどおりに変数はvariables.tfに宣言しましょう。
同じく活用しやすい項目は下記の通りだと思います。

  • 引数の用途や目的などの詳細を変数名(変数のタイプ、単位まで)に定義しましょう。 また、変数の説明(description)、タイプも定義します。必要に応じてデフォルトを定義します。
  • 環境によって差があるバリューだけを変数に定義しましょう。つまりバリューをハードコーディングしなくてdataブロックで処理できるなら、そうしましょう。
  • dataブロックは呼ばれるリソースの続けて定義し、dataブロックが多くなったらdata.tfファイルに分けます。
  • ルートモジュールなら.tfvars変数ファイルで変数値を提供します。
    • ファイル名は通用する名前であるterraform.tfvarsに指定しましょう。また、.tfvarsファイルはgithubなどにアップロードしないようにご注意ください。

コード作成について

すべての terraform ファイルは一貫的なルールに従います。terraform fmtterraform validateコマンドを利用して形式と有効性を確認しましょう。

オブジェクトは一貫的な名前指定ルールに従いましょう。

  • アンダーバー(_)を区分記号として使い、名前は英語小文字にする
  • リソース名にはリソースタイプを繰り返さないようにする
    • 唯一なリソースタイプのリソース名は参照を単純化するためにリソース名をmainにする
  • 同じタイプのリソースを区別するために意味があるリソース名(例:primary_~~~とsecondary_~~~)にする
  • 単一値の変数やタイプは単数名詞に指定し、listやmapの場合は複数名詞に指定する
  • 変数とアウトプットの説明を定義する
# 正しい例
resource "aws_~~~" "web_server" { ... } # アンダーバーがあるリソース名
resource "aws_~~~" "main" { ... } # 単純化したリソース名

# 悪い例
resource "aws_~~~" "web-server" { ... } # アンダーバーなし
resource "aws_~~~" "main_application_server" { ... } # 複合的なリソース名

条件付きでリソースをインスタンス化する場合はcountメタ引数を使用します。
ユーザー指定の変数でcount変数を設定する際は注意が必要です(リソースが存在しないとvalue of count cannot be computedエラーが発生します)。別の enable_x 変数を使用して条件付きロジックを計算します。
入力する値によって、リソース作成が繰り返される場合はfor-eachメタ引数を使用します。

プロジェクトの管理について

簡単に適用できるのは下記の通りだと思いました。

  • 作成するリソースにタグを定義しましょう。デバッギングが楽になります。
    • このようなタグはリソースコードの最後に定義して、すぐ確認できるようにしましょう。
    • 全リソースに一貫的なタグ設定が必要ならdefault_tagsで定義します。ドキュメントをご参照ください。
  • ステータスファイル(state file)には敏感な情報が含まれる可能性があります。協業とセキュリティのためにリモートステータス(remote state)状態にすることをお勧めします。Terraform は Terraform Cloud , HashiCorp Consul , Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS などのサービスへのステータス保存を対応しています。
    • 保存されたリモートステータスファイルにCMKをかけることでされに安全な管理ができます。

プロジェクトの規模がどんどん大きくなるとシステムが安全に運用できるようにポリシーを定義する必要があります。
terraform enterpriseはポリシーを楽に管理できるポリシーフレームワークであるsentinelを提供しています。
ほかにもポリシーに対して使えるツールがありますので、検討することをお勧めします。

セキュリティ

セキュリティ向上のために最初にできるのは秘密管理です。
敏感な情報はテキストに保存してアップロードしないでください。 代わりにTF_VAR 環境変数を活用し、出力に敏感な変数はsensitive=trueオプションをつけましょう。
より良い方法は Hashicorp Vault または AWS Secrets Manager, GCP gcloud config などの秘密管理サービスを利用することです。

運用中に問題が発生するかも知らないのでデバッギングを活性化する必要があります。デバッギングについては公式ドキュメントをご参照ください。

テスト

一番簡単にテストする方法はterraform validateterraform planを実行してみることです。
テストについてもっと詳細な内容は hasicorp のブログを読むことをお勧めします。

terraform cloud と他ツール

terraform cloud を利用するとterraformを使用する際に様々な部分で楽になります。terraform cloud については公式ドキュメントをご参照ください。
ほかにもコードの作成からテストまで効率的な作業をサポートするツールがあります。いくつか紹介しますと

  • tflint – plan でとれないエラーに対するterraform linter
  • tfenv – Terraform バージョン管理ツール
  • checkov – Terraform 静的分析ツール
  • terratest – Terraform に対して自動化されたテストをサポートするGo ライブラリ
  • pre-commit-terraform – pre-commit hookで実行するスクリプトを管理するフレームワーク
  • terraform-docs – モジュールのドキュメント作成ツール
  • spacelift – Terraform 協業インフラ・プラットホーム(Terraform Cloud みたいな機能)
  • terraform-cost-estimation – コスト推定サービス

Awesome Terraformにもっと多いツールが確認できますので、ご参照ください。

最後に

本記事には略した説明だけが記載されてますので、ぜひ参考資料をご確認ください。
そうすると terraform を理解するのに大いに役立つと思います。

お読みいただきありがとうございます。
誤字脱字、内容フィードバックはいつもありがとうございます。must01940 gmailでお願いします。