★★ 中級

09. Key Vault

Azure Key Vault は 「シークレット・キー・証明書」 を一元保管する金庫。AWS では Secrets Manager + KMS + ACM を統合した存在です。RBAC モードと Access Policy モードの 2 種類に注意。

3 つの保管対象

種別内容用途
Secrets任意の文字列(最大 25KB)DB パスワード、API キー、接続文字列
Keys暗号化キー(RSA / EC / oct)SSE-KMS 相当。Storage Account 暗号化等
CertificatesTLS 証明書(X.509)Web アプリの SSL 証明書管理

Key Vault の作成

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "main" {
  name                = "kv-myapp-prd-jpe"   # 全世界一意、3-24 文字
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  tenant_id           = data.azurerm_client_config.current.tenant_id
  sku_name            = "standard"   # standard or premium (HSM)

  # ソフト削除(必須、無効化不可)
  soft_delete_retention_days  = 30
  purge_protection_enabled    = true   # 本番では true 推奨(即時削除を完全禁止)

  # RBAC モードを使う(推奨)
  enable_rbac_authorization = true

  # ネットワーク制限
  public_network_access_enabled = false
  network_acls {
    default_action = "Deny"
    bypass         = "AzureServices"
    ip_rules       = ["203.0.113.0/24"]
    virtual_network_subnet_ids = [azurerm_subnet.private.id]
  }

  tags = local.common_tags
}
purge_protection_enabled は一度有効化すると戻せない ソフト削除中の Key Vault を即時削除する権限を 恒久的に 失います。本番では必須、テスト環境では off のままで良い。

RBAC モード vs Access Policy

Key Vault のアクセス制御方式は 2 種類:

RBAC モード(推奨)Access Policy モード(旧)
設定enable_rbac_authorization = true同 false(デフォルト)
権限付与Azure RBAC(azurerm_role_assignment)azurerm_key_vault_access_policy
粒度Vault 全体/個別 Secret 単位Vault 全体のみ
監査Activity Log で統一個別に確認

2026 年は RBAC モード一択。以下 RBAC 前提で進めます。

Secrets

resource "random_password" "db" {
  length  = 32
  special = true
}

resource "azurerm_key_vault_secret" "db_password" {
  name         = "db-password"
  value        = random_password.db.result
  key_vault_id = azurerm_key_vault.main.id

  content_type = "password"
  expiration_date = "2027-12-31T23:59:59Z"

  tags = local.common_tags
}

# 取り出す(data ソース)
data "azurerm_key_vault_secret" "db_password" {
  name         = "db-password"
  key_vault_id = azurerm_key_vault.main.id
}
# 使い方: data.azurerm_key_vault_secret.db_password.value
state に平文で残る Terraform で値を取得すると state に書き込まれます。state は必ず暗号化バックエンドへ。可能ならアプリ側で Managed Identity 経由 で取得し、Terraform は値を読まない設計に。

Keys(暗号化キー)

resource "azurerm_key_vault_key" "app" {
  name         = "myapp-cmk"
  key_vault_id = azurerm_key_vault.main.id
  key_type     = "RSA"
  key_size     = 2048

  key_opts = [
    "decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey",
  ]

  rotation_policy {
    automatic {
      time_before_expiry = "P30D"   # 期限 30 日前にローテーション
    }
    expire_after         = "P90D"
    notify_before_expiry = "P7D"
  }
}

# Storage Account の CMK 暗号化に使う
resource "azurerm_storage_account_customer_managed_key" "data" {
  storage_account_id = azurerm_storage_account.data.id
  key_vault_id       = azurerm_key_vault.main.id
  key_name           = azurerm_key_vault_key.app.name
}

Certificates

resource "azurerm_key_vault_certificate" "site" {
  name         = "myapp-com"
  key_vault_id = azurerm_key_vault.main.id

  certificate_policy {
    issuer_parameters {
      name = "Self"   # 本番は DigiCert / GlobalSign など
    }
    key_properties {
      exportable = true
      key_size   = 2048
      key_type   = "RSA"
      reuse_key  = true
    }
    secret_properties {
      content_type = "application/x-pkcs12"
    }
    x509_certificate_properties {
      subject            = "CN=myapp.com"
      validity_in_months = 12
      key_usage          = ["digitalSignature", "keyEncipherment"]
    }
  }
}

他リソースから参照

# Container App で Key Vault のシークレットを使う
resource "azurerm_container_app" "main" {
  # ...
  identity {
    type         = "UserAssigned"
    identity_ids = [azurerm_user_assigned_identity.app.id]
  }

  secret {
    name                = "db-password"
    identity            = azurerm_user_assigned_identity.app.id
    key_vault_secret_id = azurerm_key_vault_secret.db_password.id   # 直接参照
  }
}

# MI に「Key Vault Secrets User」を付与
resource "azurerm_role_assignment" "app_kv_secret" {
  scope                = azurerm_key_vault.main.id
  role_definition_name = "Key Vault Secrets User"
  principal_id         = azurerm_user_assigned_identity.app.principal_id
}

この方式なら Terraform は値を読まず、Container App がランタイムで MI 経由で取得する。state に残らない。