🌏

Terraform初心者のためのAzure構築の実践的アプローチとトラブルシューティング

2024/03/21に公開

この記事はなに

私は今回始めてTerraformを用いたAzureのインフラ構築に挑戦しました。その中で気付いた効率的なキャッチアップと、構築していく上でどうやって前進していったのかのアプローチ方法と、困った時のトラブルシューティング方法をまとめてみました。

Terraformのコードを軽く読んだことはあるし、どんなものかは概ね分かっている。しかし実際に1から構築したことはないという方には少し役にたつかもしれません。Terraformに詳しい人は「もっといいやり方あるよ」があれば是非コメントなどで教えていただけると嬉しいです。

動機:アプリケーションを開発しながら必要なリソースを足していったらIaCしたくなった

私たちは、モックやPOCの開発中で、スピードが求められる場面にありました。そのため、GUI(Azure Portal)でAzureリソースを作成していました。随時必要になるたびにリソースが足され、さらにリソースグループが足されていきました。(リソースグループのことがあまりよくわかっていなかった)

しかし、リソースグループをまたいでアクセスが必要になる場面が出てきました。例えば、Azure FunctionからBlob Storageにアクセスするような場合です。また、Front Door CDNとBlob Storageを違うリソースグループで作った時に、「これはなにか正しくない」と感じるようになりました。

しかし、またGUIでイチから作り直すのは面倒。そこで、どうせ作り直すなら、IaCで作りたいと考えました。GUIで行った設定は忘れてしまうこともあります。Terraformを使用することで、環境構築のプロセスを自動化し、再現性と信頼性を高めることができるはず。

IaCにする目的の1つは、サンドボックス、本番、開発のそれぞれの環境をコピー&ペーストで作成することでした。また、すぐに壊して作り直せる環境を作りたいと考えていました。

そもそもの理解は公式のドキュメントを読む。スクラップ機能とかを使うと後で使いやすいのでオススメ

そもそものAzureとTerraformってどうやってどうなるの?みたいな理解は公式のドキュメントを読みました。

https://learn.microsoft.com/ja-jp/azure/developer/terraform/

「使いそうだな」「気になるな」というタイトルの記事には目を通しました。後で困った時にここに帰ってくる事もありますのでタイトルだけでも見ておいたのが役に立ったりしました。

Obsidianや、Notionなどなんでもいいのですが、読みながらメモを取るといいと思います。私は後で社内に共有したりこういう記事に貼ると役に立つかもしれないのでzennのスクラップ機能を使って書いてみました。

https://zenn.dev/noblejasper/scraps/7f8eb43b28dbe4

この次のステップで実際に動かしてみる時にスレッドにコメントを書きながらメモを書いていけていい体験でした。とてもシンプルで便利です。

(投稿の一覧と各投稿へのジャンプが出来ると嬉しいです。zenn様見ていたらバックログへ・・・。スレッドが長くなるほど次のスレッドに移動するのが難しい。正しい使い方ではないのかもしれないです。)

記事に書いてある事をそのままやってみる

Azure のドキュメントでは、やってみたら最後に全部消すみたいな手順になっているものが多いです。そのため最後までやれば費用が無駄にかかったりするリスクは軽減出来そう。

私が実際にやったのはこのあたりです

https://learn.microsoft.com/ja-jp/azure/developer/terraform/get-started-cloud-shell-bash?tabs=bash

https://learn.microsoft.com/ja-jp/azure/developer/terraform/create-resource-group?tabs=azure-cli

https://learn.microsoft.com/ja-jp/azure/developer/terraform/azure-export-for-terraform/export-resources-hcl?tabs=azure-cli

https://learn.microsoft.com/ja-jp/azure/developer/terraform/store-state-in-azure-storage?tabs=azure-cli

実際の作業は スクラップ にコメントしながらやりました。作業途中で中断されてしまった時にも何をやっていたのかが思い出しやすいですし、よくわからないエラーが出た時などにも辿りやすくなります。誰かに共有したくなった時にも便利ですね。

GUIで作ったAzureリソースをExportしてぱっと眺める

ここまで基本を触ってきて、だいたいAzureをTerraformで操作する手段は理解できました。
では実際にGUIで作った環境をTerraformで再現するにはどうしたらよいでしょうか?

Terraformの記述形式HCLでExport出来るので、とりあえず全部Exportして手元で眺めましょう。どんなリソースがあって、以前設定した値はこんな感じなんだなーという具合に。

しかしここで全てを熟読する必要はあんまりありません。 実際に作っていく過程で参照するためのベースにすることが最も重要だからです。ExportしたHCLは変数化もされておらず、設定値も生で記述されています。
もちろんこのままでは環境構築をコピペで簡単!という訳にはいきません。

Exportの方法はここにまとまっています
https://learn.microsoft.com/ja-jp/azure/developer/terraform/azure-export-for-terraform/export-resources-hcl?tabs=azure-cli

では少しずつ構築を始めてみます

まずは何か1つから作ってみる

簡単なものから着手すると良いと思います。私の場合はストレージアカウントを作るという所から始めました。
どうやって作るといいかを考えましょう。いや、ググりましょう。

Azure Terraform storage account とかでググる

誰かがAzureでTerraformでストレージアカウントを作っている記事とかがあればそれを参考にするのがいいでしょう。できれば正当性を確認するために複数の記事を参考にすると更に良いと思います。

しかし私はここで見つけてしまいました。これはazure を terraform で操作する際に使う azurerm というライブラリのリファレンスです。

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account

Terraform Registry

Terraform...Registry ? という事はいろんなパッケージ(ライブラリ)があるということか。公式のライブラリではなくてもっと良しなに色々やってくれるのとかあるんじゃないのか?

https://registry.terraform.io/

例えば監視とCDNとストレージアカウントを一緒に設定してくれるとか。(まあそこまで超便利なものはあんまり見つけられませんでしたが、)色々めんどくさい部分をまとめてやってくれるライブラリとかが見つかります。

AWSとかに比べると少ないけど、それなりに基本的なものはありそう。なにより、GitHubへのリンクがあるので実装が読めるのが便利です!

方向性が良さそうなmoduleを見繕う

私は https://registry.terraform.io/namespaces/claranet が良さそうなのでめっちゃ参考にしており、多用しています。

Terraformでmodule使うときには、バージョンの指定も出来るし、実際に実行環境にコードを落としてくるのでおかしなことをしていないか確認の上で使えます。ちゃんと確認すればセキュリティリスクも無さそう。

実装中のわからない値のトラブルシュート

良さそうなmoduleを見つけてサンプルを参考に設定値を書いてみます。

途中できっとわからない設定値が出てきます。大体わからない事だらけです。そんな時は先程の「GUIで作成してExportしたコード」を読みましょう。

moduleによっては設定値の変数名が違っていて何がどうなるんじゃい?ってものもあります。そんな時は素直に、moduleのGitHubにコードを読みにいきましょう。説明書きを見るより大体早いです。

さらにわからない場合には、新たにGUIで再現出来そうな設定をしたAzureリソースを作ってみましょう。それをExportすればきっと分かるはずです。

あとは plan → apply → 修正の繰り返し

ひたすら繰り返していく事で試行錯誤しましょう。私は何度も全消ししました。グローバルでユニークな値のものなどがあるので全消ししたら書き換えが必要な場合とかもあります。ストレージアカウント名とかがそうだった気がします。

まとめ

正直、泥臭い作業です。キャッチアップと初期の構築なので「こんなもんだろう」と思いながらやりました。

しかし得るものはかなり多かったです 今後Azureリソース足したいなーって思った時にすぐ作れるようになります。おかしな設定があった時に見つけやすくなります。お客さんに見せる新しいデモ環境が欲しいんだよねーって時にコピペ+少しの修正ぐらいで作れるようになります。言い過ぎですかね?w

Terraformは最新の技術ではないかもしれませんが、いつかキャッチアップしようと思っていました。今回第一歩が踏み出せました。私と一緒に第一歩を踏み出したい人がいたら嬉しいなと思いながら書きました。

今回の記事は私が自分で考えて取り組んだ方法なので、もっと簡単な方法や、こんないい方法があるよって人は是非コメントして教えてくれるととても嬉しいです。

最後まで読んでいただいてありがとうございました。最後に私が作ったstorage_accountを作るTerraformの一部を載せておきます。参考になれば。

module "azure_region" {
  source  = "claranet/regions/azurerm"
  version = "7.1.0"

  azure_region = var.azure_region
}

module "rg" {
  source  = "claranet/rg/azurerm"
  version = "6.1.0"

  location    = module.azure_region.location
  client_name = var.client_name
  environment = var.environment
  stack       = var.stack
}

module "run" {
  source  = "claranet/run/azurerm"
  version = "7.8.1"

  client_name    = var.client_name
  environment    = var.environment
  location       = module.azure_region.location
  location_short = module.azure_region.location_short
  stack          = var.stack

  monitoring_function_enabled = false

  resource_group_name = module.rg.resource_group_name

  keyvault_public_network_access_enabled = true
  keyvault_rbac_authorization_enabled    = true
  keyvault_reader_objects_ids = [
    module.sp.sp_object_id,
    data.azurerm_client_config.current.object_id
  ]
  keyvault_admin_objects_ids = [
    data.azuread_group.admin_group.object_id
  ]
  keyvault_network_acls = {
    bypass         = "None"
    default_action = "Allow"
  }
}

module "storage_account" {
  source  = "claranet/storage-account/azurerm"
  version = "7.10.0"

  location       = module.azure_region.location
  location_short = module.azure_region.location_short
  client_name    = var.client_name
  environment    = var.environment
  stack          = var.stack

  resource_group_name = module.rg.resource_group_name

  account_replication_type = "ZRS"
  hns_enabled              = true
  network_rules_enabled    = false
  nfsv3_enabled            = false

  storage_blob_data_protection = {
    delete_retention_policy_in_days           = 7
    container_delete_retention_policy_in_days = 7
    container_point_in_time_restore           = true
  }

  storage_blob_cors_rule = {
    allowed_headers    = ["*"]
    allowed_methods    = ["GET", "HEAD", "POST", "PUT"]
    allowed_origins    = ["*"]
    exposed_headers    = ["*"]
    max_age_in_seconds = 200
  }

  logs_destinations_ids = [
    module.run.logs_storage_account_id,
    module.run.log_analytics_workspace_id,
  ]

  containers = [
    {
      name = "public"
      container_access_type = "private"
    }
  ]

  file_shares = []
  tables = []
  queue_properties_logging = {}
  queues = []
  extra_tags = {}
}

Discussion