結論
アプリケーション設定より、 WEBSITE_LOAD_USER_PROFILE = 1 と設定しましょう。
設定しない場合、下記のようなエラーメッセージが出力され、インスタンス作成に失敗する可能性があります。
Exception calling ".ctor" with "3" argument(s): "The system cannot find the file specified.
※ この記事は Microsoft Azure Tech Advent Calendar 2021 の 15 日目の記事です。
事象
例えば、 Azure Key Vault から証明書情報を読み込み、 X509Certificate2 クラスを作成するような Azure Functions の関数があったとします。
アプリケーション設定で WEBSITE_LOAD_USER_PROFILE = 1 を設定していない場合、関数を実行すると下記のエラーが発生してしまう場合があります。
Exception calling ".ctor" with "3" argument(s): "The system cannot find the file specified.
エラー メッセージから見ると、引数の証明書情報に不備があるように見えますが、 WEBSITE_LOAD_USER_PROFILE = 1 が設定されていない場合の Azure Functions (App Service) の想定された挙動となりますのでお気を付けください。
サンプル
関数の実行環境として Powershell を選択した場合のサンプル コードは下記のようなものです。
内容はシンプルで Azure Key Vault から証明書の情報を取得し、それに基づいてX509Certificate2
クラスを作成するというものです。
$containerName = '<キーコンテナ名>'
$certName = '<証明書名>'
$certificate = Get-AzKeyVaultCertificate -VaultName $containerName -Name $certName
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $certificate.Name
$secretText = '';
$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)
try {
$secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)
}
finally {
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
}
$secretByte = [Convert]::FromBase64String($secretText)
$Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($secretByte, '', 'Exportable,PersistKeySet')
なお、上記テスト コードを実行する際には Get-AzKeyVaultCertificate
Get-AzKeyVaultSecret
コマンドを利用できるよう、依存関係を追加する必要があります。
下記のドキュメントを参考に host.json
および requirements.psd1
を編集してください。
なお、 host.json
はデフォルトでmanagedDependency
プロパティが true
になっています。
また、requirements.psd1
はデフォルトで Az
コマンドがコメント アウトされているので、コメント インすれば OK です。
Azure ポータルから、[アプリ ファイル] から編集可能です (画像はコメント イン後)。
依存関係の追加後、 Azure Functions を再起動しましょう。再起動後、追加された依存関係のダウンロードが開始されます。
※上記サンプルは KeyVault にアクセスする際に必要な認証情報は省略しております。
手軽に設定したい場合は Functions にマネージド ID を付与する方法が簡単です。
- ID ブレードからシステム割り当てマネージド ID をオンに
- KeyVault 側でアクセス ポリシー ブレードより 1 で登録したマネージド ID に対してアクセス許可を付与する
- 関数コード冒頭に
Connect-AzAccount -Identity
を追加する
実行結果
依存関係を追加して再起動後、上記の関数コードを実行し、結果を[モニター]より確認するとエラーが発生していることがわかります。
原因
WEBSITE_LOAD_USER_PROFILE = 1 が設定されていない場合、 Functions(App Service) ホストはユーザー プロファイルおよびその配下の証明書ストアの情報をロードしません。
しかし、 X509Certificate2
クラスのコンストラクタはユーザー プロファイル内から証明書情報をインポートしようとします。
その結果、上述のエラーが発生した、ということになります。
ちなみに下記の記事にある通り、 X509Certificate2 クラスを作成した際に作成される証明書情報はデフォルトでは X509KeyStorageFlags.UserKeySet
が設定されているためユーザー プロファイル内の証明書ストアに保存されます。
- [Seven tips for working with X.509 certificates in .NET]
(https://paulstovell.com/x509certificate2/)
まとめ
Azure Functions (App Service) 上で X509Certificate2
クラスを作成するときには、アプリケーション設定よりWEBSITE_LOAD_USER_PROFILE = 1 を設定すると、不可解なエラーに遭遇せず済む場合がありますので、ご検討ください。
局所的な内容となってしまいましたが、こちらの記事がエラーで困っているどなたかの助けとなればと思います。
参考文献
- Seven tips for working with X.509 certificates in .NET
- [Best X509Certificate2 Practices · projectkudu/kudu Wiki]
(https://github.com/projectkudu/kudu/wiki/Best-X509Certificate2-Practices) - Configurable settings · projectkudu/kudu Wiki
- c# - How can constructing an X509Certificate2 from a PKCS#12 byte array throw CryptographicException("The system cannot find the file specified.")? - Stack Overflow
- Use a TLS/SSL certificate in code - Azure App Service | Microsoft Docs
- Azure App Services: How to determine if the client certificate is loaded | Microsoft Docs