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

Using Microsoft Graph to obtain Access Token for Azure Web App Continuous Integration Deployment

我正在尝试通过 C# 以编程方式将源代码控制与 Azure Web 应用程序的持续集成联系起来。过去,我曾使用 Azure PowerShell 来做同样的事情。

在 C# 方面,我使用的是 Microsoft.Azure.Management.Fluent 库。连接源代码控制的代码非常简单:

await webApp.Update().DefineSourceControl().WithContinuouslyIntegratedGitHubRepository(GIT_URL).WithBranch(GIT_BRANCH).WithGitHubAccessToken(GIT_TOKEN).Attach().ApplyAsync();

此代码 运行s 大约 5 分钟,然后 returns 错误:

{"Code":"BadRequest","Message":"Parameter x-ms-client-principal-name is null or empty."}

我最初将其解释为 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, HttpCompletionOption.ResponseHeadersRead, 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", Encoding.UTF8, "application/x-www-form-urlencoded");
    CancellationToken cancellationToken = new CancellationToken();
    var httpClient = new HttpClient();
    var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();

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

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

    return auth.AccessToken;
}

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

AADSTS90002: Tenant '' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.

这是我为此使用的代码:

public static string GetAuthToken(string tenantId, string username, string password, string clientId, string clientSecret)
{
    var request = new HttpRequestMessage(HttpMethod.Post, $"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", Encoding.UTF8, "application/x-www-form-urlencoded");
    CancellationToken cancellationToken = new CancellationToken();
    var httpClient = new HttpClient();
    var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();

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

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

    return auth.AccessToken;
}

我不能让 C# 代码调用 PS 脚本作为变通方法,因为它需要 Azure PowerShell,而这不是 gua运行teed 在机器上 运行ning代码 (Azure Web App),由于管理员限制无法安装。

我需要能够获得一个访问令牌,该令牌也具有 azure dev ops(正式名称为 VSTS)的权限,因此我可以 bind/wire-up 源代码控制以进行持续集成。非常感谢任何可以帮助我解决这个问题的指导。

我终于能够让它工作了。首先,我必须创建一个新的 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;
}

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