我正在尝试通过 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();
此代码运行大约 5 分钟,然后返回错误:
{"Code":"BadRequest","Message":"参数 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, 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:未找到租户''。如果租户没有活动订阅,则可能会发生这种情况。检查以确保您拥有正确的租户 ID。请咨询您的订阅管理员。
这是我使用的代码:
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,不能保证它在运行代码(Azure Web 应用程序)的机器上,并且由于管理员限制而无法安装。
我需要能够获得一个访问令牌,该令牌也具有 azure dev ops(正式为 VSTS)的权限,以便我可以绑定/连接源代码控制以进行持续集成。任何可以帮助我克服这个问题的指导都非常感谢。