概要
Azure Blob Storageのデータを取得するAPIをローカルで動かした。
認証にはマネージドIDを使ってみたメモ。
コード
import { DefaultAzureCredential } from '@azure/identity';
import { BlobServiceClient } from '@azure/storage-blob';
export const getBlobClient = () => {
  const accountName = process.env.Azure_Storage_AccountName;
  if (!accountName) throw Error('Azure Storage accountName not found');
  const blobServiceClient = new BlobServiceClient(
    `https://${accountName}.blob.core.windows.net`,
    new DefaultAzureCredential(),
  );
  return blobServiceClient;
};
import {
  app,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
} from '@azure/functions';
import { getBlobClient } from '@yakumi-api/lib/azure-storage-with-credential';
export async function magiaCharacter(
  request: HttpRequest,
  context: InvocationContext,
): Promise<HttpResponseInit> {
  const { uid, id } = request.params;
  context.log(`uid: ${uid}, id: ${id}`);
  const bloblClient = getBlobClient();
  const container = bloblClient.getContainerClient('$web');
  const blob = container.getBlobClient(
    `fall-magia/character-data/${uid}/${id}/character-data.json`,
  );
  const res = await blob.download();
  if (res.errorCode || !res.readableStreamBody) {
    throw new Error('Blob not found');
  }
  const data = JSON.parse(
    (await streamToBuffer(res.readableStreamBody)).toString(),
  );
  return {
    status: 200,
    headers: new Headers([['Content-Type', 'application/json']]),
    body: JSON.stringify(data),
  };
}
app.http('magiaCharacter', {
  methods: ['GET'],
  authLevel: 'anonymous',
  route: 'magia-character/{uid}/{id}',
  handler: magiaCharacter,
});
function streamToBuffer(
  readableStreamBody: NodeJS.ReadableStream,
): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    readableStreamBody.on('data', (chunk) => {
      const content = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
      chunks.push(content);
    });
    readableStreamBody.on('end', () => {
      const buffer = Buffer.concat(chunks);
      resolve(buffer);
    });
    readableStreamBody.on('error', reject);
  });
}
マネージドIDの設定
リソースグループのアクセス制御(IAM)から設定する
BloBストレージのCRUD操作ができるロールを選択
メンバーから自分を選択
選択したメンバーになったことを確認して、選択ボタンを押下
条件は特になし
スコープがリソースグループ内になっていることを確認して、割り当て
確認
割り当て後、ローカルでnpm run startでAPIを立ち上げ、アクセスしたところBlobコンテナ内のファイルを取得できた。
メモ
マネージドIDのロールを割り当てる前は下記のエラーが発生。
 System.Private.CoreLib: Exception while executing function: Functions.magiaCharacter. System.Private.CoreLib: Result: Failure
Exception: This request is not authorized to perform this operation using this permission.
参考
Azure SDK for JavaScript を使用して Azure リソースに対して Azure でホストされるアプリを認証する
TypeScript を使って BLOB をダウンロードする
2024.09.19 追記
GUIではなくbicepを使って設定する方法を追記する。
@description('Specifies the role definition ID used in the role assignment.')
param roleDefinitionID string
@description('Specifies the principal ID assigned to the role.')
param principalId string
// リソースグループの単位でロールを割り当てる
var roleAssignmentName= guid(principalId, roleDefinitionID, resourceGroup().id)
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: roleAssignmentName
  properties: {
    roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionID)
    principalId: principalId
  }
}
#!/bin/bash
BIN_DIR=$(cd $(dirname $0) && pwd)
BICEP_DIR=$(cd $BIN_DIR/../biceps && pwd)
# 環境変数読み込み
source $BIN_DIR/.env
# ローカルでログイン中のユーザのIDを取得
PRINCIPAL_ID=$(az ad signed-in-user show --query id -o tsv)
# 組み込みロール
# https://learn.microsoft.com/ja-jp/azure/role-based-access-control/built-in-roles
# ストレージ BLOB データ共同作成者 のロールを割り当てる
cd $BICEP_DIR && az deployment group create \
  --name frontendDeployment \
  --template-file rbac.bicep \
  --parameters roleDefinitionID=ba92f5b4-2d11-453d-a403-e96b0029c9fe \
  principalId=$PRINCIPAL_ID \
  -g $RESOURCE_GROUP_NAME
# ストレージ キュー データ共同作成者 のロールを割り当てる
cd $BICEP_DIR && az deployment group create \
  --name frontendDeployment \
  --template-file rbac.bicep \
  --parameters roleDefinitionID=974c5e8b-45b9-4653-ba55-5f855dd0fb88 \
  principalId=$PRINCIPAL_ID \
  -g $RESOURCE_GROUP_NAME
参考: 組み込みロールのroleDefinitionID
参考: クイックスタート: Bicep を使用して Azure でのロールを割り当てる
なお、ローカルから接続する場合はファイアウォールの設定で許可する必要がある。
下記はすべて開けた場合のbicep
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: '${storageAccountName}storage'
  location: location
  sku: { name: storageAccountSku }
  kind: 'StorageV2'
  properties: {
+    allowBlobPublicAccess: true
    minimumTlsVersion: 'TLS1_2'
+    publicNetworkAccess: 'Enabled'
  }
}
試す。
ソースコード
cd apps/api && npm run dev
post http://localhost:7071/api/characters/async
Content-Type: application/json
{
  "CharacterID": "test1",
  "CharacterName": "テストキャラクターあるふぁ"
}






