オンプレ IIS の .NET 8 で Azure Key Vault 認証エラーを解決する方法(DefaultAzureCredential)

オンプレの IIS に配置した .NET 8 アプリが Azure Key Vault の認証エラーで起動できない――Visual Studio では動くのに本番だけ失敗する原因は、DefaultAzureCredential が利用できる資格情報が環境ごとに違うためです。IIS での環境変数の渡し方と、安全に運用する考え方をまとめます。

目次

現象:Visual Studio では動くのに、オンプレ IIS だと起動時に落ちる

開発 PC で Visual Studio 2022 から実行すると、Connected Services で Azure Key Vault に接続でき、アプリも問題なく起動する。一方で、同じアプリをオンプレミスの Windows Server + IIS に配置すると、起動時に例外が発生し、Program.cs の AddAzureKeyVault(..., new DefaultAzureCredential()) 付近でプロセスが停止する――このパターンは非常によくあります。

典型的には、次のような例外メッセージの形で表れます(文言は環境により多少異なります)。

Azure.Identity.CredentialUnavailableException:
DefaultAzureCredential failed to retrieve a token from the included credentials.
... (EnvironmentCredential / ManagedIdentityCredential / VisualStudioCredential ... が失敗したログが続く)

まず押さえる:DefaultAzureCredential は「認証手段を順番に試す」仕組み

DefaultAzureCredential は、Azure への認証を 1 つに固定するのではなく、複数の認証方法を「成功するまで順番に試す」ための便利な仕組みです。逆に言うと、開発環境と本番環境で“使える認証手段”が変わると、同じコードでも結果が変わります。

認証手段(例)どこで成功しやすいか前提条件・ポイント
環境変数(Environment Credential)オンプレ / VM / コンテナなど広くAZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_CLIENT_SECRET などがプロセス環境変数として渡っている必要があります
マネージド ID(Managed Identity)Azure VM / App Service / Functions / AKS などAzure 側が発行する ID を利用。原則、秘密文字列の配布が不要で運用が楽です
開発者ログイン情報(Visual Studio / Azure CLI など)開発 PC「その PC のログイン状態」が前提。IIS サーバーには基本的に存在しません

Visual Studio で動いているのに IIS で動かないときは、ほぼ例外なく「開発 PC では使える認証手段が、本番 IIS プロセスでは使えない」ことが原因です。

なぜ Visual Studio 2022 で成功するのか

開発 PC では、次の要素が揃っていることが多く、DefaultAzureCredential の途中のどこかで認証が通ります。

  • Visual Studio にサインインしている(Microsoft アカウント / 組織アカウント)
  • Azure CLI(az login)や Azure PowerShell でログイン済み
  • 開発用の設定(ユーザー シークレット、ローカルのキャッシュ)が存在する

つまり、開発者が“自分の資格情報”で Key Vault にアクセスできている状態です。ここが、本番環境にそのまま持ち込めない落とし穴になります。

なぜ オンプレ IIS だと失敗するのか:IIS は「別ユーザー」で動く

IIS に配置した ASP.NET Core アプリは、通常 アプリプールの ID(ApplicationPoolIdentity、ドメインユーザー、gMSA など)で動きます。これは開発者のログインユーザーとは別物です。

そしてオンプレ IIS サーバーには、開発 PC にあった次のような“便利な資格情報”が存在しない/参照できないケースがほとんどです。

  • Visual Studio のサインイン情報(IIS サーバーに VS を入れていても、w3wp が参照できるとは限りません)
  • Azure CLI のログインキャッシュ(存在しても別ユーザーのプロファイルに入ります)
  • ユーザー シークレット(Development 限定で、サーバー配布が前提ではありません)

その結果、DefaultAzureCredential が試す認証手段がすべて失敗し、起動時に落ちます。ここまでが「なぜ VS では動いて IIS だと失敗するか」の本質です。

解決策の基本:IIS 上で“本番用の認証手段”を用意する

オンプレ IIS で Key Vault に接続したい場合、代表的な選択肢は次の 3 つです。

選択肢秘密情報の扱いおすすめ度向いているケース
環境変数でサービスプリンシパル(Client Secret)クライアント シークレットが必要○(導入が簡単)すぐ直したい/まず動かしたい
証明書ベースのサービスプリンシパル(Client Certificate)秘密文字列を置かず、証明書(秘密鍵)を OS で保護◎(運用が堅い)オンプレでもセキュリティ重視、長期運用
マネージド ID基本的にシークレット不要◎(使えるなら最優先)Azure 上の実行基盤(VM / App Service 等)

ここでは、まず多くの現場で採用される「環境変数」を中心に、IIS でハマりやすいポイントを具体的に説明します。

重要:IIS の「アプリ設定(appSettings)」は OS 環境変数ではない

質問で多いのが、「IIS の Configuration Editor で web.config に値を追加したのに、Environment.GetEnvironmentVariable() で見えない」というケースです。これは 入れた場所が違うのが原因であることがほとんどです。

設定した場所何のための設定かEnvironment.GetEnvironmentVariable() で見える?ASP.NET Core の IConfiguration で扱える?
<appSettings>(従来の web.config)IIS / .NET Framework の設定に近い×既定では ×(別途読み込み設定が必要)
<aspNetCore><environmentVariables>ANCM が起動するプロセスに渡す環境変数○○(既定の構成で環境変数は読み込まれる)
Windows のシステム環境変数OS レベルで全プロセスに適用(起動時に読み込まれる)○○

つまり、Key Vault の認証に環境変数を使うなら、IIS の appSettings ではなく、ANCM の <environmentVariables> か OS の環境変数に設定する必要があります。

方法A:web.config の <environmentVariables> で渡す(最短で直したいとき)

ASP.NET Core を IIS でホストしている場合、IIS は ASP.NET Core Module(ANCM)を通じてアプリを起動します。web.config の <aspNetCore> 配下に <environmentVariables> を書くと、アプリのプロセス環境変数として渡せます。

例(値はダミーです。実際のシークレットは貼り付けないでください):

<configuration>
  <system.webServer>
    <aspNetCore processPath="dotnet" arguments="MyApp.dll" hostingModel="inprocess">
      <environmentVariables>
        <environmentVariable name="AZURE_TENANT_ID" value="00000000-0000-0000-0000-000000000000" />
        <environmentVariable name="AZURE_CLIENT_ID" value="11111111-1111-1111-1111-111111111111" />
        <environmentVariable name="AZURE_CLIENT_SECRET" value="__REPLACE_WITH_SECRET__" />
      </environmentVariables>
    </aspNetCore>
  </system.webServer>
</configuration>

ポイントは次のとおりです。

  • AZURE_TENANT_ID / AZURE_CLIENT_ID / AZURE_CLIENT_SECRET は スペルが 1 文字でも違うと認証手段として認識されません
  • web.config を更新しただけでは反映されないことがあるため、アプリプールの再起動(Recycle)をセットで行う
  • この方法は「デプロイ成果物にシークレットが入る」形になりやすいので、後述のセキュリティ対策も併せて検討する

方法B:Windows のシステム環境変数として設定する(構成をアプリ外に出したいとき)

アプリの配布物に秘密情報を含めたくない場合は、Windows Server のシステム環境変数として設定する方法が分かりやすいです。設定は GUI でも可能ですが、重要なのは 設定後にプロセスを再起動することです。

  • システム環境変数として AZURE_TENANT_ID / AZURE_CLIENT_ID / AZURE_CLIENT_SECRET を追加
  • IIS のアプリプールを Recycle(または IISRESET)して、w3wp / dotnet プロセスを再起動

環境変数はプロセス起動時に読み込まれるため、設定直後にアプリが落ち続ける場合は「再起動ができていない」「別のアプリプールが動いている」などを疑うと切り分けが早いです。

切り分け:本当に環境変数が見えているかを確認する

本番では値そのものをログに出すのは避けたいので、「存在するかどうか」だけを確認するのが安全です。起動直後に一時的にだけ入れるなら、次のようなログが役に立ちます。

var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");

logger.LogInformation("AZURE_TENANT_ID exists: {exists}", !string.IsNullOrEmpty(tenantId));
logger.LogInformation("AZURE_CLIENT_ID exists: {exists}", !string.IsNullOrEmpty(clientId));
logger.LogInformation("AZURE_CLIENT_SECRET exists: {exists}", !string.IsNullOrEmpty(clientSecret));

3 つがすべて true になっているのに認証が失敗する場合は、Key Vault 側の権限、ネットワーク到達性、テナント/クライアント ID の取り違えなど、別要因を疑います(後述)。

より意図が明確な実装:ClientSecretCredential を明示する

DefaultAzureCredential は便利ですが、環境によって“どの認証が使われたか”が変わります。運用でのブレを減らしたい場合は、サービスプリンシパルで固定するのが分かりやすいです。

using Azure.Identity;

var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");

var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);

// builder.Configuration.AddAzureKeyVault(new Uri(keyVaultUri), credential);

「環境変数が見えているのに DefaultAzureCredential が選ばれない」といった混乱が減り、障害時の切り分けも早くなります。

シークレット文字列を置かない選択:証明書ベース(Client Certificate)

オンプレ IIS で長期運用するなら、クライアント シークレットよりも 証明書ベースが堅いことが多いです。秘密鍵は Windows の証明書ストアで保護でき、アクセス権も OS 側で制御できます。

ざっくりした流れは次のとおりです。

  • Microsoft Entra ID(Azure AD)でアプリ登録を作成し、証明書を資格情報として登録
  • 同じ証明書(秘密鍵付き)を IIS サーバーの証明書ストア(通常は LocalMachine)に配置
  • アプリプールの実行 ID に秘密鍵へのアクセス権を付与
  • アプリはサムプリントなど“識別情報”だけを設定として持ち、認証に使用

例(証明書の取得部分は環境により実装が変わります):

using System.Security.Cryptography.X509Certificates;
using Azure.Identity;

X509Certificate2 LoadCertFromStore(string thumbprint)
{
using var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);


var certs = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false);
if (certs.Count == 0) throw new InvalidOperationException("Certificate not found.");
return certs[0];


}

var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var thumbprint = Environment.GetEnvironmentVariable("AZURE_CLIENT_CERT_THUMBPRINT");

var cert = LoadCertFromStore(thumbprint);
var credential = new ClientCertificateCredential(tenantId, clientId, cert);

この方式だと、アプリが保持するのはサムプリントなどの“公開しても致命傷になりにくい情報”で、秘密鍵は OS の管理領域に閉じ込められます。運用の手間は増えますが、漏えい時のインパクトが下がります。

別案:マネージド ID を使う(使える環境なら最優先)

Azure VM や App Service など、Azure 上の実行基盤を使っているなら、マネージド ID を検討する価値があります。アプリはシークレットを持たずにトークンを取得でき、ローテーションも不要になりやすいからです。

ただし「オンプレ IIS」という前提だと、そのままでは使えないことが多い点に注意が必要です。Azure 上の基盤(Azure VM、Azure App Service、Azure Functions、AKS など)か、Azure Arc などの仕組みで“Azure 側が ID を発行できる状態”になっているかを確認してください。

Key Vault 側の設定も確認:権限とスコープが合っているか

環境変数が正しく渡っていても、Key Vault 側の権限が不足していると認証後の操作で失敗します。よくあるのは次の 2 つです。

  • アクセス ポリシー方式で、シークレットの Get / List 権限が付与されていない
  • Azure RBAC 方式で、Key Vault のスコープに必要なロール(例:Secrets User 相当)が付与されていない
やりたいこと最低限必要になりやすい権限不足すると起きること
アプリ設定として Key Vault のシークレットを読みたいSecrets の Get(状況により List)起動時にシークレット取得に失敗し、設定値が空で例外になる
Key Vault に新しいシークレットを保存したいSecrets の Set書き込みが Forbidden になり、更新処理が失敗する

「認証エラーに見えるが、実は権限不足」もよくあるため、ログの内側(HTTP 403 など)まで確認すると判断が速くなります。

ネットワーク要因も意外と多い:プロキシ、TLS、時刻ずれ

オンプレから Azure に出る場合、認証そのものよりもネットワークの制約で失敗していることがあります。エラーメッセージが “認証できない” に寄るため、見落としやすいポイントです。

  • サーバーから login.microsoftonline.com(トークン発行)と Key Vault のエンドポイントに HTTPS で到達できるか
  • 企業プロキシがある場合、IIS のプロセスがプロキシ経由で外部に出られるか(WinHTTP / 環境変数のプロキシ設定など)
  • サーバー時刻が大きくずれていないか(JWT の nbf/exp で弾かれる原因になります)

疑問の核心:「appsettings.json は危険扱いなのに、Tenant/Client/Secret は OK なの?」

ここは誤解されやすいのですが、結論から言うと クライアント シークレットは appsettings.json に入れても OK ではありません。危険性の種類が違うだけです。

項目秘密情報か漏えいした場合のインパクト置き場所の考え方
Tenant ID基本は秘密ではない(識別子)単体では直接侵入できないが、標的情報として利用され得る設定として持ってよいが、むやみに公開しない
Client ID基本は秘密ではない(識別子)単体では直接侵入できないが、攻撃の足がかりになり得る設定として持ってよいが、管理範囲は限定する
Client Secret明確に秘密盗まれると「そのアプリとして」トークンを取られる可能性平文ファイル・ソース管理に置かない。アクセス制御された保管が必須

appsettings.json や launchSettings.json が危険扱いされる理由は、次の性質があるからです。

  • ソース管理(Git)に入りやすい/レビューや共有の過程で漏れやすい
  • ビルド成果物として配布され、サーバー上で平文閲覧できることが多い
  • 誤ってログやエラーページに出力されると、二次被害が大きい

一方で Tenant ID や Client ID は “識別子” なので、それ自体が秘密鍵ではないという整理になります。ただし、識別子でも「攻撃者にとっての手がかり」にはなるため、公開範囲を無制限にしてよいという意味ではありません。

現実解:Key Vault を本命の保管庫にしつつ、ブートストラップは最小限にする

「Key Vault からシークレットを取得するために、アプリ側にシークレットが必要」問題は、運用設計として次の形に落ち着くことが多いです。

  • アプリが最初に持つのは、Key Vault にアクセスするための最小限の情報だけ
  • アプリが必要とする DB パスワードや API キー等は、Key Vault を“唯一の正”として管理
  • 可能ならシークレット文字列ではなく、マネージド ID または証明書でブートストラップする

オンプレ IIS の現場で実践しやすい落としどころを整理すると次のとおりです。

優先度ブートストラップ方法理由
高証明書ベース(Client Certificate)秘密文字列の配布を避け、OS の証明書管理とアクセス制御に寄せられる
中環境変数(Client Secret)導入が簡単。まず復旧したいときに強い。ただし保護とローテーションが必須
高(使えるなら最優先)マネージド ID運用負荷が最も低い。シークレット管理が不要になりやすい

オンプレ IIS でのセキュリティ実践チェックリスト

  • 最小権限:Key Vault には必要なシークレットの読み取りだけ(書き込み権限をむやみに付けない)
  • スコープ分離:開発・検証・本番でアプリ登録(クライアント ID)を分け、横展開の被害を抑える
  • 期限とローテーション:クライアント シークレットには短めの期限を設定し、更新手順を運用に組み込む
  • ファイル権限:web.config を使う場合は NTFS の ACL を絞り、閲覧できるユーザーを限定する
  • アプリプール ID:実行アカウントを明確にし、不要な権限(ローカル管理者等)で動かさない
  • ログの扱い:トラブルシュート時もシークレットの値は絶対に出さない(存在有無、失敗理由だけ出す)

まとめ:IIS で落ちるのは「環境の資格情報の差」。設定場所を揃えれば解決できる

Visual Studio で動くのにオンプレ IIS で落ちるとき、原因はほぼ「開発者のログイン情報に依存していた」ことです。IIS では DefaultAzureCredential が使える認証手段が限られるため、環境変数(正しい場所)、証明書、マネージド IDのいずれかで本番用の認証を成立させる必要があります。

特に「IIS の設定画面で入れたのに環境変数として見えない」場合は、<appSettings> と <aspNetCore><environmentVariables> を取り違えているケースが典型です。まずは設定場所を揃え、アプリプールを再起動し、Key Vault 側の権限とネットワークまで含めて確認する――これが最短の解決手順です。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次