0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Azure Functionsをローカル実行してマネージドIDを使った認証でBlobStorageにアクセスしたメモ

Last updated at Posted at 2024-06-27

概要

Azure Blob Storageのデータを取得するAPIをローカルで動かした。
認証にはマネージドIDを使ってみたメモ。

ソースコード

コード

api/src/lib/azure-storage-with-credential.ts
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;
};

api/src/functions/magiaCharacter.ts
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)から設定する

image.png

image.png

BloBストレージのCRUD操作ができるロールを選択

image.png

メンバーから自分を選択

image.png

選択したメンバーになったことを確認して、選択ボタンを押下

image.png

条件は特になし

image.png

スコープがリソースグループ内になっていることを確認して、割り当て

image.png

確認

割り当て後、ローカルで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を使って設定する方法を追記する。

infra/biceps/rbac.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
  }
}
infra/bin/rbac.bash
#!/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": "テストキャラクターあるふぁ"
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?