🚀

🔐 Azure Key Vault + Let's Encrypt 自動証明書更新にチャレンジしたらめちゃくちゃ大変だった

に公開

📋 目次

  1. なぜこのガイドが必要か?
  2. 全体構成の理解(図解)
  3. 事前準備チェックリスト
  4. Step 1: Azure DNS ゾーンの作成と設定
  5. Step 2: お名前.comからAzure DNSへの移行
  6. Step 3: Acmebotのデプロイ
  7. Step 4: 権限設定
  8. Step 5: テスト証明書の発行
  9. Step 6: 動作確認
  10. Step 7: 本番環境への適用
  11. トラブルシューティング
  12. メンテナンス・運用

🎯 なぜこのガイドが必要か?

現在の問題

2025年7月28日以降、Azure App Serviceの無料マネージド証明書は以下の環境で自動更新されなくなります:

  • ✅ IPアクセス制限がある環境
  • ✅ Private Endpointを使用している環境
  • ✅ Traffic Managerなど特殊なDNS構成

お名前.comの制限

  • ❌ _acme-challengeへのNSレコード設定ができない(_が特殊文字扱いされる)
  • ❌ API経由での自動DNS更新が不可能
  • ❌ CNAME方式もAcmebotの現バージョンでは未対応(2025年8月時点)

解決策:Azure DNSへ移行

Azure DNSへのへ移行により:

  • 🔓 DNS-01チャレンジが確実に動作
  • 🔒 アクセス制限を維持したままSSL証明書を自動更新
  • 💰 Let's Encryptの無料証明書を利用可能
  • ⚡ Azureで一元管理

ドメインの管理とDNS管理を混同してたけど全然別だった


🗺️ 全体構成の理解

移行前の構成(お名前.com管理)

[インターネット] 
    ↓
[お名前.com DNS] ← すべてのDNS管理
    ├── [あなたのドメイン名] → App Service IP
    └── [www.あなたのドメイン名] → App Service IP
    
[Azure App Service]
    └── 証明書の更新にHTTPアクセスが必要 ← ❌ IP制限で不可

移行後の構成(Azure DNS管理)

[インターネット]
    ↓
[お名前.com] ← ドメイン所有のみ(ネームサーバーをAzure DNSに設定)
    ↓
[Azure DNS Zone] ← すべてのDNS管理
    ├── [あなたのドメイン名] → App Service IP
    ├── [www.あなたのドメイン名] → App Service IP
    └── _acme-challenge(TXTレコード自動管理)
            ↓
    [Acmebot Function App]
    (証明書自動発行・更新)
            ↓
    [Azure Key Vault]
    (証明書保管)
            ↓
    [App Service]
    (証明書自動適用)

DNS移行の仕組み(重要!)

移行前:
  ドメイン所有: お名前.com
  DNS管理: お名前.com
  制限: API非対応、柔軟性なし

移行後:
  ドメイン所有: お名前.com(変更なし)
  DNS管理: Azure DNS
  メリット: API対応、Acmebot完全統合、自動化可能

✅ 事前準備チェックリスト

必要なもの

項目 確認内容 チェック
Azure サブスクリプション 有効なサブスクリプションがある ☐
Azure CLI インストール済み(az --versionで確認) ☐
お名前.com DNS管理画面にログイン可能 ☐
お名前.com 現在のDNSレコードをメモ/スクショ ☐
App Service [App Service名]が存在する ☐
ドメイン [あなたのドメイン名]を所有している ☐
権限 Azure上でリソース作成権限がある ☐

現在のDNS設定の確認(重要!)

お名前.comで以下を必ず記録:

  • Aレコード(IPアドレス)
  • CNAMEレコード(www等)
  • MXレコード(メール設定)
  • TXTレコード(SPF、DKIM等)

Azure CLIのインストール(未インストールの場合)

# Windows(PowerShell管理者権限)
Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi
Start-Process msiexec.exe -ArgumentList '/I AzureCLI.msi /quiet' -Wait

# Mac
brew install azure-cli

# Linux (Ubuntu/Debian)
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

📝 Step 1: Azure DNS ゾーンの作成と設定

1-1. Azureにログイン

# Azureにログイン
az login

# サブスクリプションの確認
az account show

# 必要に応じてサブスクリプションを切り替え
# az account set --subscription "サブスクリプション名"

1-2. リソースグループの作成

# 証明書管理用のリソースグループを作成
az group create \
  --name [証明書管理用のリソースグループ名] \
  --location japaneast

1-3. Azure DNSゾーンの作成

# [あなたのドメイン名]のDNSゾーンを作成
az network dns zone create \
  --resource-group [証明書管理用のリソースグループ名] \
  --name [あなたのドメイン名]

1-4. NSレコードの確認(重要!)

# 作成されたNSレコードを確認
az network dns zone show \
  --resource-group [証明書管理用のリソースグループ名] \
  --name [あなたのドメイン名] \
  --query nameServers -o table

出力例:

Result
------------------------
ns1-01.azure-dns.com.
ns2-01.azure-dns.net.
ns3-01.azure-dns.org.
ns4-01.azure-dns.info.

📝 これらのNSサーバー名を必ずメモしてください!Step 2で使用します。

1-5. 既存DNSレコードをAzure DNSに複製

# Aレコード([あなたのドメイン名]のIPアドレス)
az network dns record-set a add-record \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --record-set-name "@" \
  --ipv4-address "[IPアドレス]"

# CNAMEレコード(www)
az network dns record-set cname set-record \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --record-set-name "www" \
  --cname "[App Serviceのデフォルトドメイン]"

# TXTレコード(asuid.[あなたのドメイン名])- 追加
az network dns record-set txt add-record \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --record-set-name "asuid" \
  --value "[カスタムドメイン検証用TXTレコード値]"

# TXTレコード(asuid.www.[あなたのドメイン名])- 追加
az network dns record-set txt add-record \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --record-set-name "asuid.www" \
  --value "[カスタムドメイン検証用TXTレコード値]"

1-6. レコードの確認

# 設定したレコードを確認
az network dns record-set list \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --output table

🔄 Step 2: お名前.comからAzure DNSへの移行

2-1. 移行前の最終確認

# 現在のDNS解決を確認(移行前の状態を記録)
nslookup [あなたのドメイン名]
nslookup [www.あなたのドメイン名]

2-2. お名前.comでネームサーバー変更

  1. お名前.com Navi にログイン
  2. 「ドメイン」→「ドメイン機能一覧」を選択
  3. 「[あなたのドメイン名]」を選択
  4. 「ネームサーバー設定」→「ネームサーバーの変更」をクリック

2-3. Azure DNSのネームサーバーを設定

「他のネームサーバーを利用」を選択し、以下を入力:

ネームサーバー 入力値
ネームサーバー1 [Azure DNSネームサーバー1]
ネームサーバー2 [Azure DNSネームサーバー2]
ネームサーバー3 [Azure DNSネームサーバー3]
ネームサーバー4 [Azure DNSネームサーバー4]

⚠️ 重要な注意事項:

  • Step 1-4でメモしたNSサーバー名を正確に入力
  • 最後のピリオド(.)は省略可能
  • この変更により、DNS管理がAzure DNSに完全移行されます

2-4. DNS伝播の確認(1-48時間)

# ネームサーバーの変更を確認
nslookup -type=NS [あなたのドメイン名]

# Azure DNSが応答していることを確認
nslookup [あなたのドメイン名] [Azure DNSネームサーバー1]

成功時の出力:

Server: [Azure DNSネームサーバー1]
Address: xxx.xxx.xxx.xxx

Name: [あなたのドメイン名]
Address: [IPアドレス]

2-5. Webサイトのアクセス確認

# HTTPSアクセスの確認
curl -I https://[あなたのドメイン名]
curl -I https://[www.あなたのドメイン名]

# ブラウザでも確認
# https://[あなたのドメイン名]

🤖 Step 3: Acmebotのデプロイ

3-1. デプロイボタンからインストール

  1. Acmebot GitHubページ にアクセス
  2. 「Deploy to Azure」ボタンをクリック

3-2. デプロイ設定

Azureポータルで以下を設定:

設定項目 設定値 説明
基本設定
Subscription お使いのサブスクリプション
Resource Group [証明書管理用のリソースグループ名] Step 1で作成したもの
Region Japan East
Acmebot設定
App Name Prefix [Acmebotアプリ名プレフィックス] ユニークな名前
Mail Address [あなたのメールアドレス] Let's Encrypt通知用
Acme Endpoint https://acme-v02.api.letsencrypt.org/ 本番用
Create With Key Vault true
Key Vault Name [Key Vault名] 新規作成される
Key Vault SKU Standard
DNS Provider設定
DNS Provider Azure DNS
DNS Subscription Id サブスクリプションID
DNS Resource Group [証明書管理用のリソースグループ名]

3-3. デプロイの確認

# 作成されたリソースを確認
az resource list --resource-group [証明書管理用のリソースグループ名] --output table

# 期待される出力:
# - Function App ([Acmebot Function App名])
# - Storage Account
# - Key Vault ([Key Vault名])
# - Application Insights
# - App Service Plan

3-4. Function App設定の追加

# DNS Provider設定を確認/追加
az functionapp config appsettings set \
  --name [Acmebot Function App名] \
  --resource-group [証明書管理用のリソースグループ名] \
  --settings \
    "Acmebot:DnsProvider=AzureDns" \
    "Acmebot:AzureDns:SubscriptionId=$(az account show --query id -o tsv)"

3-5. App Service認証の有効化(必須)

Azure Portalで:

  1. Function App([Acmebot Function App名])を開く
  2. 「認証」をクリック
  3. 「IDプロバイダーの追加」→「Microsoft」を選択
  4. デフォルト設定のまま「追加」
  5. これによりhttps://[Acmebot Function App名].azurewebsites.net/dashboardへのセキュアアクセスが可能に

🔐 Step 4: 権限設定

4-1. Managed Identityの有効化と確認

# Managed Identityを有効化
az functionapp identity assign \
  --name [Acmebot Function App名] \
  --resource-group [証明書管理用のリソースグループ名]

# Object IDを取得
objectId=$(az functionapp identity show \
  --name [Acmebot Function App名] \
  --resource-group [証明書管理用のリソースグループ名] \
  --query principalId -o tsv)

echo "Object ID: $objectId"

このステップは、Azure Function App (Acmebot) が他のAzureサービス(Azure Key VaultやAzure DNSなど)に安全にアクセスするために必要です。

具体的には:

  1. Managed Identity (マネージドID) の有効化:

    • az functionapp identity assign コマンドは、Function App に「マネージドID」と呼ばれる特別なIDを割り当てます。
    • このマネージドIDは、Azure Active Directory (Azure AD) によって管理されるIDで、Azureリソース(Function Appなど)が他のAzureリソースに対して認証を行うための仕組みです。
    • これを利用することで、アプリケーションコード内に資格情報(パスワードやAPIキーなど)を直接埋め込む必要がなくなり、セキュリティが向上します。
  2. Object ID の取得:

    • az functionapp identity show ... --query principalId -o tsv コマンドで、有効化されたマネージドIDの Object ID (または Principal ID) を取得しています。
    • この Object ID は、次のステップである 4-2. Key Vaultアクセス権限の設定 や 4-3. Azure DNSアクセス権限の設定 で、このFunction App (Acmebot) に対して特定のリソース(Key VaultやDNSゾーン)へのアクセス権限を割り当てるために使用されます。

まとめると、このステップはAcmebotが以下のことを行うために不可欠です。

  • Azure Key Vaultへのアクセス: 証明書を保管・取得するため。
  • Azure DNSへのアクセス: DNSチャレンジのためのTXTレコードを作成・削除するため。
  • Azure App Serviceへのアクセス: 発行された証明書をApp Serviceにバインドするため。

これらのサービス間の連携を安全かつ効率的に行うための「身分証明」と「権限割り当ての準備」のステップ、と考えると分かりやすいでしょう。

4-2. Key Vaultアクセス権限の設定

# Key VaultのRBAC (ロールベースのアクセス制御) を設定
az role assignment create \
  --assignee $objectId \
  --role "Key Vault Certificates Officer" \
  --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/[証明書管理用のリソースグループ名]/providers/Microsoft.KeyVault/vaults/[Key Vault名]"

AcmebotがLet's Encryptから取得した証明書をAzure Key Vaultに安全に保管し、管理できるようにするため、Function AppのManaged IdentityにKey Vaultへの適切なアクセス権限を付与する必要があります。

現在のKey VaultはRBAC (ロールベースのアクセス制御) モードで構成されているため、az keyvault set-policy コマンドの代わりに、az role assignment create コマンドを使用してロールを割り当てます。

具体的には、Function AppのManaged Identityに対して「Key Vault Certificates Officer」ロールを割り当てます。このロールは、Key Vault内の証明書の取得、一覧表示、作成、更新、インポート、削除といった全ての証明書管理操作を許可します。

  • --assignee $objectId: 前のステップで取得したFunction AppのManaged Identity (Object ID)。
  • --role "Key Vault Certificates Officer": Key Vault内の証明書を管理するためのロール。
  • --scope: 権限を割り当てるKey Vaultのリソースパス。これにより、Acmebotはこの指定されたKey Vaultに対してのみ証明書操作が可能になります。

この設定により、AcmebotはKey Vault内の証明書を安全に管理できるようになります。

4-3. Azure DNSアクセス権限の設定

# DNS Zone Contributorロールを割り当て
az role assignment create \
  --assignee $objectId \
  --role "DNS Zone Contributor" \
  --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/[証明書管理用のリソースグループ名]/providers/Microsoft.Network/dnszones/[あなたのドメイン名]"

このステップは、Function App (Acmebot) が Azure DNSゾーン 内のDNSレコードを操作するために必要な権限を付与するために必要です。

具体的には、az role assignment create コマンドを使用して、以下の権限を設定しています。

  • --assignee $objectId: 前のステップで取得したFunction AppのManaged IdentityのObject ID。このIDを持つエンティティに権限を付与します。
  • --role "DNS Zone Contributor": Azure DNSゾーン内のDNSレコード(特にTXTレコード)を作成、更新、削除するための権限です。
  • --scope "/subscriptions/.../resourceGroups/[証明書管理用のリソースグループ名]/providers/Microsoft.Network/dnszones/[あなたのドメイン名]": 権限を割り当てるAzure DNSゾーンのリソースパスです。これにより、Acmebotはこの指定された[あなたのドメイン名]ゾーンに対してのみDNSレコード操作が可能になります。

なぜこの権限が必要かというと、AcmebotがLet's Encryptから証明書を取得する際の「DNS-01チャレンジ」を実行するためです。

DNS-01チャレンジでは、Acmebotはドメインの所有者であることを証明するために、特定のDNSゾーンに _acme-challenge.<your_domain> という形式のTXTレコードを作成する必要があります。このTXTレコードは一時的に作成され、証明書の発行が完了すると削除されます。

DNS Zone Contributor ロールがなければ、AcmebotはAzure DNSゾーンにTXTレコードを作成できず、DNS-01チャレンジが失敗し、結果としてSSL証明書を発行することができません。

したがって、このステップはAcmebotがLet's Encryptとの間で証明書発行プロセスを完了させるために不可欠な権限設定となります。

4-4. App Serviceへのアクセス権限

# App Serviceが所属するリソースグループ名
appServiceRG = "[App Serviceのリソースグループ名]"

# Website Contributorロールを割り当て
az role assignment create \
  --assignee $objectId \
  --role "Website Contributor" \
  --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/$appServiceRG"

このステップは、Function App (Acmebot) が Azure App Service に対して、発行されたSSL証明書をバインドするために必要な権限を付与するために必要です。

具体的には、az role assignment create コマンドを使用して、以下の権限を設定しています。

  • --assignee $objectId: 前のステップで取得したFunction AppのManaged IdentityのObject ID。
  • --role "Website Contributor": App Serviceのウェブサイトに対する共同作成者権限です。これには、SSL証明書をApp Serviceにバインドする操作が含まれます。
  • --scope "/subscriptions/.../resourceGroups/$appServiceRG": 権限を割り当てるApp Serviceが属するリソースグループのリソースパスです。$appServiceRG は、[App Service名] が存在するリソースグループ名(この場合は [App Serviceのリソースグループ名])に設定されています。

なぜこの権限が必要かというと、Acmebotの最終的な目的が「取得したSSL証明書をApp Serviceに適用する」ことだからです。

Acmebotは、Let's Encryptから証明書を取得し、Key Vaultに保管するだけでなく、その証明書をApp Serviceにデプロイされたウェブアプリケーションにバインドして、HTTPS接続を有効にする役割も担っています。

Website Contributor ロールがなければ、AcmebotはApp Serviceの構成を変更して証明書をバインドすることができません。

したがって、このステップはAcmebotが取得したSSL証明書を実際に利用可能な状態にするために不可欠な権限設定となります。

この解説は「SSL証明書自動更新で陥りがちな権限設定の落とし穴」について説明しています。詳しく分解すると:

4-4-1. App Serviceへのアクセス権限:追記(Web App自身の権限)

🔍 問題の構造

📊 証明書更新の3段階プロセス

1. [Acmebot] → [Key Vault] ← 証明書発行・保存
2. [Key Vault] → [App Service] ← 証明書取り込み  
3. [App Service内] ← ホスト名バインド更新

❌ よくある誤解

「Website Contributor を Function App に付与すれば自動更新される」
→ 半分正解、半分間違い

🔧 権限の役割分担

1️⃣ Function App(Acmebot)の権限

# これは正しく設定済み
az role assignment create \
  --assignee $functionAppManagedId \
  --role "Website Contributor" \
  --scope "/subscriptions/.../resourceGroups/[App Serviceのリソースグループ名]"

役割: App Service のバインド設定を変更する権限(3段階目)

2️⃣ Web App 自身の権限(見落としがち)

# これが不足していた
WEBAPP_MI=$(az webapp identity show \
  --resource-group [App Serviceのリソースグループ名] \
  --name [App Service名] \
  --query principalId -o tsv)

az role assignment create \
  --assignee $WEBAPP_MI \
  --role "Key Vault Secrets User" \
  --scope "/subscriptions/.../resourceGroups/[証明書管理用のリソースグループ名]/providers/Microsoft.KeyVault/vaults/[Key Vault名]"

役割: Key Vault から証明書を読み取る権限(2段階目)

💡 なぜ両方必要?

シナリオ A: Function の権限のみ

Acmebot「App Service よ、新しい証明書にバインドを変更せよ」
App Service「了解。Key Vault から証明書を取得します」
Key Vault「誰?権限がないのでアクセス拒否」
→ `Initialized` 状態で停止

シナリオ B: 両方の権限あり

Acmebot「App Service よ、新しい証明書にバインドを変更せよ」
App Service「了解。Key Vault から証明書を取得します」
Key Vault「Web App のマネージドIDですね。証明書を渡します」
App Service「バインド更新完了」
→ 自動反映成功

🔍 症状による診断

症状 不足権限 解決方法
Key Vault 証明書が更新されない Function → Key Vault Key Vault Certificates Officer 付与
Initialized で止まる Web App → Key Vault Key Vault Secrets User 付与
取り込み成功でもバインド更新されない Function → Web App Website Contributor 付与

🚨 Microsoft.Azure.WebSites について

# これは環境によって不要な場合もある
SP_OBJECT_ID=$(az ad sp list --display-name "Microsoft.Azure.WebSites" --query "[0].id" -o tsv)

目的: 古い App Service や特定の構成で、Web App のマネージドID以外にサービスプリンシパル経由でのアクセスが必要な場合の保険

現実: 現在のマネージドID構成では通常不要(実際に今回も不要だった)

🎯 要点

  1. Acmebot の権限: 「証明書を作る」「App Service を操作する」
  2. Web App の権限: 「Key Vault から証明書を読む」
  3. 両方そろって: 完全自動更新が実現

落とし穴: 「Function に Website Contributor を付与すれば終わり」と思いがちだが、実際は「Web App 側の Key Vault 読み取り権限」も必須という、見落としやすい設定が存在する点です。

4-5. 権限設定の確認

  1. Azure Portal にログイン:
    https://portal.azure.com/ にアクセスし、ログインしてください。

  2. Function Appに移動:

    • 左側のナビゲーションメニューから「リソースグループ」を選択し、[証明書管理用のリソースグループ名] をクリックします。
    • リソースグループ内のリソース一覧から、AcmebotのFunction App(例: [Acmebot Function App名])を見つけてクリックします。
  3. Managed Identity の設定を確認:

    • Function Appのメニューで「ID」を選択します。
    • 「システム割り当て済み」タブに、マネージドIDが有効になっていること(「状態」が「オン」になっていること)と、オブジェクト (プリンシパル) ID が表示されていることを確認します。このIDが $objectId と一致するはずです。
  4. 各リソースのアクセス制御 (IAM) でロールの割り当てを確認:

    • Key Vault のロール割り当て確認:

      • リソースグループ [証明書管理用のリソースグループ名] 内の Key Vault(例: [Key Vault名])をクリックします。
      • 左側のメニューで「アクセス制御 (IAM)」を選択します。
      • 「ロールの割り当て」タブをクリックします。
      • 割り当てられたロールの一覧で、Key Vault Certificates Officer ロールが、Function AppのManaged Identity (名前またはオブジェクトIDで識別) に割り当てられていることを確認します。
    • Azure DNS Zone のロール割り当て確認:

      • リソースグループ [証明書管理用のリソースグループ名] 内の DNSゾーン([あなたのドメイン名])をクリックします。
      • 左側のメニューで「アクセス制御 (IAM)」を選択します。
      • 「ロールの割り当て」タブをクリックします。
      • 割り当てられたロールの一覧で、DNS Zone Contributor ロールが、Function AppのManaged Identity に割り当てられていることを確認します。
    • App Service があるリソースグループのロール割り当て確認:

      • App Serviceがあるリソースグループ(例: [App Serviceのリソースグループ名])をクリックします。
      • 左側のメニューで「アクセス制御 (IAM)」を選択します。
      • 「ロールの割り当て」タブをクリックします。
      • 割り当てられたロールの一覧で、Website Contributor ロールが、Function AppのManaged Identity に割り当てられていることを確認します。

🧪 Step 5: テスト証明書の発行

5-1. テスト用サブドメインの準備

ここは既存の[www.あなたのドメイン名]のAレコードを利用しているのでスキップした。

# Azure DNSにテスト用Aレコード追加
az network dns record-set a add-record \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --record-set-name "test" \
  --ipv4-address "[IPアドレス]"  # App ServiceのIPアドレス

# App Serviceにカスタムドメイン追加
az webapp config hostname add \
  --resource-group [App Serviceのリソースグループ名] \
  --webapp-name [App Service名] \
  --hostname test.[あなたのドメイン名]

5-2. Acmebotダッシュボードにアクセス

  1. ブラウザで https://[Acmebot Function App名].azurewebsites.net/dashboard を開く
  2. Microsoftアカウントでログイン
  3. ダッシュボードが表示されることを確認

5-3. テスト証明書の発行

ダッシュボードで:

  1. 「Add Certificate」をクリック
  2. 表示されるフォームで以下を設定:
    • DNS Zone: [あなたのドメイン名] を選択 (通常は自動で選択されます)
    • DNS Names: www と入力します。(これは [www.あなたのドメイン名] を意味します。既存のカスタムドメインを利用するため、新しいAレコードの追加は不要です。)
    • Use Advanced Options?: No を選択したままで構いません。
    • Web Apps: [App Service名] を選択します。
  3. 「Add」をクリック

5-4. 発行状況の確認

# 閲覧ロールを付与しておく
myObjectId=$(az ad signed-in-user show --query id -o tsv)

az role assignment create \
      --assignee $myObjectId \
      --role "Key Vault Reader" \
      --scope "/subscriptions/$(az account show --query id -o tsv)/resourcegroups/[証明書管理用のリソースグループ名]/providers/Microsoft.KeyVault/vaults/[Key Vault名]"

# Key Vaultの証明書を確認
az keyvault certificate list \
  --vault-name [Key Vault名] \
  --output table

# Function Appのログを確認
az webapp log tail \
  --name [Acmebot Function App名] \
  --resource-group [証明書管理用のリソースグループ名]

5-5. Key Vaultからの証明書インポートとApp Serviceへのバインド

AcmebotによってKey Vaultに発行されたLet's Encrypt証明書を、App Serviceが利用できるようにインポートし、カスタムドメインにバインドします。

5-5-1. Key Vaultからの証明書インポート前に必要な権限設定

# 証明書をインポートするとエラー
az webapp config ssl import \
      --resource-group [App Serviceのリソースグループ名] \
      --name [App Service名] \
      --key-vault [Key Vault名] \
      --key-vault-certificate www-[あなたのドメイン名に相当する名前]
Unable to verify Key Vault permissions.
You may need to grant Microsoft.Azure.WebSites service principal the Secret:Get permission
Find more details here: https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html
The service does not have access to '/subscriptions/[あなたのサブスクリプションID]/resourcegroups/[証明書管理用のリソースグループ名]/providers/microsoft.keyvault/vaults/[Key Vault名]' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.

App ServiceがKey Vaultから証明書を読み込む権限を持っている必要があります。この処理はMicrosoft.Webリソースプロバイダーによって実行されるため、そのサービスプリンシパル([Microsoft.Azure.WebSitesサービスプリンシパルID])にKey Vaultの「Key Vault Secrets User」ロールが付与されていることを確認してください。

# ポータルからはできない
az role assignment create \
  --assignee [Microsoft.Azure.WebSitesサービスプリンシパルID] \
  --role "Key Vault Secrets User" \
  --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/[証明書管理用のリソースグループ名]/providers/Microsoft.KeyVault/vaults/[Key Vault名]"

デフォルトでは、Web アプリ RP は顧客の KV にアクセスできません。証明書の展開に KV を使用するには、次の PowerShell コマンドを実行して RP を承認する必要があります。
RP には KV への読み取りアクセスが必要です。[Microsoft.Azure.WebSitesサービスプリンシパルID]は RP サービスプリンシパル名で、すべての Azure サブスクリプションで同じです。
Azure Gov クラウド環境では、[Azure Gov クラウドのサービスプリンシパルID]上記のコマンドで RP サービスプリンシパル名として「[Microsoft.Azure.WebSitesサービスプリンシパルID]」の代わりに RP サービスプリンシパル名を使用する必要があります。
最後に必要なのは、デプロイする PFX 証明書を含む KVS です。証明書をデプロイする前に、RP は KVS に対して以下のチェックを実行します。
https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html

重要:App ServiceがKey Vaultから証明書を取得するための権限設定

App ServiceがKey Vaultから証明書を読み込むには、特別な権限設定が必要です。

なぜこの設定が必要なのか?

  • App Serviceが証明書をインポートする際、Azureの内部システム(リソースプロバイダー)がKey Vaultにアクセスします
  • このシステムには、あなたのKey Vaultを読み取る権限が最初から付与されていません
  • そのため、手動でアクセス権限を与える必要があります

設定内容の説明:

  • [Microsoft.Azure.WebSitesサービスプリンシパルID]:Azure App Serviceのシステム(リソースプロバイダー)のID
    • この英数字の文字列は、全世界のAzureで共通の固定値です
    • Azure Gov クラウドでは [Azure Gov クラウドのサービスプリンシパルID] を使用
  • Key Vault Secrets User:Key Vaultの証明書(シークレット)を読み取る権限

5-5-2. Key Vaultからの証明書インポート

  1. Azure Portalで [App Service名] App Service を開きます。
  2. 左側のメニューで「証明書」を選択します。
  3. 上部の「+ 秘密キー証明書の追加」をクリックします。
  4. 「ソース」のドロップダウンで「キーコンテナーからインポートする」を選択します。
  5. 以下の項目を設定します。
    • サブスクリプション: お使いのサブスクリプションを選択します。
    • キー コンテナー: [Key Vault名] を選択します。
    • 証明書: www-[あなたのドメイン名に相当する名前] (Acmebotが発行した証明書)を選択します。
  6. 「追加」または「選択」ボタンをクリックしてインポートを完了します。

5-5-3. カスタムドメインへのSSLバインド

インポートされたLet's Encrypt証明書を [www.あなたのドメイン名] にバインドします。

  1. App Serviceの「カスタム ドメイン」メニューに移動します。
  2. [www.あなたのドメイン名] の行で、右側の「...」をクリックし、「バインディングの追加」を選択します。(既存の場合はバインディングの更新を選択)
  3. 「TLS/SSL 証明書」のドロップダウンから、新しくインポートした「www-[あなたのドメイン名に相当する名前] (発行者: Let's Encrypt)」のような名前の証明書を選択します。
  4. 「TLS/SSL の種類」は「SNI SSL」を選択します。
  5. 「バインディングの追加」をクリックして完了します。

このステップ完了後、既存のマネージド証明書がバインドされている場合は、それを削除または新しい証明書に置き換える形になります。


5-6. HTTPS接続の確認

Webサイトにアクセスし、Let's Encrypt証明書が正しく適用されているかを確認します。

# 証明書が適用されているか確認
curl -I https://[www.あなたのドメイン名]

# ブラウザでも確認し、証明書の詳細を見る
# 発行者が「Let's Encrypt」になっていることを確認

✅ Step 6: 動作確認

6-1. DNS-01チャレンジの動作確認

# TXTレコードが作成・削除されることを確認
az network dns record-set txt list \
  --resource-group [証明書管理用のリソースグループ名] \
  --zone-name [あなたのドメイン名] \
  --output table

このコマンドは、SSL証明書の自動更新が正しく行われているかを確認するために、[あなたのドメイン名]というドメインのDNS情報(TXTレコード)をAzure上で見ている、ということです。
正しく証明書を更新するためには、このTXTレコードが一時的に作られて、目的を果たしたら削除されるという一連の動きが必要になります。

6-2. 証明書の詳細確認

# Key Vaultから証明書情報を取得
az keyvault certificate show \
  --vault-name [Key Vault名] \
  --name www-[あなたのドメイン名に相当する名前] \
  --query "{subject:policy.x509CertificateProperties.subject, expires:attributes.expires, issuer:issuer.name}" \
  --output table

6-3. 自動更新のテスト

https://[Acmebot Function App名].azurewebsites.net/dashboard
対象のCertificate name「www-[あなたのドメイン名に相当する名前]」を選択して、「Renew」をクリックする。


🚀 Step 7: 本番環境への適用

7-1. 本番前チェックリスト

確認項目 チェック
Azure DNSへの移行が完了 ☐
テスト証明書が正常に発行された ☐
HTTPSアクセスが成功した ☐
Key Vaultに証明書が保存されている ☐
DNS解決が正常に動作している ☐

7-2. 本番証明書の発行

Acmebotダッシュボードで:

  1. 「Add Certificate」をクリック
  2. 設定:
    • DNS Names: [あなたのドメイン名],[www.あなたのドメイン名](カンマ区切り)
    • DNS Provider: Azure DNS
    • Web Apps: [App Service名]を選択
  3. 「Add」をクリック

7-3. 証明書の適用確認

# 新しい証明書のバインディングを確認
az webapp config ssl list \
  --resource-group [App Serviceのリソースグループ名] \
  --query "[?contains(name,'[あなたのドメイン名に相当する名前]')]" \
  --output table

# 本番サイトのHTTPS確認
curl -I https://[あなたのドメイン名]
curl -I https://[www.あなたのドメイン名]

7-4. 古いマネージド証明書の削除(オプション)

# 古いApp Serviceマネージド証明書を確認
az webapp config ssl list \
  --resource-group [App Serviceのリソースグループ名] \
  --query "[?certificateSource=='AppServiceManaged']" \
  --output table

# 不要な証明書を削除
# az webapp config ssl delete --resource-group [App Serviceのリソースグループ名] --certificate-thumbprint <thumbprint>

🔐 SSL証明書自動更新の完全ガイド

✅ 設定完了状況

  1. Acmebot による証明書更新: 自動でKey Vaultに新証明書を保存 ✓
  2. 必要権限の付与: すべて完了 ✓
    • Function App → Key Vault: Key Vault Certificates Officer
    • Function App → DNS Zone: DNS Zone Contributor
    • Function App → Web App: Website Contributor
    • Web App → Key Vault: Key Vault Secrets User
  3. App Service への自動反映: 5〜60分のラグで自動実行 ✓

🔄 正常な自動更新フロー

  1. 期限前(30日前): Acmebot が自動で証明書更新開始
  2. DNS-01チャレンジ: _acme-challenge TXTレコード作成→検証→削除
  3. Key Vault更新: 新しい証明書をKey Vaultに保存
  4. App Service反映: 新証明書を取り込み→ホスト名バインドを自動切替(5〜60分後)

🛠️ 万が一自動反映しない場合の手動手順

1) 現状確認

# 変数設定(環境に合わせて調整)
RG_CERT=[証明書管理用のリソースグループ名]
RG_APP=[App Serviceのリソースグループ名]
KV=[Key Vault名]
APP=[App Service名]

# Key Vaultの最新証明書を確認
az keyvault certificate show \
  --vault-name $KV \
  --name www-[あなたのドメイン名に相当する名前] \
  --query "{expires:attributes.expires, thumbprint:thumbprint}" \
  -o table

# App Serviceの現在のバインド状況を確認
az webapp config hostname list \
  --resource-group $RG_APP \
  --webapp-name $APP \
  --query "[?name=='www.[あなたのドメイン名]'].{name:name, ssl:sslState, thumbprint:thumbprint}" \
  -o table

2) 新証明書の取り込み(必要な場合のみ)

# App Serviceに証明書一覧を確認
az webapp config ssl list --resource-group $RG_APP -o table

# 新しいThumbprintが無い場合は取り込み
az webapp config ssl import \
  --resource-group $RG_APP \
  --name $APP \
  --key-vault $KV \
  --key-vault-certificate www-[あなたのドメイン名に相当する名前]

3) バインドの手動更新

# 新しいThumbprintを取得
NEW_THUMBPRINT=$(az keyvault certificate show \
  --vault-name $KV \
  --name www-[あなたのドメイン名に相当する名前] \
  --query thumbprint -o tsv)

# wwwを新証明書にバインド
az webapp config ssl bind \
  --resource-group $RG_APP \
  --name $APP \
  --certificate-thumbprint $NEW_THUMBPRINT \
  --ssl-type SNI \
  --hostname [www.あなたのドメイン名]

# apex([あなたのドメイン名])も必要な場合
az webapp config ssl bind \
  --resource-group $RG_APP \
  --name $APP \
  --certificate-thumbprint $NEW_THUMBPRINT \
  --ssl-type SNI \
  --hostname [あなたのドメイン名]

4) 反映確認

# バインド状況の確認
az webapp config hostname list \
  --resource-group $RG_APP \
  --webapp-name $APP \
  --query "[?name=='www.[あなたのドメイン名]' || name=='[あなたのドメイン名]'].{name:name, ssl:sslState, thumbprint:thumbprint}" \
  -o table

# 実際のHTTPS接続確認
curl -I https://[www.あなたのドメイン名]
curl -I https://[あなたのドメイン名]

# 証明書の詳細確認(発行者・有効期限)
echo | openssl s_client -servername [www.あなたのドメイン名] -connect [www.あなたのドメイン名]:443 2>/dev/null | openssl x509 -noout -issuer -enddate

📋 トラブルシューティング

症状 原因 対処法
Key Vault更新されない DNS/KV権限不足 Function AppにDNS Zone Contributor・KV Certificates Officer付与
App Service取り込み失敗 KV読み取り権限不足 Web AppにKey Vault Secrets User付与
バインド自動更新されない 時間ラグ または 自動デプロイ未設定 5〜60分待機 または 手動バインド実行
Initialized状態で止まる Azure内部同期遅延 通常10〜30分でSucceededに遷移、急ぎなら手動バインド

🔍 動作確認用コマンド(定期チェック)

# 月1回程度の確認推奨
echo "=== SSL証明書ステータス確認 ==="
az keyvault certificate list --vault-name $KV --query "[].{name:name, expires:attributes.expires}" -o table

echo "=== App Serviceバインド確認 ==="
az webapp config hostname list --resource-group $RG_APP --webapp-name $APP --query "[?sslState=='SniEnabled'].{name:name, thumbprint:thumbprint}" -o table

echo "=== 外部からのHTTPS確認 ==="
curl -s -I https://[www.あなたのドメイン名] | head -1

🎯 要点

  • 通常時: 完全自動(人的作業不要)
  • 緊急時: 上記手動手順で即座に反映可能
  • 確認: 月1回程度の定期チェックで安心運用

Discussion