IIS に配置した ASP.NET アプリケーションからマネージドIDを使用して Azure サービスに接続しようとした際に発生したエラーの解決方法

## 前置き

ASP.NET の Web アプリケーションで、マネージドIDを使用して Azure ストレージに接続する処理を作成していました。  
アプリケーションをローカルのIISにデプロイして実行したところ、認証関連のエラーが発生しました。
本記事はそのエラーの解決方法になります。

ストレージに接続するコードは MS 公式のチュートリアルを参考にした以下のような内容です。

```
（中略）
// Construct the blob container endpoint from the arguments.
string containerEndpoint = string.Format("https://{0}.blob.core.windows.net/{1}",
                                            accountName,
                                            containerName);

// Get a credential and create a client object for the blob container.
BlobContainerClient containerClient = new BlobContainerClient(new Uri(containerEndpoint),
                                                                new DefaultAzureCredential());
（中略）
```

https://docs.microsoft.com/ja-jp/azure/app-service/scenario-secure-app-access-storage?tabs=azure-portal#net-example

# 最初に発生したエラー

```
Exception: Azure.Identity.AuthenticationFailedException
Message: Azure CLI authentication failed due to an unknown error. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/azclicredential/troubleshoot Traceback (most recent call last):
  File "runpy.py", line 194, in _run_module_as_main
  File "runpy.py", line 87, in _run_code
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/__main__.py", line 38, in <module>
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/__init__.py", line 905, in get_default_cli
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/azlogging.py", line 30, in <module>
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/commands/__init__.py", line 25, in <module>
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/extension/__init__.py", line 18, in <module>
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\knack/config.py", line 38, in __init__
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\knack/util.py", line 115, in ensure_dir
  File "D:\a\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\knack/util.py", line 112, in ensure_dir
  File "os.py", line 223, in makedirs
PermissionError: [WinError 5] アクセスが拒否されました。: 'C:\\windows\\system32\\config\\systemprofile\\.azure'
```

ASP.NET Core の記事になりますが、解決方法としてはアプリケーションプールがユーザープロファイルを使用できるようにする設定を ON にする必要があるようです。

https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/iis/advanced?view=aspnetcore-6.0

>この設定は、アプリ プールの [詳細設定] の [プロセス モデル] セクションにあります。 [ユーザー プロファイルの読み込み] を True に設定します。 True に設定すると、キーはユーザー プロファイル ディレクトリに格納され、ユーザー アカウントに固有のキーと共にデータ保護 API を使って保護されます。 キーは %LOCALAPPDATA%/ASP.NET/DataProtection-Keys フォルダーに保存されます。
>
>アプリ プールの 属性も有効にする必要があります。 setProfileEnvironment の既定値は trueです。 一部のシナリオ (たとえば、Windows OS) では、setProfileEnvironment は false に設定されます。 キーが期待どおりにユーザー プロファイル ディレクトリに格納されていない場合:
>
>%windir%/system32/inetsrv/config フォルダーに移動します。
applicationHost.config ファイルを開きます。
<system.applicationHost><applicationPools><applicationPoolDefaults><processModel> 要素を探します。
setProfileEnvironment 属性 (その規定値は true です) が存在しないことを確認するか、属性の値を明示的に true に設定します。

## 対処法1：アプリケーションプールのユーザープロファイルの読み込みを true にする

1. `C:\Windows\Sysnative\inetsrv\config\applicationHost.config` を開きます。
2. 以下の記述を探して、`setProfileEnvironment`を true にして保存します。

※ 編集前に applicationHost.conifg はバックアップしておくのが良いです。

```
<applicationPoolDefaults managedRuntimeVersion="v4.0">
       <processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="true" />
</applicationPoolDefaults>
```

## アプリケーションプールのユーザープロファイルの読み込みも確認する

1. インターネットインフォメーションサービス（IID）マネージャーを表示する
1. アプリケーションプールを選択する
1. 対象のサイトのアプリケーションプールを選択する
1. 画面右のメニューから「詳細設定」をクリックする
1. 「プロセルモデル > ユーザープロファイルの読み込み」が false だったら true にする

# 次に発生したエラー

前項の解決方法で setProfileEnvironment（ユーザープロファイルの読み込み） を true にしてから実行したところ、
以下のようにエラーの内容が変わりました。

```
Exception: Azure.Identity.CredentialUnavailableException
Message: DefaultAzureCredential failed to retrieve a token from the included credentials. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/defaultazurecredential/troubleshoot
- EnvironmentCredential authentication unavailable. Environment variables are not fully configured. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/environmentcredential/troubleshoot
- ManagedIdentityCredential authentication unavailable. Multiple attempts failed to obtain a token from the managed identity endpoint.
- Visual Studio Token provider can't be accessed at C:\Users\<アプリケーションプールの仮想アカウント名>\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json
- Stored credentials not found. Need to authenticate user in VSCode Azure Account. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/vscodecredential/troubleshoot
- Please run 'az login' to set up account
- Az.Account module >= 2.2.0 is not installed.
```

# 最終的にエラーが解決した方法

1. インターネットインフォメーションサービス（IID）マネージャーを表示する
1. アプリケーションプールを選択する
1. 対象のサイトのアプリケーションプールを選択する
1. 画面右のメニューから「詳細設定」をクリックする
1. 「プロセルモデル > ID」を選択し三点リーダーボタンをクリックする
1. 「アプリケーションプールID」ダイアログで「カスタムアカウント」を選択し「設定」ボタンをクリックする
1. 「ユーザー名」に、現在ログインしている Windows ユーザーのユーザー名を、「ドメイン/ユーザー名」の形式で入力する
1. 「パスワード」「パスワードの確認入力」でユーザーのパスワードを入力する
1. 念のため対象のサイトを再起動する

# 結果

前項の解決方法で、前述した2つのエラーはいずれも表示されなくなりました。

結論としては IIS 上の WEB アプリケーションはアプリケーションプールの仮想アカウントが実行ユーザーとなるので、
そのユーザーが Azure にログインしている情報がないと`DefaultAzureCredential`で資格情報が取得できないということだと思います。

仮想アカウントで Azure にログインする方法は無いと思われるので、アプリケーションプールの実行ユーザーをカスタムアカウントで開発に使用しているローカルユーザーにすることで解決できました。

当然ですが「最終的にエラーが解消した方法」のカスタムアカウントで設定しているユーザーで、
Azure CLI 上で「az login」を行い Azure の資格情報とどのサブスクリプションを使用するかを設定しておく必要があります。  

https://docs.microsoft.com/ja-jp/cli/azure/authenticate-azure-cli

https://docs.microsoft.com/ja-jp/cli/azure/manage-azure-subscriptions-azure-cli

# トラブルシュート

チームメンバーの方に同手順を行ってもらっても、そちらの環境では以下のようなエラーが発生しました。    
 [PowerShell 7.3](
`https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3#msi) をインストールしたところ、エラーは発生しなくなりました。  
※ `pwsh` は PowerShell 6 以降の実行ファイルのことなので

```
Exception: Azure.Identity.AuthenticationFailedException
Message: Azure PowerShell authentication failed due to an unknown error. See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/powershellcredential/troubleshoot 'pwsh' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```

また、こちらは必須なのかは不明ですが [Azure PowerShell](https://learn.microsoft.com/ja-jp/powershell/azure/install-az-ps-msi?view=azps-9.1.0#install-or-update-on-windows-using-the-msi-package) も行いました。