Azure VM 上のマネージド ID を使って Azure SDK のサンプルを実行してみた

#シナリオとサンプル
例えば、Azure Blob Storage へデータをアップロードするといった操作を定期的に行う場合、Azure SDK を使うことがあるだろう。
Azure 仮想マシン(VM)であればシステム割り当てマネージド ID を利用して Azure SDK を実行可能である。
開発したコードからリソースへアクセスする時の認証にマネージド ID を使うメリットとして、クライアントシークレットや証明書など従来の資格情報を直接指定する必要がなく、開発・運用時におけるリスク低減につなげることができるという点がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/19ebb9fb-4ad0-80f1-6d8f-476096330f09.png)

https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview

Azure SDK のサンプルは GitHub にある JavaScript のコードを利用した。
https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/azureAdAuth.js

Node.js の実行環境で、ストレージアカウントへコンテナーを作成するサンプルである。
オリジナルからの変更点は以下 4 点。

①マネージド ID を利用するように [DefaultAzureCredential](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/azureAdAuth.js#L26) をコメントアウトし、代わりに ManagedIdentityCredential を指定する。
[こちら](https://www.npmjs.com/package/@azure/identity)を参照したところ、DefaultAzureCredential は以下のメカニズムで認証を試行するため、デフォルトのままでマネージド ID の使用は問題がなさそうだが、初回実行時に必ず Azure CLI could not be found. で失敗してしまったため（ 2 回目以降は成功するが）、明示的にマネージド ID を指定するように変更した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/da7b3daf-936c-1f19-67a0-980f2c79eb12.png)
②dotenv は使用しないためを[コメントアウト](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/azureAdAuth.js#L29)
③ストレージアカウント名を[指定](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/azureAdAuth.js#L33)
④テナントIDやクライアントシークレットを指定する箇所 ([Azure AD Credential information is required to run this sample: の部分](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/azureAdAuth.js#L35)) をコメントアウト

```javascript:azureAdAuth.js
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/*
  ONLY AVAILABLE IN NODE.JS RUNTIME
  If you are using the browser, you can use the InteractiveBrowserCredential provided via @azure/identity or any other feasible implementation of TokenCredential.
  Setup :
    - Reference - Authorize access to blobs and queues with Azure Active Directory from a client application 
      - https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-app
 
    - Register a new AAD application and give permissions to access Azure Storage on behalf of the signed-in user
      - Register a new application in the Azure Active Directory(in the azure-portal) - https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
      - In the `API permissions` section, select `Add a permission` and choose `Microsoft APIs`. 
      - Pick `Azure Storage` and select the checkbox next to `user_impersonation` and then click `Add permissions`. This would allow the application to access Azure Storage on behalf of the signed-in user.
    - Grant access to Azure Blob data with RBAC in the Azure Portal 
      - RBAC roles for blobs and queues - https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-portal.
      - In the azure portal, go to your storage-account and assign **Storage Blob Data Contributor** role to the registered AAD application from `Access control (IAM)` tab (in the left-side-navbar of your storage account in the azure-portal). 
    
    - Environment setup for the sample
      - From the overview page of your AAD Application, note down the `CLIENT ID` and `TENANT ID`. In the "Certificates & Secrets" tab, create a secret and note that down.
      - Make sure you have AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET as environment variables to successfully execute the sample(Can leverage process.env).
*/
const { BlobServiceClient } = require("@azure/storage-blob");
// const { DefaultAzureCredential } = require("@azure/identity");
const { ManagedIdentityCredential } = require("@azure/identity");
// Load the .env file if it exists
// require("dotenv").config();
async function main() {
  // Enter your storage account name
  const account = process.env.ACCOUNT_NAME || "blobpoc2021";
/*
  // Azure AD Credential information is required to run this sample:
  if (
    !process.env.AZURE_TENANT_ID ||
    !process.env.AZURE_CLIENT_ID ||
    !process.env.AZURE_CLIENT_SECRET
  ) {
    console.warn(
      "Azure AD authentication information not provided, but it is required to run this sample. Exiting."
    );
    return;
  }
*/
  // ONLY AVAILABLE IN NODE.JS RUNTIME
  // If you are using the browser, you can use the InteractiveBrowserCredential provided via @azure/identity or any other feasible implementation of TokenCredential.
  // DefaultAzureCredential will first look for Azure Active Directory (AAD)
  // client secret credentials in the following environment variables:
  //
  // - AZURE_TENANT_ID: The ID of your AAD tenant
  // - AZURE_CLIENT_ID: The ID of your AAD app registration (client)
  // - AZURE_CLIENT_SECRET: The client secret for your AAD app registration
  //
  // If those environment variables aren't found and your application is deployed
  // to an Azure VM or App Service instance, the managed service identity endpoint
  // will be used as a fallback authentication source.
  // const defaultAzureCredential = new DefaultAzureCredential();
  const credential = new ManagedIdentityCredential();
  const blobServiceClient = new BlobServiceClient(
    `https://${account}.blob.core.windows.net`,
    credential
  );
  // Create a container
  const containerName = `newcontainer${new Date().getTime()}`;
  const createContainerResponse = await blobServiceClient
    .getContainerClient(containerName)
    .create();
  console.log(`Created container ${containerName} successfully`, createContainerResponse.requestId);
}
main().catch((err) => {
  console.error("Error running sample:", err.message);
});
```

#SDK 実行環境

	$ cat /etc/lsb-release
	DISTRIB_ID=Ubuntu
	DISTRIB_RELEASE=20.04
	DISTRIB_CODENAME=focal
	DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"
	
	$ npm -v
	8.3.0
	
	$ node -v
    v16.13.1

# マネージド ID へのアクセス付与
ストレージアカウントのアクセス制御でマネージド ID の割り当てを行い、コードを実行する Azure VM を指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/cf4a40b7-ae1d-4bb1-f22b-26c04a775428.png)

ストレージ BLOB データ共同作成者として Azure VM が割り当てられている状態を確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/07c35774-9136-0590-e018-b6c321b4c278.png)

#実行手順
サンプルの実行は下記のとおり。

	$ npm install @azure/storage-blob
	$ npm install --save @azure/identity
	
	$ node azureAdAuth.js
	Created container newcontainer1640605793585 successfully d0a71faa-a01e-00a2-3d17-fb0d60000000

正常に実行完了したら、コンテナーが作成されていることが確認できた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/60d1279c-c748-b22c-8b4b-7b8ebd66c17b.png)

#ログの確認
**Azure AD のサインインログ**からは、コードの実行時刻のマネージド ID のサインインを確認できた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/5f88a0b5-696e-f220-5ca2-bafc5a0dcdff.png)

ストレージアカウントの**診断設定**を blobに対して有効にし、Log Analytics ワークスペースからクエリを実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/7908e7fd-ee93-06ce-b70e-93eb318eb9b1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/cc45121d-cdff-2b16-abb9-56a633ef1ef3.png)

診断設定を有効化した後で、コードを実行すると、数分経過してからログが表示された。
KQL クエリと結果は下記の通り。

	StorageBlobLogs 
	| sort by TimeGenerated desc 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/1cb6b399-5305-2a3c-794f-5bfe5df5fce0.png)

マネージド ID と Azure VM がどのように連携するかについては、下記が参考になる。

https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/how-managed-identities-work-vm

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1879870/f150ff30-6c83-e6ed-64c1-773158c81d13.png)

Azure SDK のサンプル実行によるマネージド ID の動作確認がメインの目的であるため、手動で blob コンテナーを削除して終わりとする。
以上。最後まで読んでいただきありがとうございました。