使用 Azure 活动目录进行身份验证时调用 Azure 管理库 API 时出错

Error making Azure Management Library API call when authenticating with azure active directory

我的公司正在研究 Azure 上的报告。我们只希望我们的客户给我们只读凭证供我们使用。我做了一些研究,看起来 Azure Active Directory 就是这样做的。所以我希望使用只读的 Azure 目录应用程序进行身份验证。

为了让我开始,我关注了这个关于通过 Azure Active Directory 使用管理 API 的博客。

https://msdn.microsoft.com/en-us/library/azure/dn722415.aspx

除了approach show很不友好外,没用=(

我以全局管理员身份登录后出现此错误:

"AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'."

做了一些研究,发现这种身份验证方式适用于本机应用程序,而不适用于网络应用程序(尽管博客 post 说的不是这样......)。所以我做了一个调整。我的 GetAuthorizationHeader 现在看起来像这样:

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }

我能够获得访问令牌(是的)。但是现在,当我尝试将其与 Azure 管理库客户端一起使用时,出现此错误:

"ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription."

我仔细检查了我在应用程序中的权限。看起来不错。我尝试授予对所有内容的完全访问权限,看看这是否会产生影响。

我仔细检查了我的 tenantId、clientId 和 subscriptionId,一切正常。

我确保我正在使用的订阅指向我的应用程序所在的 AD。

我尝试制作一个新的密钥。

我猜这是问题所在: 然而,在这个 UI 中,我无法 select 那个 属性 的任何值。我不确定这是错误还是未完成的功能的结果。 我在这里遗漏了什么吗?

谢谢

这是我的完整代码供参考:

class Program
{
    static void Main(string[] args)
    {
        var token = GetAuthorizationHeader();

        var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);

        using (var computeClient = new ComputeManagementClient(credential))
        {
            var images = computeClient.VirtualMachineOSImages.List();
        }
    }

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }
}

编辑: 取得了进展。正如我与 Gaurav 讨论的那样,我需要放弃 Azure 管理库,因为目前它似乎不支持 Azure 资源管理器 (ARM) API!因此,我改为执行原始 Web 请求。它按预期工作。如果我从我的 AD 应用程序中删除角色访问权限,我将被拒绝访问。当我拥有它时,我会取回数据。

我不确定的一件事是我的应用程序是否会自动添加到新资源。

另外,有没有办法列出我的 AD 应用程序可以访问的资源组?

新代码:

    class Program
{
    static void Main(string[] args)
    {
        var token = GetAuthorizationHeader();

        string subscriptionId = ConfigurationManager.AppSettings["subscriptionId"];
        string resourceGroupName = ConfigurationManager.AppSettings["resourceGroupName"];
        var uriListMachines = string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualmachines?api-version=2015-05-01-preview", subscriptionId, resourceGroupName);
        var t = WebRequest.Create(uriListMachines);
        t.ContentType = "application/json";
        t.Headers.Add("Authorization", "Bearer " + token);
        var response = (HttpWebResponse)t.GetResponse();

        string result = "";
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            result = reader.ReadToEnd(); 
        }

        //Original Attempt:
        //var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"], token);

        //using (var client = CloudContext.Clients.CreateComputeManagementClient(credential))
        //{
        //    var images = client.VirtualMachineVMImages.List();
        //}
    }

    private static string GetAuthorizationHeader()
    {
        AuthenticationResult result = null;

        var context = new AuthenticationContext("https://login.windows.net/" + ConfigurationManager.AppSettings["tenantId"]);

        string clientId = ConfigurationManager.AppSettings["clientId"];
        string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
        ClientCredential clientCred = new ClientCredential(clientId, clientSecret);

        var thread = new Thread(() =>
        {
            result = context.AcquireToken(
              "https://management.core.windows.net/",
              clientCred);
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Name = "AquireTokenThread";
        thread.Start();
        thread.Join();

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;
        return token;
    }
}

编辑编辑: 我想通了我的挂断电话。在旧门户中创建的资源将拥有自己独特的资源组。

据我所知,您无法添加在旧门户现有资源组 (boooo) 中创建的资源。在新门户中创建的资源将能够将资源分配给现有组(也就是赋予角色访问我的 AD 应用程序的组)。

这真是一团糟!但至少我知道现在发生了什么。

关于为什么 运行 遇到这个问题,我相信你是在正确的轨道上。

这是正在发生的事情:

本质上执行Service Management API的权限是一个delegated permission and not an application permission。换句话说,API 是在为其获取令牌的用户的上下文中执行的。现在您正在为您的应用程序获取此令牌(由客户端 id/secret 指定)。但是,您的应用程序无权访问您的 Azure 订阅,因为在您的 Azure AD 中为此应用程序创建的用户记录类型为 Service Principal。由于此服务委托人无权访问您的 Azure 订阅,您将收到此 Forbidden Error(我必须说该错误具有误导性,因为您根本没有使用证书)。

您可以做几件事:

  1. 切换到 Azure 资源管理器 (ARM) API - ARM API 是下一代服务管理 API (SM API) 而 Azure 只是朝着这个方向发展。它专门用于 Azure AD 令牌。如果可能,请利用它来管理您的 Azure 资源(尽管您需要记住,截至目前,并非所有 Azure 资源都可以通过 ARM API 进行管理)。他们这样做的方式是获取您的服务主体并使用 new Azure Portal. Please see this link for more details on this: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.
  2. 将其分配给特定角色
  3. 使用 X509 证书 - 您始终可以使用基于 X509 证书的授权来授权您的 SM API 请求。请参阅此 link 了解更多详细信息:https://msdn.microsoft.com/en-us/library/azure/ee460782.aspx#bk_cert。这种方法的缺点是应用程序(或任何有权访问此证书的人)将获得对你的 Azure 订阅的完全访问权限,并可以在那里执行所有操作(包括删除资源)。
  4. 为用户而非应用程序获取令牌 - 这是您可以采用的另一种方法。本质上要求您的用户通过您的控制台应用程序登录到 Azure AD 并为该用户获取令牌。同样,请记住,此用户必须是您的 Azure 订阅中的 Co-Admin,并且将拥有对您的 Azure 订阅的完全访问权限,因为 SM API 没有 Role-based access control 的概念。