🌵
【terraform】GoogleStyleGuide咀嚼
概要
GoogleStyleGuide(terraform)
terrafomrを使用する為のベストプラクティス(Google公式)への自己解釈
特に留意するべきことを抜粋(意訳アリ・詳細はそれぞれリンク参照)
スタイルと構造に関する一般的なガイドライン
🪨標準のモジュール構造に従う
🌵- terraform実行ディレクトリには
main.tf
を起因として、モジュールを呼び出す - モジュール配備ディレクトリには
README.md
を必ず配置 - terraform実行ディレクトリには
examples/
ディレクトリを作成し、
サンプルコードをとREADME.mdを配置 - モジュールの
*.tf
は目的別に分け、長大化を避ける - モジュールディレクトリには
CHANGELOG.md
を配置し履歴を手書きで残す
命名規則を採用する
🌵- リソース名には
_
を使用する。-
を使用しない - 参照されるリソースについては名前を簡略化する
- リソースタイプに含まれる文言をリソース名に含まない
-
aws_s3_bucket
というリソース名に s3、bucket というワードは避ける
-
変数を慎重に使用する
🌵- すべての変数を
variables.tf
で宣言する - 単位(MB・GB等)が影響する変数は、単位を変数名に組み込むとメンテナンス性が上がる
- 必須でないオプショナルな変数は、接頭語に
optional
などを付与-
required
の場合は特に明記しないことで差別化を計る
-
- ON・OFFの条件を判定する場合は、正(enable・true)の値を変数名に組み込む
- 変数名のdescriptionは必須明記
- 変数の型定義は必ず行う
- 環境に依存しない変数についてはデフォルト値を設ける
- 非公開のvalueについては、
.gitignore
に含まれるファイルで管理する
出力を公開する
🌵-
output.tf
を使用する際は、仕様についてREADME.md
に明記する -
output.tf
にて渡す変数は、descriptionを併せて記載する - variablesから、outputには直接変数を渡さないこと(依存関係が崩れる為)
データソースを使用する
🌵- データソースにて外部値を参照する場合は、引用するリソースと一緒の箇所に記載する
- 但し、データソースを多く参照する場合については専用の
data.tf
を検討する
- 但し、データソースを多く参照する場合については専用の
カスタム スクリプトの使用を制限する
🌵- カスタムスクリプトは極力使用しないこと
- terraformの挙動が対応していない場合のみ使用
- カスタムスクリプトは
scripts/
ディレクトリを作成して配備する - README.mdに詳細を記載し、メンテナンス性を担保すること
静的ファイルを別のディレクトリに配置する
🌵- terraformにて参照する静的ファイルは
files/
ディレクトリ配下に配備する - template関数を使用して参照するファイルは
templates/
ディレクトリ配下に配備する- templateファイルは拡張子
.tftpl
を使用する
- templateファイルは拡張子
ステートフル リソースを保護する
🌵- データベース・ディスクなどのステートフルリソースは削除保護を有効にする
組み込みの書式設定を使用する
🌵-
terraform fmt
に準拠したコーディングをする(要はterraform fmt
を実行しろ)
式の複雑さを制限する
🌵- 1行のコードに関数を多用しない
- local関数などを使用し、分割をこころがけ可読性をあげる
- 1行のコードに3項演算子を複数用いない
条件値に count を使用する
🌵- 条件付きでリソースをインスタンス化するには、count メタ引数を使用する
variable "readers" { description = "..." type = list default = [] } resource "resource_type" "reference_name" { // Do not create this resource if the list of readers is empty. count = length(var.readers) == 0 ? 0 : 1 ... }
反復されるリソースに対して for_each を使用する
🌵- 複数リソースを一括で作成する際は、
for_each
関数を使用する
再利用可能なモジュール
🪨モジュールで必要な API を有効にする
🌵- GCPでterraformを使用する際は、
project_services
リソースを使用してAPIを有効化する
オーナー ファイルを含める
🌵- すべての共有モジュールにOWNERS ファイルを含める
- GitHub では CODEOWNERSが相当
タグ付きバージョンをリリースする
🌵- バージョニングによって互換性を失うリソースについては、メジャーバージョンを固定する
プロバイダやバックエンドを構成しない
🌵- 共有モジュールでbackendやproviderは指定せず、terraformコマンド実行ディレクトリにて指定する
- providerバージョンを共有モジュールで記載する場合は、
required_providers
ブロックに最低限のバージョンを記載する
複雑なロジックにインライン サブモジュールを使用する
🌵- インラインサブモジュールとはモジュール内で更に呼び出す為のモジュールである
- モジュール内は完結に記載できるようサブモジュールを使用するケースがある
- インラインサブモジュールはモジュールからしか呼ばれなく、外部からは呼ばれない作りにする
Terraform のルート モジュール
🪨- ルートモジュールとはterraform CLIを実行するディレクトリを指す
各ルート モジュールのリソース数を最小限に抑える
🌵- ルートモジュールから呼び出されるリソースは100個未満に抑える
環境固有のサブディレクトリにアプリケーションを分割する
🌵- サービスのディレクトリ配下には下記の2つのディレクトリを設ける
- サービスモジュール
modules/
- 再利用し、複数構築されるリソースはサービスモジュールと呼ぶ(EC2など)
- 再利用し、環境を跨ぐ(Stage・Prod)リソースは参照モジュールと呼ぶ(ACMなど)
- 環境ディレクトリ
environments/
-
environments/
ディレクトリ配下は環境ディレクトリを掘る
-
- サービスモジュール
環境ディレクトリを使用する
🌵- サービスモジュールは共通入力をハードコーディングする
- 環境毎の固有値は変数化して、ルートモジュールからの値を参照する構成にする
- 環境ディレクトリ配下に下記ファイルを必須とする
- tfstateを管理する
backend.tf
- サービスモジュールをインスタンス化する
main.tf
- tfstateを管理する
- terraformの機能である、workspaceは非推奨
リモート状態から出力を公開する
🌵-
terraform_remote_state
とは、別のルートモジュールに存在するtfstate
ファイルの情報を読み込むことが可能である- e.g.)別ディレクトリの参照モジュールにある、VPCのtfstate情報を読み込む
参考サイト
- e.g.)別ディレクトリの参照モジュールにある、VPCのtfstate情報を読み込む
マイナー プロバイダのバージョンに固定する
🌵-
versions.tf
というファイルを設け、terraformブロックにrequired_providersを記載する
変数を tfvars ファイルに格納する
🌵-
terraform.tfvars
に変数の値を入力する -
-var-file
はイレギュラーな為、使用しないこと
バージョン管理
🪨デフォルトのブランチ戦略を使用する
🌵- 機能ブランチ名は
feature/$feature_name
- バグ修正ブランチ
fix/$bugfix_name
ルート構成に環境ブランチを使用する
🌵- 環境名・ブランチ名・ディレクトリ名は構成を考慮して設計する
e.g.)dev環境構築
devという名前の入ったブランチ名を使用し、環境ディレクトリ名はdevという名前で作成
シークレットを commit しない
🌵- secret情報はSecretManagerや環境変数を利用する
- terraformCLIを実行する際に、標準出力にアウトプットされるかも懸念すること
運用
🪨常に計画してから始める
🌵-
terraform plan
事前に実行し、反映されるリソースについて計画通りであることを確認する
既存のリソースのインポートを回避する
🌵-
terraform import
による既存リソースのインポートは極力避ける- 手動で作成したリソースやリソースの来歴を完璧に把握するのが困難な為
Terraform の状態を手動で変更しない
🌵-
terraform.tfstate
は手動で変更せず、terraform state
で更新する
バージョンの固定状態を定期的に確認する
🌵-
dependbot
を使いバージョンの固定状態の問題性を定期的に確認する
Terraform にエイリアスを設定する
🌵- ローカル開発環境用にエイリアスを用意し、 コマンド短縮・タイポのフェイルセーフに万全を期す
alias tf="terraform"
alias terrafrom="terraform"
セキュリティ
🪨リモート状態を使用する
🌵- 状態ファイル(tfstate)についてはリモートにて管理する
- チームにての共同作業が制限されるので、チーム内での構築ルールを設ける
- tfstateファイルについてはgitignoreに記載し、git管理外とする
状態を暗号化する
🌵- 状態ファイル(tfstate)の保存の際に必ず暗号化を行う(AWS・GCP)
- GOOGLE_ENCRYPTION_KEY やAWSのKMS
シークレットを状態に保持しない
🌵- シークレット情報についてはterraformのコード管理からは外す
- シークレットの箱はterraformで管理し、valueは手動で設定を行う
機密出力をマークする
🌵- 標準出力に出したくない機密情報については
sensitive = true
をリソースに組み込む- planやapplyに出力されないので、README.mdなどに明記すること
職掌分散を確保する
🌵- terraform実行の際に使用するプロファイルについては、権限を制限し必要最低限にする
適用前のチェックを実行する
🌵-
terraform vet plan
コマンドを事前に実行する- セキュリティ・コンプライアンスに準拠しているかを構築前にチェックし、
出力が無い状態をキープする
- セキュリティ・コンプライアンスに準拠しているかを構築前にチェックし、
継続的な監査を実施する
🌵- terraformで構築したリソースに対して、
terraform外部の自動セキュリティチェックを導入し安全性を担保する
テスト
🪨terraformCLIを実行する際の留意点して下記を挙げる
- Terraform テストを実行すると、実際のインフラストラクチャが作成、変更、破棄されるため、テストに時間と費用がかかる可能性がある
- エンドツーエンドの単体テストはterraformで完結しないが、ストランタイムの高速化・モジュール毎に反復開発が迅速に行えることを有効活用する
- tfstateファイルについてテスト中は極力弄らない。テスト中にtfstateファイルを弄ると、予期せぬリソース変更が検出される為
最初は低コストのテスト方法を使用する
🌵- 静的分析
コンパイラ・リンター・ドライランを使用しリソースを構築せず、構文・構造をテストする - モジュール統合テスト
サービス単位でモジュールを構築し、想定されるリソース群が作成されることを確認する - エンドツーエンドのテスト
テスト環境にて全てのリソースを構築し、本番と同等の環境が構築されることを確認する
小規模で始める
🌵- 小規模のテストを繰り返し実施した後に、複雑なテストを行いフェイルファストアプローチを実行する
プロジェクト ID とリソース名をランダム化する
🌵-
resource "random_id"
を使用しリソース名の競合を避ける
テストに別の環境を使用する
🌵- テスト用の環境は個別で用意すること
- 構築・削除を前提とした環境とし、サービスアカウントやフォルダなどもテスト用を設ける
すべてのリソースをクリーンアップする
🌵- リソースはクリーンアップするまでがフローである(構築しっぱなしだと課金される為)
テスト ランタイムを最適化する
🌵- テストを並行して実行する
- フレームワーク(terratest)などを使用することで、並行してリソース構築が可能である
構築依存関係などを確認し、構築順が発生していないかを確認可能
- フレームワーク(terratest)などを使用することで、並行してリソース構築が可能である
- 段階的にテストする
- 別個にテストできるような構成に分離しておき、個別で反復開発できるよう独立させる
Discussion