Azure Functions .NET 8(Isolated) QueueTrigger の Connection を Key Vault 参照で解決する方法(ローカル実行の落とし穴と対処)

Azure Functions(.NET 8 / Isolated)で QueueTrigger の Connection を Key Vault 参照にすると、Azure では動くのにローカル実行だけ失敗することがあります。本記事では、原因になりやすい VaultName の指定ミスと、local.settings.json の具体例、AzureWebJobsStorage の扱いまでまとめて解説します。

目次

やりたいこと:QueueTrigger の Connection を「接続文字列名」で参照し、実体は Key Vault に置きたい

.NET 8(分離ワーカー / Isolated)の Azure Functions で、Storage Queue のトリガーを次のように書くケースは多いです。

[Function("ProcessEvent")]
public void Run(
    [QueueTrigger("my-queue", Connection = "MyConnectionString")] string myQueueItem)
{
    // ...
}

このとき Connection = "MyConnectionString" は「接続文字列そのもの」ではなく、設定名(環境変数名)を見に行きます。Azure にデプロイした環境では、アプリ設定(App Settings / 環境変数)に次のような Key Vault 参照を置いておけば、接続文字列(AccountKey を含む)をアプリに直書きせずに済みます。

  • App Settings: MyConnectionString
  • 値: @Microsoft.KeyVault(...) 形式の参照

問題はここからで、ローカル実行(local.settings.json 等)でも同様に Key Vault 参照を書きたいのに、QueueTrigger の Connection だけが解決されず失敗することです。生の接続文字列(AccountKey を含む)を直書きすると動きますが、開発者全員のローカル設定更新が必要になり、ローテーションや運用の負担が一気に増えます。

ローカル実行で起きる症状:QueueTrigger だけが失敗する

代表的には、起動時(Functions Host がインデックスを作るタイミング)でエラーになり、トリガーが張れません。ログの文言は環境・拡張のバージョンで多少揺れますが、次のようなニュアンスのものが出やすいです。

  • 接続設定 MyConnectionString が見つからない / 解決できない
  • ストレージ接続文字列の形式が不正
  • ストレージアカウントへ接続できない

一方で、同じ Key Vault 参照を使っている別の設定(アプリの独自設定や、コード内で読む IConfiguration)は動いているように見えて、「QueueTrigger だけおかしい」という印象になりがちです。

前提整理:.NET 8 Isolated では「トリガーの解決」は Functions Host 側で起きる

Isolated(分離ワーカー)構成では、ざっくり言うとプロセスが分かれています。

  • Functions Host:トリガー/バインディングの解決、リスナーの開始、ホストの内部状態管理
  • .NET Worker:あなたの関数コード(DI、ログ、アプリ設定の読み込みなど)

QueueTrigger の Connection は、関数コードが動く「前」に Host が必要とする情報です。つまり「Program.cs で設定を読み替える」「DI で Key Vault を読んでから…」といった工夫をしても、Host 側が接続文字列を解決できなければトリガーは開始できません。

この構造を知っておくと、トラブルシュートが一気に楽になります。「コードは正しいのに起動前に落ちる」系は、Host が読む設定(環境変数/ローカル設定)の段階で何かが起きています。

結論:@Microsoft.KeyVault の VaultName は Key Vault の“名前だけ”を指定する

今回の原因は、Key Vault 参照の書き方(特に VaultName)が誤っていたことでした。ローカル設定に次のように書いていると、QueueTrigger の Connection が解決されずに失敗します。

指定例結果
NG(ホスト名/URL 形式を入れてしまう)VaultName=my-vault.vault.azure.net参照先 URL が不正になりやすく、Secret を引けずに失敗
OK(Key Vault のリソース名だけ)VaultName=my-vault正しく Secret を参照できる

ポイントは、「VaultName は Key Vault 名(リソース名)であって、FQDN/URL ではない」ことです。FQDN を入れると、内部で組み立てられる URL が二重になったり、名前解決できない宛先になったりして、結果として Secret が取得できず、@Microsoft.KeyVault(...) が“文字列のまま”残ってしまいます。

そして QueueTrigger は接続文字列が必要なので、そのまま起動に失敗します。逆に言うと、VaultName を正すだけで「ローカルでも Key Vault 参照が動いた」という状況は十分に起こり得ます。

ローカル設定の正しい例:local.settings.json(VaultName は名前だけ)

ローカル実行では local.settings.json の Values が環境変数として扱われます(ファイル自体は通常 Git 管理しません)。今回のポイントを反映した例は次のとおりです。

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",

    // Functions Host が内部動作で使うストレージ(後述)
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",

    // QueueTrigger の Connection が参照する設定名
    "MyConnectionString": "@Microsoft.KeyVault(VaultName=my-vault;SecretName=MyConnectionString)"
  }
}

この例では、QueueTrigger 側は Connection = "MyConnectionString" のまま変えません。変えるのは「MyConnectionString の値」だけで、Azure でもローカルでも同じキーで運用できます。

Secret に格納する値(Key Vault 側)は“通常どおり”でOK

Key Vault の Secret の値は、従来どおりストレージ接続文字列を入れます。たとえば以下のような形式です(値は環境ごとに異なります)。

DefaultEndpointsProtocol=https;AccountName=xxxx;AccountKey=yyyy;EndpointSuffix=core.windows.net

Key Vault 参照は「参照の書式さえ正しければ」中身の形式はいつもの接続文字列で問題ありません。

@Microsoft.KeyVault の書式を整理:SecretUri と VaultName/SecretName

Key Vault 参照は環境によって書式が混在しやすいので、チーム内でどれを使うか決めておくと事故が減ります。代表的には次の 2 パターンです。

パターン書式例特徴
SecretUri 指定@Microsoft.KeyVault(SecretUri=https://my-vault.vault.azure.net/secrets/MyConnectionString/xxxxx)URI をそのまま貼れる。バージョン固定もしやすいが、URI が長くなりがち
VaultName / SecretName 指定@Microsoft.KeyVault(VaultName=my-vault;SecretName=MyConnectionString)短く書ける。今回のように VaultName を FQDN で書かないのが重要

どちらを使っても目的は同じですが、VaultName/SecretName 方式は「VaultName に何を入れるか」で詰まりやすいため、ルールを明文化しておくのがおすすめです。

AzureWebJobsStorage を誤解しない:名前が似ているだけで “別物”

QueueTrigger の接続設定(MyConnectionString)とは別に、Functions には AzureWebJobsStorage という有名な設定があります。ここが混線すると、原因が見えにくくなります。

AzureWebJobsStorage は、Functions Host が内部動作で使うストレージ設定名です。拡張パッケージ名に Microsoft.Azure.WebJobs.* が付いている/付いていない、といった参照関係とは別の話で、単に「歴史的にそういう名前」なだけです。

設定名主に使うものローカル例よくある注意点
AzureWebJobsStorageFunctions Host(内部状態・ログ・一部トリガーなど)UseDevelopmentStorage=true(Azurite)空にすると起動できない/挙動が不安定になることがある
MyConnectionString(任意名)QueueTrigger の Connection(Storage Queue への接続)Key Vault 参照 or 実接続文字列参照名と値の双方を見直す。VaultName の指定ミスに注意

ローカルで Azurite を使うなら AzureWebJobsStorage は UseDevelopmentStorage=true が定番です。実ストレージを使うなら接続文字列を入れますが、ここも必要に応じて Key Vault 参照にできます(ただし Host が解決できる前提)。

動作確認の手順:原因切り分けを短時間で終わらせる

同じ現象に当たったとき、再発防止も兼ねて「何を確認すべきか」を手順化しておくと便利です。

Key Vault 側の確認

  • Key Vault の名前(リソース名)を確認する(FQDN ではない)
  • SecretName が一致しているか確認する(大文字小文字も含め)
  • Secret の値が正しい接続文字列形式になっているか確認する

ローカル設定の確認(local.settings.json)

  • Values 配下に MyConnectionString が存在するか
  • @Microsoft.KeyVault(...) の括弧内が正しいか(特に VaultName)
  • 余計な空白、全角文字、引用符の欠けがないか

権限(認証)の確認

ローカルで Key Vault の Secret を参照できるようにするには、実行している環境(開発者の Azure アカウント、サービスプリンシパル等)が Key Vault の Secret を読める必要があります。RBAC/アクセス ポリシーのどちらを使っている環境でも、最低限「Secret を取得する権限」が必要です。

ここが不足していると、VaultName が合っていても取得に失敗します。「参照は書けているのに値が解決されない」場合は、書式だけでなく権限も疑ってください。

チーム開発で運用を楽にするコツ

「ローカルにも AccountKey を配る」運用を避けるために、Key Vault 参照を軸にする場合、チームで次のようなルールを作っておくと回りやすくなります。

SecretName と設定名を固定し、環境差分は Key Vault 側で吸収する

  • 関数側の Connection は固定:Connection = "MyConnectionString"
  • Key Vault の SecretName も固定:MyConnectionString
  • 環境(dev/stg/prod)で Key Vault を分ける、または Secret の値だけを環境別にする

こうしておくと、開発者のローカル設定は「Key Vault 参照の 1 行」だけで済み、接続文字列の配布や更新が最小化できます。

local.settings.json を配らない(例ファイル運用にする)

実ファイルは開発者ローカルに置き、リポジトリには local.settings.json をコミットしないのが基本です。その代わりに、local.settings.example.json のようなサンプルを置き、チームで差分が出ない形にします。

  • 例ファイルには Key Vault 参照の書式(VaultName の書き方)を明記
  • 実ファイルは各自がコピーして使う(中身は参照だけで個人差が出にくい)

どうしても Key Vault 参照が安定しない場合の回避策

開発環境やツールの事情で、@Microsoft.KeyVault(...) の解決が安定しないこともあります。その場合は「参照の仕組み」から少し外れますが、次の回避策が現実的です。

起動スクリプトで Key Vault から Secret を取得し、環境変数として渡してから起動する

QueueTrigger の接続文字列は Host 起動時に必要なので、Host を起動する前に環境変数を用意するのがポイントです。例えば PowerShell なら次のようなイメージです。

$kvName = "my-vault"
$secretName = "MyConnectionString"

# Azure CLI などで取得(事前にログインが必要)
$cs = az keyvault secret show --vault-name $kvName --name $secretName --query value -o tsv

$env:MyConnectionString = $cs
func start

この方法なら、接続文字列をファイルに固定保存せずに済みます。ただし、Key Vault を読むための認証(az login など)や権限付与が必要で、運用ルールも追加で必要になります。

将来的には「鍵を持たない」設計も検討する

Storage Queue の利用方法によっては、AccountKey を前提としない認証(マネージド ID など)を検討できる場合があります。すべてのケースで置き換えられるわけではありませんが、“接続文字列を安全に配る”発想そのものを減らせるので、中長期的には有効です。

まとめ

  • .NET 8 Isolated の QueueTrigger は、Host 側で接続文字列を解決するため、設定の不整合があると起動時に落ちやすい
  • ローカルで @Microsoft.KeyVault(...) を使う場合、VaultName には Key Vault の名前(リソース名)だけを入れる(FQDN/URL 形式は NG)
  • AzureWebJobsStorage は Functions Host の内部ストレージ設定。QueueTrigger の接続設定とは役割が違うので切り分けて考える
  • 不安定な場合は、起動前に Secret を取得して環境変数に入れてから起動するなど、現実的な回避策も用意しておく

この記事を書いた人

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

コメント

コメントする

目次