terraformのmoduleについて
★写真は先日のランチ会の際に撮影したGoogleのオフィスになります
ランチ会の記事はこちら
みなさん、お疲れ様です。
Asobicaにジョインしてからもう直ぐ半年ということで、terraformをチームで管理していく上で学んだことをまとめていこうと思います。
主にGoogleCloudがまとめているベストプラクティスに沿っていきます。
今回はmoduleがテーマになります。
想定読者
terraform中級者
→「より効率的にコードを管理したい」といったフェーズにいる方terraformのmodule化について勉強中の方
→基本的な文法については触れません未来の自分
→本記事を読んで何を思うのか・・・
module化するメリット
※詳しいことは割愛します(解説記事はいくらでも出てくるので)
複数のAWSアカウントやGoogleプロジェクトなどを効率的に環境構築することができるようになります。
そもそも「複数の環境で使える汎用的なmoduleを作るのが難しい」と自分は考えています。(現在進行形)
これが現在の自分の立ち位置だということで、本記事ではmodule化に際して自分がつまづいたところを中心にまとめていきます。
命名規則について
当初はresource識別子でどんなリソースかがわかるように名前について考えていました。
# 以下サンプルだと「service_account」が識別子
resource "google_service_account" "service_account" {
account_id = var.service_account_id
display_name = var.service_account_id
project = var.project_id
description = var.description
}
ところがGoogleのベストプラクティスをみると以下の様になっていました。
# 推奨👍
resource "google_compute_global_address" "main" { ... }
# 非推奨👎
resource "google_compute_global_address" "main_global_address" { … }
とてもシンプルですね。
こちらの命名規則で実際に実装していくと、スムーズに実装ができました。(特にresource参照)
# サービスアカウントとかは識別子を設けてもいいかもしれない
resource "google_service_account" "terraform_sa" {
account_id = var.service_account_id
display_name = var.service_account_id
project = var.project_id
description = var.description
}
# サービスアカウントにowner権限を付与
resource "google_project_iam_member" "main" {
project = var.project_id
role = "roles/owner"
member = "serviceAccount:${google_service_account.terraform_sa.email}"
}
# サービスアカウントの IAM Policy 設定と GitHub リポジトリの指定
resource "google_service_account_iam_member" "main" {
service_account_id = google_service_account.terraform_sa.id
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.mypool.name}/attribute.repository/${var.github_repository}"
}
よく考えたら、どんなリソースなのかなんてresource名を見ればだいたい分かるので識別子について配慮する合理性があまりない気がしています。
組み込みの書式設定を使用する
「terraform fmt」をふとしたタイミングで実行してしまうと他のメンバーとコンフリクトを起こしてしまうことがあるので、pre-commit-terraformなどでコミット直前のタイミングなどに「terraform fmt」を実行する仕組みを構築した方がいいです。
※本当はterraform-docsのようにGHA上でPushまでしてくれる仕組みがほしい・・・
環境固有のサブディレクトリにアプリケーションを分割する
こちらは詳しい解説は不要なのかなと思っています。
terraformのworkspaceという機能で本番、検証環境などを分けることができますが、1ディレクトリで複数環境を管理するやり方はいずれ限界がきます。
-- SERVICE-DIRECTORY/
-- OWNERS
-- modules/
-- <service-name>/
-- main.tf
-- variables.tf
-- outputs.tf
-- provider.tf
-- README
-- ...other…
# environments配下で環境を分けている
-- environments/
-- dev/
-- backend.tf
-- main.tf
-- qa/
-- backend.tf
-- main.tf
-- prod/
-- backend.tf
-- main.tf
リモート状態から出力を公開する
別ディレクトリで作成したリソースを使って連携する時に必要になります。
(Pub/SubやAWS SNSなどでお世話になることが多い)
トピック名やARNを静的に宣言してしまうやり方も個人的にはアリかなと思っています。(エラー時の解析が楽になるので)
output "service" {
value = module.service
description = "The service module outputs"
}
ローカルでの実行中にアプリケーションのデフォルト認証情報を使用する
ローカルで複数プロジェクトに対してterraformを実行する際に、プロジェクトの切り替えや通常の「gcloud auth login」ではエラーになるため迷ったら以下のコマンドでログインし直すと解決することが多いです。
運用効率化の観点からローカルからの実行は、最終手段という位置付けにして基本は自動パイプラインでterraformを実行するべきだと思います。
※別記事にて静的認証情報(Credentials)を使うことなくGitHub Actionsでterraformで自動デプロイするために必要なリソース(サービスアカウントなど)をmodule化した記事を上げる予定
gcloud auth application-default login
# 「*」が付いているアカウントでgloudやterraformが実行される
gloud auth list
さいごに
今回はGoogleCloudがまとめているベストプラクティスから自分が特に重要だと感じた点をピックアップしました。
こういった記事は、初級者と中級者で内容の理解度が段違いなので、定期的に読み返したいと思いました。
(最初はほとんど意図が理解できなかったです・・・)
変数の宣言も重要な点であることには間違いないのですが、奥が深すぎるのでまた別の機会に自身の考えをまとめていこうと思います。