Azure Node.js SDK使用服务主体列资源组返回空结果且报错
问题描述
我尝试使用Azure Node.js SDK列出资源组,使用交互式登录可正常运行,但使用服务主体登录时无法正常工作。
代码示例
const { ClientSecretCredential, DefaultAzureCredential, } = require("@azure/identity"); const ComputeManagementClient = require('azure-arm-compute') const { ResourceManagementClient } = require("@azure/arm-resources"); var msRestAzure = require('ms-rest-azure'); const tenantId = process.env["AZURE_TENANT_ID"] || "XXXXXX"; const clientId = process.env["AZURE_CLIENT_ID"] || "XXXXX"; const secret = process.env["AZURE_CLIENT_SECRET"] || "XXXXXX"; const sub = 'XXXXXX'; async function listResourceGroups(){ msRestAzure.loginWithServicePrincipalSecret(clientId, secret, tenantId, async function (err, credentials) { // KO //msRestAzure.interactiveLogin(async function (err, credentials) { // OK const resourceClient = new ResourceManagementClient(credentials, sub); let resourceGroups = await resourceClient.resourceGroups.list(); console.log(resourceGroups) resourceGroups.forEach(element => {console.log(element)}) }) } listResourceGroups()
执行报错信息
{ next: [Function: next], byPage: [Function: byPage], [Symbol(Symbol.asyncIterator)]: [Function: [Symbol.asyncIterator]] } /temp/azure/resgroups.js:34 resourceGroups.forEach(element => {console.log(element)}) ^ TypeError: resourceGroups.forEach is not a function at /temp/azure/resgroups.js:34:20 at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
该服务主体已配置Reader权限,请问我遗漏了什么?
解决方案
1. 处理异步迭代器返回值
resourceClient.resourceGroups.list() 返回的是异步迭代器,而非直接的数组,因此无法直接调用forEach方法。交互式登录可能因隐式处理了迭代逻辑未暴露问题,但服务主体模式下会直接体现类型差异。
解决方式:
- 使用
for-await-of循环遍历:
async function listResourceGroups(){ msRestAzure.loginWithServicePrincipalSecret(clientId, secret, tenantId, async function (err, credentials) { if (err) { console.error('登录失败:', err); return; } const resourceClient = new ResourceManagementClient(credentials, sub); // 遍历异步迭代器 for await (const group of resourceClient.resourceGroups.list()) { console.log(group); } }) }
- 转换为数组后再使用
forEach:
async function listResourceGroups(){ msRestAzure.loginWithServicePrincipalSecret(clientId, secret, tenantId, async function (err, credentials) { if (err) { console.error('登录失败:', err); return; } const resourceClient = new ResourceManagementClient(credentials, sub); const groupsArray = []; for await (const group of resourceClient.resourceGroups.list()) { groupsArray.push(group); } groupsArray.forEach(element => console.log(element)); }) }
2. 统一使用新版SDK避免兼容性问题
你同时混用了旧版的msRestAzure和新版的@azure/identity,建议统一使用新版ClientSecretCredential创建服务主体凭证,减少潜在兼容问题:
const { ClientSecretCredential } = require("@azure/identity"); const { ResourceManagementClient } = require("@azure/arm-resources"); const tenantId = process.env["AZURE_TENANT_ID"] || "XXXXXX"; const clientId = process.env["AZURE_CLIENT_ID"] || "XXXXX"; const secret = process.env["AZURE_CLIENT_SECRET"] || "XXXXXX"; const sub = 'XXXXXX'; async function listResourceGroups(){ try { const credential = new ClientSecretCredential(tenantId, clientId, secret); const resourceClient = new ResourceManagementClient(credential, sub); const groupsArray = []; for await (const group of resourceClient.resourceGroups.list()) { groupsArray.push(group); } console.log('资源组列表:', groupsArray); } catch (err) { console.error('操作失败:', err); } } listResourceGroups()
3. 验证服务主体权限细节
即便配置了Reader权限,仍需确认:
- 权限是否已绑定到目标订阅(而非仅租户级别)
- 权限是否已生效(RBAC权限可能存在几分钟延迟)
- 服务主体所属租户ID与订阅的租户ID是否完全匹配
内容的提问来源于stack exchange,提问作者thomas
相关产品推荐
相关产品推荐

