使用Microsoft Graph获取Azure Web App持续集成部署的访问令牌

问题描述

我试图通过C#以编程方式连接与持续集成到Azure Web App的源代码控制。历史上我曾经使用Azure PowerShell来做同样的事情。

在C#端,我正在使用Microsoft.Azure.Management.Fluent库。连接源代码管理的代码非常简单:

await webApp.Update().DefinesourceControl().WithContinuouslyIntegratedGitHubRepository(GIT_URL).WithBranch(GIT_BRANCH).WithGitHubAccesstoken(GIT_TOKEN).Attach().ApplyAsync();

代码运行约5分钟,然后返回错误

{“代码”:“ BadRequest”,“消息”:“参数x-ms-client-principal-name 为null或为空。“}

我最初将其解释为Fluent库没有将必要的值传递给API,因此我尝试直接使用Fluent库来访问API以抽象授权部分:

var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal( CLIENT_ID,CLIENT_SECRET,TENANT_ID,AzureEnvironment.AzureGlobalCloud);

var client = RestClient.Configure().WithEnvironment(AzureEnvironment.AzureGlobalCloud).WithCredentials(credentials).Build();

CancellationToken cancellationToken = new CancellationToken();

var request = new HttpRequestMessage(HttpMethod.Get,$"https://management.azure.com/subscriptions/{SUBSCRIPTION_ID}/resourcegroups/{RESOURCE_GROUP}?api-version=2019-10-01");
request.Headers.Add("x-ms-client-principal-name",USERNAME);
client.Credentials.ProcessHttpRequestAsync(request,cancellationToken).GetAwaiter().GetResult();
var httpClient = new HttpClient();
var response = httpClient.SendAsync(request,HttpCompletionoption.ResponseHeadersRead,cancellationToken).GetAwaiter().GetResult();

var result = response.Content.ReadAsstringAsync().GetAwaiter().GetResult();

方法的几种变体导致引用 x-ms-client-principal-name的相同错误这使我相信问题出在使用令牌和相关权限。为了测试这一点,我运行了上面提到的PowerShell脚本,看着它通过fiddler运行,并获取了它用来完成的令牌。将该令牌与基本相同的代码一起使用,效果很好:

var token = "<THE TOKEN I copIED FROM fiddleR>";
CancellationToken cancellationToken = new CancellationToken();

var request = new HttpRequestMessage(requestType,$"https://management.azure.com/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEB_APP}/sourcecontrols/web?api-version=2015-08-01");

request.Headers.Add("Authorization",$"Bearer {token}");

if ((requestType == HttpMethod.Put || requestType == HttpMethod.Post) && !string.IsNullOrEmpty(postData))
{
    request.Content = new StringContent(postData,Encoding.UTF8,"application/json");
}

var httpClient = new HttpClient();
var response = httpClient.SendAsync(request,cancellationToken).GetAwaiter().GetResult();

var result = response.Content.ReadAsstringAsync().GetAwaiter().GetResult();

因此,现在看来,这只是我自己获取访问令牌的问题。这就是事情变得困难的地方。如果获得令牌作为服务主体,则最终得到与开始时相同的 x-ms-client-principal-name

public static string GetAuthToken(string tenantId,string clientId,string clientSecret)
{
    var request = new HttpRequestMessage(HttpMethod.Post,$"https://login.microsoftonline.com/{tenantId}/oauth2/token");
    request.Content = new StringContent($"grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&resource=https://management.azure.com","application/x-www-form-urlencoded");
    CancellationToken cancellationToken = new CancellationToken();
    var httpClient = new HttpClient();
    var response = httpClient.SendAsync(request,cancellationToken).GetAwaiter().GetResult();

    var result = response.Content.ReadAsstringAsync().GetAwaiter().GetResult();

    var auth = JsonConvert.DeserializeObject<AuthResponse>(result);

    return auth.Accesstoken;
}

当我尝试使用用户名和密码获取令牌时,我收到一条错误消息:

AADSTS90002:找不到租户“。如果没有,可能会发生这种情况 租户的有效订阅。检查以确保您拥有 正确的租户ID。请与您的订阅管理员联系。

这是我使用的代码

public static string GetAuthToken(string tenantId,string username,string password,$"https://login.microsoftonline.com/{tenantId}/oauth2/token");
    request.Content = new StringContent($"grant_type=password&username={username}&password={password}&client_id={clientId}&client_secret={clientSecret}&resource=https://management.azure.com",cancellationToken).GetAwaiter().GetResult();

    var result = response.Content.ReadAsstringAsync().GetAwaiter().GetResult();

    var auth = JsonConvert.DeserializeObject<AuthResponse>(result);

    return auth.Accesstoken;
}

我无法让C#代码作为一种变通方法调用PS脚本,因为它需要Azure PowerShell,但不能保证该Azure PowerShell可以在运行该代码的计算机上运行(Azure Web App),并且由于管理员限制而无法安装。 / p>

我需要能够获得一个访问令牌,该访问令牌也具有azure开发人员(正式为VSTS)的权限,因此我可以绑定/连接源代码控制程序以进行持续集成。任何可以帮助我克服这一困难的指导都将受到赞赏。

解决方法

我终于能够使它工作。首先,我必须创建一个新的Azure AD用户并为其授予必要的权限。这不适用于我的常规Azure登录名。有了新的用户和权限,我就能成功获得令牌,如下所示:

public static async Task<string> GetAccessToken(string clientId,string userName,string password)
{
    AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/<TENANT_ID>");
    var resourceId = "https://management.azure.com";
    var result = await authenticationContext.AcquireTokenAsync(resourceId,clientId,new UserPasswordCredential(userName,password));
    return result.AccessToken;
}

此令牌随后用于连接源代码控制和持续集成。