概要
Node.jsを使ってAzure Resource Graphの実行結果を取得した
環境
ローカル
- Windows 11
- Core Tools Version: 4.0.7030 Commit hash: N/A +bb4c949899cd5659d6bfe8b92cc923453a2e8f88 (32-bit)
Function Runtime Version: 4.1037.0.23568
Functions
- オペレーティング システム: Linux
- App Service プラン: JapanEastLinuxDynamicPlan (Y1: 0)
- ランタイム バージョン: 4.1038.400.1
- Node.js バージョン: Node.js 20 LTS
ポータルでクエリの確認
resources
| where type == 'microsoft.storage/storageaccounts'
| where resourceGroup == 'async-ttrpg'
2件取得できた。
組み込みロールの確認
組み込みのロール | 説明 | ID |
---|---|---|
Reader | すべてのリソースを表示しますが、変更を加えることはできません。 | acdd72a7-3385-48ef-bd42-f606fba81ae7 |
日本語のポータルから見ると「Reader」が「閲覧者」と訳されている。
わかりにくい。
ローカル実行用に組み込みロールの設定
自分で作成したリソースグループだと共同編集者権限を持っているのでこの作業は不要
基本的なやりかたはAzure Functionsをローカル実行してマネージドIDを使った認証でBlobStorageにアクセスしたメモと同様。選ぶロールが異なる。
ソースコード
パッケージのインストール。@azure/arm-resourcegraphのlatestの4.2.1だとNodeのfetchのエラーが出たのでaplpha版v5を選ぶ。
npm i @azure/arm-resourcegraph@5.0.0-alpha.20250506.1 @azure/identity
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { DefaultAzureCredential } from '@azure/identity';
import { Hono } from 'hono';
const app = new Hono().post('/', async (c) => {
const creds = new DefaultAzureCredential();
const client = new ResourceGraphClient(creds);
const result = await client.resources({
query: `
resources
| where type == 'microsoft.storage/storageaccounts'
| where resourceGroup == 'async-ttrpg'
`,
});
return c.json(result);
});
export default app;
サンプルコードの記法
npm のサンプルコードの書きかたをすると型エラーとなるのでコメントで回避している。
型エラーとなっても動作はした。私の環境ではsubscriptionIdを入れても入れなくとも結果は変わらなかった。
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { DefaultAzureCredential } from '@azure/identity';
import { Hono } from 'hono';
const subscriptionId = '00000000-0000-0000-0000-000000000000';
const app = new Hono().post('/', async (c) => {
const creds = new DefaultAzureCredential();
// @ts-expect-error subscriptionId is not a valid parameter
const client = new ResourceGraphClient(creds, subscriptionId);
const result = await client.resources({
query: `
resources
| where type == 'microsoft.storage/storageaccounts'
| where resourceGroup == 'async-ttrpg'
`,
});
return c.json(result);
});
export default app;
ローカル実行
ローカルで起動して問い合わせてみる。
post http://localhost:7071/api/sdk-test
実行結果
下記のような形で2件取れる。
{
"totalRecords": 2,
"count": 2,
"resultTruncated": "false",
"data": {
"0": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-resource-group/providers/Microsoft.Storage/storageAccounts/examplestorageaccount1",
"name": "examplestorageaccount1",
"type": "microsoft.storage/storageaccounts",
"tenantId": "00000000-0000-0000-0000-000000000000",
"kind": "StorageV2",
"location": "eastasia",
"resourceGroup": "example-resource-group",
"subscriptionId": "00000000-0000-0000-0000-000000000000",
"managedBy": "",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"plan": null,
"properties": {
"provisioningState": "Succeeded",
"publicNetworkAccess": "Enabled",
"encryption": {
"keySource": "Microsoft.Storage",
"services": {
"blob": {
"enabled": true,
"lastEnabledTime": "2025-01-01T01:00:00.0000000Z",
"keyType": "Account"
},
"file": {
"enabled": true,
"lastEnabledTime": "2025-01-01T01:00:00.0000000Z",
"keyType": "Account"
}
}
},
"privateEndpointConnections": [],
"supportsHttpsTrafficOnly": true,
"allowBlobPublicAccess": true,
"allowCrossTenantReplication": false,
"minimumTlsVersion": "TLS1_2",
"primaryEndpoints": {
"blob": "https://examplestorageaccount1.blob.core.windows.net/",
"file": "https://examplestorageaccount1.file.core.windows.net/",
"table": "https://examplestorageaccount1.table.core.windows.net/",
"queue": "https://examplestorageaccount1.queue.core.windows.net/",
"web": "https://examplestorageaccount1.z11.web.core.windows.net/",
"dfs": "https://examplestorageaccount1.dfs.core.windows.net/"
},
"statusOfPrimary": "available",
"primaryLocation": "eastasia",
"keyCreationTime": {
"key2": "2025-01-01T01:00:00.0000000Z",
"key1": "2025-01-01T01:00:00.0000000Z"
},
"creationTime": "2025-01-01T01:00:00.0000000Z",
"networkAcls": {
"virtualNetworkRules": [],
"defaultAction": "Allow",
"ipv6Rules": [],
"ipRules": [
{
"value": "0.0.0.1",
"action": "Allow"
},
{
"value": "0.0.0.2",
"action": "Allow"
},
{
"value": "0.0.0.3",
"action": "Allow"
}
],
"bypass": "AzureServices",
"resourceAccessRules": []
},
"accessTier": "Hot"
},
"tags": {},
"identity": null,
"zones": null,
"extendedLocation": null
},
"1": {
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-resource-group/providers/Microsoft.Storage/storageAccounts/examplestorageaccount2",
"name": "examplestorageaccount2",
"type": "microsoft.storage/storageaccounts",
"tenantId": "00000000-0000-0000-0000-000000000000",
"kind": "StorageV2",
"location": "eastasia",
"resourceGroup": "example-resource-group",
"subscriptionId": "00000000-0000-0000-0000-000000000000",
"managedBy": "",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"plan": null,
"properties": {
"provisioningState": "Succeeded",
"encryption": {
"keySource": "Microsoft.Storage",
"services": {
"blob": {
"enabled": true,
"lastEnabledTime": "2024-01-01T01:00:00.0000000Z",
"keyType": "Account"
},
"file": {
"enabled": true,
"lastEnabledTime": "2024-01-01T01:00:00.0000000Z",
"keyType": "Account"
}
}
},
"privateEndpointConnections": [],
"supportsHttpsTrafficOnly": true,
"allowBlobPublicAccess": false,
"allowCrossTenantReplication": false,
"minimumTlsVersion": "TLS1_0",
"primaryEndpoints": {
"blob": "https://examplestorageaccount2.blob.core.windows.net/",
"file": "https://examplestorageaccount2.file.core.windows.net/",
"table": "https://examplestorageaccount2.table.core.windows.net/",
"queue": "https://examplestorageaccount2.queue.core.windows.net/",
"web": "https://examplestorageaccount2.z11.web.core.windows.net/",
"dfs": "https://examplestorageaccount2.dfs.core.windows.net/"
},
"statusOfPrimary": "available",
"primaryLocation": "eastasia",
"keyCreationTime": {
"key2": "2024-01-01T01:00:00.0000000Z",
"key1": "2024-01-01T01:00:00.0000000Z"
},
"creationTime": "2024-01-01T01:00:00.0000000Z",
"networkAcls": {
"virtualNetworkRules": [],
"defaultAction": "Allow",
"ipv6Rules": [],
"ipRules": [],
"bypass": "AzureServices"
},
"accessTier": "Hot"
},
"tags": {},
"identity": null,
"zones": null,
"extendedLocation": null
}
},
"facets": []
}
組み込みロールの設定
Functionsにロールの設定が不足していると下記エラーとなる。
Access is denied to the requested resource. The user might not have enough permission.
設定してみる。
設定が完了すると、エラーが発生しなくなった。
5分ほどまつとロール割り当てに下記のように表示されることを確認できる。(作成されたと通知されても、ここに反映されるまでにはラグがあった)
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 functionAppName string
resource functionApp 'Microsoft.Web/sites@2024-04-01' existing = {
name: functionAppName
}
var roleAssignmentName= guid(functionAppName, roleDefinitionID, resourceGroup().id)
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: roleAssignmentName
properties: {
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionID)
principalId: functionApp.identity.principalId
}
}
#!/bin/bash
BIN_DIR=$(cd $(dirname $0) && pwd)
BICEP_DIR=$(cd $BIN_DIR/../biceps && pwd)
# 環境変数読み込み
source $BIN_DIR/.env
# Reader ( 閲覧者 ) のロールを割り当てる
cd $BICEP_DIR && az deployment group create \
--name frontendDeployment \
--template-file rbacToFunction.bicep \
--parameters roleDefinitionID=acdd72a7-3385-48ef-bd42-f606fba81ae7 \
functionAppName=$APP_NAME \
-g $RESOURCE_GROUP_NAME
APP_NAME=hogehoge
手動で行ったときに比べ、リソース名がリソースグループになったが、問題なく動いた。
参考
npm @azure/arm-resourcegraph ... TypeError: Expected signal to be an instanceof AbortSignal
が発生。おそらく古くてNodeのfetchの仕様変更についていけていない
npm @azure/arm-resourcegraph 5.0.0-alpha.20250506.1 ... こちらだとAbortSignalは発生せず。
@azure/arm-resourcegraph package
@azure/arm-resourcegraph package ResourceGraphClient resources
Azure Resource Graph (ARG) に馴染んでみる
訳あってMicrosoft Graph API調べてみた
Resource Graph API で Azure のリソース情報を取得する
Azure Resource Graph で Azure リソースの変更を検知して通知する
Azure Resource GraphでAzureリソースを可視化する!
Azure Resource Graphで簡単にリソースを検索する方法
Azure Functionsをローカル実行してマネージドIDを使った認証でBlobStorageにアクセスしたメモ
Azure FunctionsのマネージドIDを有効化してBlobStorageにアクセスしたメモ
Azure 組み込みロール