Я пытаюсь использовать VSTS (теперь Azure DevOps) для создания конвейера CI/CD. Для моего конвейера сборки у меня есть очень простая настройка, включающая выполнение шагов восстановления, сборки, тестирования и публикации.
Для моего тестового шага у меня есть настройка для запуска двух тестовых проектов - одного модульного тестового проекта и одного интеграционного тестового проекта. У меня есть настройка политики доступа к ключам Key для обеспечения доступа как к себе, так и к Azure Devops. Когда я запускаю свои тесты локально с помощью visual studio, так как я зашел в ту же учетную запись, которая имеет доступ к хранилищу ключей azure, я могу запускать тесты без каких-либо ошибок.
Мое приложение настроено на доступ к ключевому хранилищу, используя следующую настройку:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((ctx, builder) =>
{
var keyVaultEndpoint = GetKeyVaultEndpoint();
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
builder.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
}
}
)
.UseStartup<Startup>();
Когда я запускаю конвейер сборки, я использую экземпляр Hosted VS2017 для создания моего проекта. Все работает, за исключением тестов интеграции, которые пытаются получить доступ к отказу ключей. Я использую следующие пакеты:
- Microsoft.Azure.Services.AppAuthentication - позволяет легко извлекать токены доступа для сценариев аутентификации Service-to-Azure-Service.
- Microsoft.Azure.KeyVault - содержит методы взаимодействия с Key Vault.
- Microsoft.Extensions.Configuration.AzureKeyVault - содержит
Расширения IConfiguration для хранилища ключей Azure
Я выполнил этот учебник https://docs.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault, чтобы настроить хранилище ключей и интегрировать его в мое приложение.
Я просто пытаюсь заставить свою сборку работать, убедившись, что пройдут тестирование модуля и интеграции. Я пока не развертываю его в службе приложений. Модульные тесты запускаются без каких-либо проблем, поскольку я издеваюсь над различными службами. Мой тест интеграции не работает с сообщениями об ошибках ниже. Как получить доступ к тестовому доступу к хранилищу ключей? Нужно ли добавлять какие-либо специальные политики доступа в мое хранилище ключей для размещенной сборки VS2017? Не уверен, что делать, поскольку я не вижу ничего, что выделяется.
Ниже приведена трассировка стека для ошибки:
2018-10-16T00:37:04.6202055Z Test run for D:\a\1\s\SGIntegrationTests\bin\Release\netcoreapp2.1\SGIntegrationTests.dll(.NETCoreApp,Version=v2.1)
2018-10-16T00:37:05.3640674Z Microsoft (R) Test Execution Command Line Tool Version 15.8.0
2018-10-16T00:37:05.3641588Z Copyright (c) Microsoft Corporation. All rights reserved.
2018-10-16T00:37:05.3641723Z
2018-10-16T00:37:06.8873531Z Starting test execution, please wait...
2018-10-16T00:37:51.9955035Z [xUnit.net 00:00:40.80] SGIntegrationTests.HomeControllerShould.IndexContentTypeIsTextHtml [FAIL]
2018-10-16T00:37:52.0883568Z Failed SGIntegrationTests.HomeControllerShould.IndexContentTypeIsTextHtml
2018-10-16T00:37:52.0884088Z Error Message:
2018-10-16T00:37:52.0884378Z Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException : Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
2018-10-16T00:37:52.0884737Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response: {"error":"invalid_request","error_description":"Identity not found"}
2018-10-16T00:37:52.0884899Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "C:\Users\VssAdministrator\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json"
2018-10-16T00:37:52.0885142Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. Process took too long to return the token.
2018-10-16T00:37:52.0885221Z
2018-10-16T00:37:52.0885284Z Stack Trace:
2018-10-16T00:37:52.0885349Z at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsyncImpl(String authority, String resource, String scope)
2018-10-16T00:37:52.0885428Z at Microsoft.Azure.KeyVault.KeyVaultCredential.PostAuthenticate(HttpResponseMessage response)
2018-10-16T00:37:52.0885502Z at Microsoft.Azure.KeyVault.KeyVaultCredential.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2018-10-16T00:37:52.0886831Z at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable'1 maxresults, Dictionary'2 customHeaders, CancellationToken cancellationToken)
2018-10-16T00:37:52.0886887Z at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable'1 maxresults, CancellationToken cancellationToken)
2018-10-16T00:37:52.0886935Z at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
2018-10-16T00:37:52.0887000Z at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
2018-10-16T00:37:52.0887045Z at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList'1 providers)
2018-10-16T00:37:52.0887090Z at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
2018-10-16T00:37:52.0887269Z at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
2018-10-16T00:37:52.0887324Z at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
2018-10-16T00:37:52.0887371Z at Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder, IFeatureCollection featureCollection)
2018-10-16T00:37:52.0887433Z at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory'1.CreateServer(IWebHostBuilder builder)
2018-10-16T00:37:52.0887477Z at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory'1.EnsureServer()
2018-10-16T00:37:52.0887525Z at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory'1.CreateDefaultClient(DelegatingHandler[] handlers)
Обновить
Я нашел только одну связанную запись по этой проблеме: https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview, Но сообщение связано с развертыванием приложения в лазурном слоте. Я просто пытаюсь создать свое приложение в конвейере сборки.
Я все еще пытаюсь решить эту проблему, и я не уверен, что лучший способ обеспечить необходимый доступ.
Обновление 2
Я до сих пор не нашел для этого решения. Я теряюсь на том, как получить мой конвейер для запуска моего теста без проблем. Я видел, что в конвейере выпуска есть варианты запуска тестов. Но, похоже, они берут файлы.dll, а файл с откатным конвейером сборки имеет только веб-приложение (я не вижу ни одного из опубликованных файлов пробных проектов). Не уверен, что это даже возможность.
Обновление 3
Мне удалось заставить его работать, используя последний предоставленный здесь вариант: https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support
Я попробовал другие способы использования сертификата, но в любое время {CurrentUser} предоставляется в строке подключения, сбой конвейера сборки. Он работает на моей локальной машине, но не в конвейере сборки.
Чтобы заставить его работать, мне пришлось сделать три вещи:
- Войдите в Azure. Настройка регистрации нового приложения в Azure AD
- В вашей новой регистрации приложения AD создайте новый клиентский секрет
-
Предоставьте свой новый доступ к приложениям AD в хранилище ключей. Перейдите в свои политики доступа к хранилищу ключей и добавьте приложение, созданное в вашем AD, с доступом для чтения к вашим секретам.
-
Изменен мой вызов AzureServiceTokenProvier() в файле Program.cs следующим образом:
var azureServiceTokenProvider = new AzureServiceTokenProvider("connectionString={your key vault endpoint};RunAs=App;AppId={your app id that you setup in Azure AD};TenantId={your azure subscription};AppKey={your client secret key}")
Обратите внимание, что секрет вашего клиента должен быть отформатирован правильно. Регистрация приложений (предварительный просмотр) генерирует случайный секретный ключ. Иногда этот ключ не работает в строке соединения (выдает ошибку как неправильно отформатированную). Либо попробуйте создать свой собственный ключ в версии предварительного просмотра без предварительного просмотра, либо сгенерируйте новый ключ и повторите попытку.
После этого я смог успешно запустить мой интеграционный тест в моем конвейере сборки и создать выпуск для моего веб-приложения в Azure. Я не удовлетворен этим подходом, потому что, хотя он работает, он раскрывает секретное значение в самом коде. Управление идентификацией службы не обязательно должно быть включено из-за вышеуказанного подхода. Я чувствую, что это очень плохо в этом отношении.
Должен быть лучший путь, чем это. Один из вариантов - не запускать интеграционный тест в конвейере сборки. Не уверен, что это правильный подход. Я все еще надеюсь, что кто-то сможет обеспечить лучший подход к этому или объяснить, подходит ли мой подход.