You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:14:51