azure-pipelinesnunit.net-5azure-keyvaultazure-secrets

Use Azure Key Vault Secrets for Unit Tests in Azure Pipelines


I have a solution in .NET 5 with some Unit Tests in NUnit that required some secrets to work correctly. Testing in Visual Studio using local user secrets works fine but now I want to try to integrate using Azure Key Vault and run the Unit Tests inside a Pipeline.

I added the Task AzureKeyVault@2 and according to the log the secrets are downloaded just fine but in the next steps, the Unit Tests fail because it doesn't find some keys when try to access from IConfiguration.

This is my azure-pipelines.yml (only the relevant parts).

- job: Init
  displayName: Unit Testing
  variables:
    solutionToBuild: 'Solution.sln'
    unitTestsProject: 'UnitTests\UnitTests.csproj'
    buildConfiguration: 'Release'
  steps:
  - task: DotNetCoreCLI@2
    displayName: 'Restore Packages'
    inputs:
      command: 'restore'
      projects: '$(solutionToBuild)'
      verbosityRestore: minimal
      feedsToUse: 'select'

  - task: DotNetCoreCLI@2
    displayName: 'Build Solution'
    inputs:
      command: 'build'
      projects: '$(solutionToBuild)'
      arguments: '-c $(buildConfiguration)'

  - task: AzureKeyVault@2
    displayName: 'Configure Key Vault'
    inputs:
      azureSubscription: '**-etc-**'
      KeyVaultName: '**key-vault-name**'

  - task: DotNetCoreCLI@2
    displayName: 'Run Unit Tests'
    inputs:
      command: 'test'
      projects: '$(unitTestsProject)'
      arguments: '-c $(buildConfiguration) --collect:"XPlat Code Coverage"'
      publishTestResults: true

I created this basic tests that are currently failing in the Pipeline. I'm using the Host.CreateDefaultBuilder() method to initialize all the unit tests.

[Test]
[TestCase("secret-key-1")]
[TestCase("secret-key-1")]
public void TestSecrets(string key)
{
    Assert.NotNull(_config[key]);
    Assert.IsNotEmpty(_config[key]);
}

Does the AzureKeyVault@2 pass the secrets keys and values for the DotNetCoreCLI@2 task or do I need an additional step?. I read that you can play with the FileTransform@1 step using an empty appsettings.json but I couldn't make it work. Is there a correct method to achieve this?


Solution

  • I finally made it work. I decided to go to the route of the appsettings.json. with File Transformation Task. I will answer myself just in case anyone else find this useful.

    You need to have a appsettings.json in your Unit Test project, and add all the required keys you'll need in your Unit Tests, but without values, only "", e.g.

    {
      "secret-key-1": "",
      "secret-key-2": "",
      "secret-key-3": "",
    }
    

    If you want to test locally you can use the local user-secrets option in Visual Studio and add all the keys like in your appsettings.json file but with actual values (you'll be the only one to view the values anyways)

    Manage user secrets

    {
      "secret-key-1": "some-secret-value",
      "secret-key-2": "some-secret-value",
      "secret-key-3": "some-secret-value",
    }
    

    Then, in your SetUp method you can initialize the IConfiguration class as follow. (You can easily transform this into a static method if you have multiple Unit Tests classes)

    [TestFixture]
    public class MyUnitTest
    
       private IConfiguration Config;
    
       [SetUp]
       public void Setup()
       {
           Config = new ConfigurationBuilder()
                    .SetBasePath(AppContext.BaseDirectory)
                    .AddJsonFile("appsettings.json", optional: true)
                    .AddUserSecrets(Assembly.GetExecutingAssembly())
                    .Build();
       }
    
       [Test]
       [TestCase("secret-key-1")
       [TestCase("secret-key-2")
       public void TestKeys(string key)
       {
          Assert.NotNull(config[key]);
          Assert.IsNotEmpty(config[key]);
       }
    }
    

    Finally, in the azure-pipelines.yml you add the FileTransform@1 Task after the AzureKeyVault@2.

      - task: AzureKeyVault@2
        displayName: 'Configure Key Vault'
        inputs:
          azureSubscription: '**-etc-**'
          KeyVaultName: '**key-vault-name**'
    
      - task: FileTransform@1
        displayName: 'Set Unit Tests Settings'
        inputs:
          folderPath: '$(projectsDirectory)'
          fileType: 'json'
          targetFiles: '**/appsettings.json'
    
      - task: DotNetCoreCLI@2
        displayName: 'Run Unit Tests'
        inputs:
          command: 'test'
          projects: '$(unitTestsProject)'
          arguments: '-c $(buildConfiguration) --collect:"XPlat Code Coverage"'
          publishTestResults: true
    

    And that's it, after that in the pipeline log you'll see something like this in the FileTransform@1 task

    Applying JSON variable substitution for **/appsettings.json
    Applying JSON variable substitution for D:\a\1\s\UnitTests\appsettings.json
    Substituting value on key secret-key-1 with (string) value: ***
    Substituting value on key secret-key-2 with (string) value: ***
    Substituting value on key secret-key-3 with (string) value: ***