【Azure】Terraformを使ってストレージアカウントを作成してみよう!
はじめに
- IaCでリソース作成をしてみたかったのでTerraformを使い、ストレージアカウントを作成しました
 - 今回は、Azureにある既存のリソースグループにStorage Account(+Blobコンテナ)をTerraformで作る流れについて解説します
 
ゴールと全体像
- Terraform で Azure に接続(Azure CLI 認証)
 - 既存 リソースグループ(RG)(例:
InductionCourse_202507_son)を 参照 - Storage Account を 作成(HTTPSのみ、パブリック禁止、LRS など基本設定)
 - Blob コンテナ(例:
tfstate)も一緒に 作成 - よくあるハマり(プロバイダ登録/プロパティ名の変更)も回避
 
準備:ツールのインストール
Azure CLI のインストール(コマンド)
会社PCでインストールが難しい場合はAzure Cloud Shell(ポータル右上「>_」)でもOK。CLIが最初から入っています。(Microsoft Learn)
Windows(winget)
winget install -e --id Microsoft.AzureCLI
wingetが使えない場合は、Microsoft公式の Windows 向け手順(MSI / ZIP)もあります。(Microsoft Learn)
上のwingetコマンド自体は winget パッケージ情報でも確認できます。(winget.run)
インストール確認
az --version
Terraform のインストール(例:WindowsでZIP手動配置)
(会社PCでも通りやすい方法)
$TFV="1.6.6"
$Dst="$env:USERPROFILE\Tools\terraform"
$Zip="$env:TEMP\terraform_$TFV.zip"
$Url="https://releases.hashicorp.com/terraform/$TFV/terraform_${TFV}_windows_amd64.zip"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
New-Item -ItemType Directory -Path $Dst -Force | Out-Null
Invoke-WebRequest -Uri $Url -OutFile $Zip
Expand-Archive -Path $Zip -DestinationPath $Dst -Force
& "$Dst\terraform.exe" -version
# 任意: PATHに追加 → 新しいターミナルで `terraform -version`
[Environment]::SetEnvironmentVariable("PATH", $env:PATH + ";$Dst", "User")
Azure へのログイン&サブスク選択
# (任意) いったんログアウト
az logout
# 指定テナントでログイン(必要に応じて --use-device-code)
az login --tenant <テナントID>
# 対象サブスクリプションに切替
az account set --subscription <サブスクリプションID>
# 確認
az account show --query "{user:user.name, subscriptionId:id, tenantId:tenantId}" -o table
プロジェクト雛形(最小)
your-folder/
└─ infra/
   ├─ providers.tf
   ├─ variables.tf
   ├─ locals.tf
   ├─ main.tf
   ├─ outputs.tf
   └─ envs/
      └─ dev/
         └─ terraform.tfvars
 infra/providers.tf
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.100"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}
provider "azurerm" {
  features {}
  use_cli = true
  # 組織権限の都合で自動登録が403になる場合はオン
  skip_provider_registration = true
}
 infra/variables.tf
variable "env"                 { type = string, default = "dev" }
variable "system"              { type = string, default = "demo" } # 英数字推奨(短め)
variable "resource_group_name" { type = string }                   # 既存 RG 名
variable "location"            { type = string, default = "" }     # 空ならRGと同じ
variable "containers"          { type = list(string), default = [] } # 例 ["tfstate"]
 infra/locals.tf
locals {
  base_name = lower("st${var.system}${var.env}")
  sa_name   = substr("${local.base_name}${random_string.suffix.result}", 0, 24)
}
 infra/main.tf
data "azurerm_resource_group" "rg" {
  name = var.resource_group_name
}
resource "random_string" "suffix" {
  length  = 6
  upper   = false
  special = false
  numeric = true
}
locals {
  effective_location = var.location != "" ? var.location : data.azurerm_resource_group.rg.location
}
resource "azurerm_storage_account" "sa" {
  name                            = local.sa_name
  resource_group_name             = data.azurerm_resource_group.rg.name
  location                        = local.effective_location
  account_kind                    = "StorageV2"
  account_tier                    = "Standard"
  account_replication_type        = "LRS"
  https_traffic_only_enabled      = true     # v3推奨名
  allow_nested_items_to_be_public = false    # v3推奨名
  min_tls_version                 = "TLS1_2"
  blob_properties {
    delete_retention_policy { days = 7 }
  }
  tags = {
    env    = var.env
    system = var.system
  }
}
resource "azurerm_storage_container" "containers" {
  for_each              = toset(var.containers)
  name                  = each.key
  storage_account_name  = azurerm_storage_account.sa.name
  container_access_type = "private"
}
 infra/outputs.tf
output "storage_account_name"  { value = azurerm_storage_account.sa.name }
output "primary_blob_endpoint" { value = azurerm_storage_account.sa.primary_blob_endpoint }
 infra/envs/dev/terraform.tfvars
# ★ここだけ自分の環境に合わせて修正
resource_group_name = "InductionCourse_202507_son"
# 任意
system     = "demo"
env        = "dev"
containers = ["tfstate"]  # 不要なら []
# location = "japaneast"  # RGと同じで良ければ省略
実行手順
cd your-folder\infra
terraform init
terraform plan -var-file="envs/dev/terraform.tfvars" -out=tfplan
terraform apply "tfplan"
# または: terraform apply -var-file="envs/dev/terraform.tfvars" -auto-approve
成功すると:
- Storage Account 名(例:
stdemodev4blozl) - Blob エンドポイント(例:
https://stdemodev4blozl.blob.core.windows.net/)
が出力されます - Azure Portalを確認したところ、ちゃんとリソースが作成されていました!
 
確認コマンド(任意):
az storage account show -n <storage_account_name> -g <resource_group_name> -o table
az storage container list --account-name <storage_account_name> --auth-mode login -o table
よくあるエラーと対処
- 
Unsupported argument(プロパティ名の変更)
allow_blob_public_access→allow_nested_items_to_be_public、
enable_https_traffic_only→https_traffic_only_enabledに置き換え。 - 
Resource Provider の登録で 403
skip_provider_registration = trueをprovider "azurerm"に追加。
併せてMicrosoft.Storageが Registered か確認:
az provider show --namespace Microsoft.Storage --query registrationState -o tsv - 
tfplan が見つからない
直前のplan -out=tfplanが失敗していないか確認。エラーを直し再実行。 - 
名前規則エラー
SA名は小文字英数字3〜24文字。systemは短め英数字推奨。 
片付け(費用防止)
terraform destroy -var-file="envs/dev/terraform.tfvars"
このコマンドを実行した後に
Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.
  Enter a value: yes
と出るので、「yes」を入力した後にEnterを押して削除してください
まとめ
- Azure CLI を入れてサブスク選択 → Terraform で 既存RG参照→SA作成の流れ
 - 3.x 系ではプロパティ名が刷新されている点に注意
 - 組織の権限制約がある場合は 自動登録オフ+Microsoft.Storage Registered を確認
 
参考:Windows / macOS / Linux の Azure CLI 公式インストール手順、および Cloud Shell(CLI同梱)。(Microsoft Learn, Homebrew Formulae)
改善点・誤りなどあれば、コメントで教えていただけると嬉しいです!
Discussion