0

我正在尝试通过 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)的权限,以便我可以绑定/连接源代码控制以进行持续集成。任何可以帮助我克服这个问题的指导都非常感谢。

4

1 回答 1

0

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

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

于 2020-11-01T21:54:32.343 回答