J’essaie d’utiliser VSTS (maintenant Azure DevOps) pour créer un pipeline CI/CD. Pour mon pipeline de build, j’ai une configuration très basique impliquant les étapes de restauration, compilation, test et publication.
Pour mon étape de test, je l’ai configurée pour exécuter deux projets de test - un projet de tests unitaires et un projet de tests d’intégration. J’ai configuré ma politique d’accès Key Vault pour donner accès à la fois à mon compte et à Azure DevOps. Quand j’exécute mes tests localement avec Visual Studio, comme je suis connecté au même compte qui a accès à Azure Key Vault, je peux exécuter les tests sans erreur.
Mon application est configurée pour accéder au Key Vault avec la configuration suivante :
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>();
Quand j’exécute le pipeline de build, j’utilise une instance Hosted VS2017 pour compiler mon projet. Tout fonctionne sauf les tests d’intégration qui essaient d’accéder au Key Vault et échouent. J’utilise les packages suivants :
-
Microsoft.Azure.Services.AppAuthentication - facilite la récupération de tokens d’accès pour les scénarios d’authentification service à service Azure.
-
Microsoft.Azure.KeyVault - contient les méthodes pour interagir avec Key Vault.
-
Microsoft.Extensions.Configuration.AzureKeyVault - contient les extensions IConfiguration pour Azure Key Vault
J’ai suivi ce tutoriel https://learn.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault pour configurer le Key Vault et l’intégrer dans mon application.
J’essaie simplement de faire fonctionner ma build en m’assurant que les tests unitaires et les tests d’intégration passent. Je ne le déploie pas encore sur un App Service. Les tests unitaires s’exécutent sans problème car je simule les différents services. Mon test d’intégration échoue avec les messages d’erreur ci-dessous. Comment puis-je donner à mon test l’accès au Key Vault ? Dois-je ajouter des politiques d’accès spéciales à mon Key Vault pour la build hébergée VS2017 ? Je ne suis pas sûr de ce qu’il faut faire car je ne vois rien qui ressorte.
Voici la trace de la pile pour l’erreur :
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)
Mise à jour
Je n’ai trouvé qu’un seul article lié à ce problème : https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview. Mais l’article concerne le déploiement d’une application dans un slot Azure. Moi, j’essaie simplement de compiler mon application dans un pipeline de build.
J’essaie toujours de résoudre ce problème et je ne suis pas sûr de la meilleure façon de fournir l’accès requis.
Mise à jour 2
Je n’ai toujours pas trouvé de solution. Je suis perdu quant à la façon de faire exécuter mes tests par le pipeline sans problème. J’ai vu que dans le pipeline de release, vous avez la possibilité d’exécuter des tests aussi. Mais ceux-ci semblent prendre des fichiers .dll et mon fichier de dépôt du pipeline de build ne contient que l’application web (je ne vois aucun des projets de test dans le fichier de dépôt publié). Je ne suis pas sûr que ce soit même une possibilité.
Mise à jour 3
J’ai réussi à le faire fonctionner en utilisant la dernière option fournie ici : https://learn.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support
J’ai essayé les autres méthodes utilisant un certificat mais à chaque fois que {CurrentUser} est fourni dans une chaîne de connexion, le pipeline de build échoue. Cela fonctionne sur ma machine locale mais pas dans le pipeline de build.
Pour le faire fonctionner, j’ai dû faire trois choses :
-
Se connecter à Azure. Configurer une nouvelle inscription d’application dans Azure AD
-
Dans votre nouvelle inscription d’application AD, créer un nouveau secret client
Donner à votre nouvelle application AD l’accès à votre Key Vault. Allez dans les politiques d’accès de votre Key Vault et ajoutez l’application que vous avez créée dans votre AD avec un accès en lecture à vos secrets.
Modification de mon appel à AzureServiceTokenProvider() dans mon fichier Program.cs comme suit :
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}")
Notez que votre secret client doit être correctement formaté. Les inscriptions d’applications (aperçu) génèrent une clé secrète aléatoire. Parfois cette clé ne fonctionne pas dans la chaîne de connexion (génère une erreur de format incorrect). Essayez soit de générer votre propre clé dans la version non-aperçu de l’inscription d’application, soit de générer une nouvelle clé et réessayez.
Après cela, j’ai pu exécuter mon test d’intégration dans mon pipeline de build avec succès et créer une release vers mon application web dans Azure. Je ne suis pas satisfait de cette approche car bien qu’elle fonctionne, elle expose une valeur secrète dans le code lui-même. L’identité de service managée n’a pas besoin d’être activée grâce à l’approche ci-dessus. Je trouve que c’est extrêmement mauvais à cet égard.
Il doit y avoir une meilleure solution. Une option est de ne pas exécuter le test d’intégration dans le pipeline de build. Je ne suis pas sûr que ce soit la bonne approche. J’espère toujours que quelqu’un pourra fournir une meilleure approche ou expliquer si mon approche est acceptable.
Source : [Stack Overflow](Tutorial - Use Azure Key Vault with an Azure web app in .NET | Microsoft Learn](Tutorial - Use Azure Key Vault with an Azure web app in .NET | Microsoft Learn)