1

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?

NioZero
  • 87
  • 2
  • 11

2 Answers2

1

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: ***
NioZero
  • 87
  • 2
  • 11
0

Use the below Keyvault task in the pipeline to fetch the secrets in your keyvault :

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

Now, in the DotNetCoreCLI@2 task, add environment variables to define the keyvault secrets so that you can use these environment variables inside your project :

- task: DotNetCoreCLI@2 
  displayName: ''
  env:
    MySecret: $(key-vault-secret-name)
  inputs:
    command: ''
    projects: 'project-name'

Inside your project, you can define the keyvault secret as follows :

var mySecret = Environment.GetEnvironmentVariable("MySecret");

Reference : Azure DevOps YAML pipeline : Use Azure KeyVault secret as Environment Variable - DEV Community

RamaraoAdapa
  • 2,837
  • 2
  • 5
  • 11
  • 1
    Hi @RamaraoAdapa-MT. I have a question. Do I need to add env for each Secret Key?. We have a lot of secrets (more than 20), and frequently adding more and I don't know if will be feasible to edit the yml file each time we update or add more secrets to the vault. It's the only way to achieve this? – NioZero Oct 26 '21 at 12:54
  • You can use access the keyvault secrets directly from your .NET application. Please refer this : https://www.codeproject.com/Tips/1430794/Using-Csharp-NET-to-Read-and-Write-from-Azure-Key – RamaraoAdapa Oct 27 '21 at 03:28
  • I know, and in the app is working, but in the unit tests required a different approach. My other answer at the end solved my problem. – NioZero Oct 27 '21 at 16:15
  • Glad to hear that your issue is resolved – RamaraoAdapa Oct 27 '21 at 16:30