Store your automation credentials in Azure KeyVault

tswiftma

tswiftma

Posted on September 24, 2021

Store your automation credentials in Azure KeyVault

Note: updated on 4/30/24 with a more complete solution

There's a common problem storing login credentials for automation that runs in Production or near-Production (aka Stage), where do you store them? You absolutely shouldn't check them into source code! I guess that you could encrypt them somehow but a better solution is to store them in Azure KeyVault and pull them out when you need them. So how can you do this?

Let's consider the two locations where automation will run:

1) In an IDE like Visual Studio (aka local) when developing tests.
2) In a pipeline like Azure DevOps to run tests on a schedule.

They both represent different authentication challenges. First let's tackle the local Visual Studio scenario.

In Azure create a new KeyVault for your test credentials and create a couple of secrets that represent a username and password. The secrets could also represent a ClientId and ClientSecret if you're creating tokens.

image

To access the KeyVault we will use the DefaultAzureCredential class. This class provides an authentication workflow and tries different authentication methods automatically. In our case the class will use VisualStudioCredential to access the KeyVault from our local machine. To enable this option you need to sign in to Visual Studio with your Azure account at Tools/Options/Azure Service Authentication/Account Selection:

Image description

To get the KeyVault values you need to add the following packages to your code to support using DefaultAzureCredential:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

Here's some sample code to get the KeyVault data. We check if our tests are running locally using a bool to get the VS edition. Then if we are running local we create a new SecretClient using DefaultAzureCredential and we query the KeyVault for the secrets we're looking for.



// Check if automation is running in VS or from cmd line (aka pipeline)
var isRunningInVS = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("VisualStudioEdition"))

// Get KeyVault Uri and credential for local run
string kvUri = "https://myKeyVault.vault.azure.net";
var clientKV = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());

var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", _GrantType));                

// Get API Credentials
if (authMode == AuthMode.Candidates)
{
    if (isRunningInVS)
    {
        clientKV = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
        _ClientId = (await clientKV.GetSecretAsync(ClientIdName)).Value.Value;
        _ClientSecret = (await clientKV.GetSecretAsync(ClientSecretName)).Value.Value;
    }

    values.Add(new KeyValuePair<string, string>("client_id", _ClientId));
    values.Add(new KeyValuePair<string, string>("client_secret", _ClientSecret));
    values.Add(new KeyValuePair<string, string>("audience", _AudiencePayor));
}


Enter fullscreen mode Exit fullscreen mode

You now can securely pull data from KeyVault when running tests locally.

The second challenge is how does automation pull credential data when the same tests are running in a pipeline? In the case of using Azure DevOps you can store your credentials securely in Azure Library Variable Groups.

Create a Library Variable Group specific to your environment. The below example shows one for Development. You can then add your credential values and mask out any critical values such as ClientSecret:

Image description

Note that Library Variable Groups do have an option to directly link to your KeyVault but at this time I didn't have permissions to get it to work correctly.

Next is to access the Variable Group values in your pipeline yaml. Under the variables section, specify the group value as the name of your ADO Library Variable Group. When running the pipeline it will have access to the data in the variable group.

Image description

Note in the above example $(AuthorizationClientId) is the value that is pulled from the variable group and assigned to the yaml variable named Authorization.ClientId". You now have secure access to your credentials in your pipeline.

There's a few other pipeline steps that I won't go over in detail but here's a few tips:

Tip1: All of your variables for your automation should be stored in an appSettings.json file that is typically read during startup using the ConfigurationBuilder class. Remember not to store credentials in these files!

Tip2: At pipeline runtime you can substitute variables like we just created from the Library Variable Groups above using the FileTransform@1 task.

Image description

Good luck and let me know if you have any questions!

💖 💪 🙅 🚩
tswiftma
tswiftma

Posted on September 24, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related