创建流分析作业时授权失败

Authorization failure when creating a Stream Analytics job

我一直在尝试(但失败了)以编程方式创建 Azure 流分析作业。我最初是在关注这个例子:

https://azure.microsoft.com/en-gb/documentation/articles/stream-analytics-dotnet-management-sdk/

但它会弹出一个对话框供您登录。我希望能够在服务器端执行此操作。看来我需要使用 Azure AD 才能使用资源管理器 API。我一直在努力解决这个问题:

https://msdn.microsoft.com/en-us/library/azure/dn790557.aspx#bk_portal

代码如下所示:

var authContext = new AuthenticationContext("https://login.microsoftonline.com/{tenant id}/oauth2/token");
var clientId = "{app client id}";
var appKey = "{app key}";
var subscriptionId = "{subscription id}";
var clientCredential = new ClientCredential(clientId, appKey);

var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);

var client = new StreamAnalyticsManagementClient(creds);

var jobCreateParameters = new JobCreateOrUpdateParameters
        {
            Job = new Job
            {
                Name = streamAnalyticsJobName,
                Location = "North Europe",
                Properties = new JobProperties
                {
                    EventsOutOfOrderPolicy = EventsOutOfOrderPolicy.Adjust,
                    Sku = new Sku
                    {
                        Name = "Standard"
                    }
                }
            }
        };

 var jobCreateResponse = client.StreamingJobs.CreateOrUpdate(resourceGroupName, jobCreateParameters);

我可以成功获取令牌,但创建作业失败:

AuthorizationFailed: The client 'REDACTED' with object id 'REDACTED' does not have authorization to perform action 'Microsoft.StreamAnalytics/streamingjobs/write' over scope '/subscriptions/REDACTED/resourcegroups/REDACTED/providers/Microsoft.StreamAnalytics/streamingjobs/REDACTED'

我做错了什么吗?该应用已设置委派权限。

更新 - 2015 年 12 月 8 日

现在有一种简单的方法可以将角色分配给服务主体。请参阅此 link 了解更多详情:https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/.


原始回复

当您向 Azure 订阅授予对应用程序的访问权限时,会在后台使用您的 Azure AD 中的 Service Principal 用户类型创建一个用户。您在下面使用的代码假定您在获取访问令牌时使用此 Service Principal 用户。

var clientCredential = new ClientCredential(clientId, appKey);

var result = authContext.AcquireToken("https://management.core.windows.net/", clientCredential);
var creds = new TokenCloudCredentials(subscriptionId, result.AccessToken);

但是默认情况下,此用户未被授予对您的订阅的任何权限 (RBAC),这就是您收到授权错误的原因。

要解决此问题,您需要在您的订阅中为该用户授予适当的角色。现在您可以使用 PowerShell 来执行此操作,或者您可以使用 ADAL 库通过代码 + 发出一些 Web 请求来执行此操作。

我所做的是使用 ADAL 库获取访问令牌,然后使用 Google Postman(或 Fiddler)做其他事情。就我而言,它是一个网络应用程序。这是我所做的:

  1. 我以 Global Administrator(订阅所有者)身份登录到应用程序并获得 code。使用 code 和 ADAL 库,我获得了访问令牌(我们称之为 token1)。

        var authContext = new AuthenticationContext(string.Format("{0}/common", signinEndpoint));//signinEndpoint = https://login.windows.net
        var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, credential);
    
  2. 我复制了上面result中返回给我的租户ID和访问令牌。

  3. 接下来我做的是使用 POSTMAN 找到 Service Principal 用户的 object id。这是我在那里执行的 GET URL。对于 Authorization header,您需要使用 Bearer {token1}.

    https://graph.windows.net/{subscription-id}/servicePrincipals?api-version=1.5&$filter=appId eq '{app-client-id}'

  4. 之后,我使用以下代码为服务管理 API 操作获取了另一个访问令牌(我们称之为 token2):

    authContext = new AuthenticationContext(string.Format("{0}/{1}", signinEndpoint, result.TenantId));
    result = await authContext.AcquireTokenSilentAsync(serviceManagementApiEndpoint, credential, new UserIdentifier(request.UserInfo.UniqueId, UserIdentifierType.UniqueId));//serviceManagementApiEndpoint = https://management.core.windows.net/
    
  5. 之后,我在我的订阅中列出了角色,并选择了我想分配给我的 Service Principal 用户的角色。在我的例子中,我想分配一个 Reader 角色,所以我记下了角色的 ID。对于 Authorization header,您需要使用 Bearer {token2}.

    https://management.azure.com/subscriptions/{subscription-id}/providers/Microsoft.Authorization/roleDefinitions?api-version=2015-06-01

  6. 接下来是将此角色分配给用户。为此,我创建了一个 guid 作为 role assignment id 并使用了以下 URL:

    https://management.azure.com/subscriptions/{subscription-id}/providers/microsoft.authorization/roleassignments/{role-assignment-id}?api-version=2015-06-01

这将是一个 PUT 请求,这是请求 body 将类似于:

{
"properties":
    {
        "roleDefinitionId": "{role id of the selected role from step 5}",
        "principalId": "{id of the service principal from step 3"
    }
} 

请确保请求的 content-type 设置为 application/json;odata=verbose 而不是 application/json.

差不多就这些了!之后你的代码应该可以正常工作了:)

试一试,看看会发生什么。