Using Azure KeyVault to Secure Graph API Automation Scripts

I previously published a post on how we can use Certificates to securely connect to the Microsoft Graph API. This is a great way to secure automation with Graph. Taking this idea and going a step further, by using Azure KeyVault to store our certificate, it can be used with Azure Functions or Azure Automation to automate Graph tasks. With Azure Automation for example, a certificate can be uploaded directly to the Automation Account but when we want to work at scale, uploading to every Automation Account and maintaining them can create some unneeded maintenance effort.

In this example we can use the Managed Service Identity (or Run As Account) of the Automation Account to access an Azure KeyVault and retrieve a certificate that has been prepopulated there securely. Before getting started, for generating a certificate and uploading to the registered app, check out the post linked above.

Configure an Automation Account

The first step is to create the first Automation Account. This can be created in the Azure Portal, make sure to enable the option to “Create Azure Run As Account”. This identity will be used to access KeyVault. Check out Figure 1 for an example from an upcoming post where I will be using this technique.

Figure 1: Creating an Automation Account with a Run As Account

Create an Azure KeyVault

Next, create a new Azure KeyVault and upload the authentication certificate as shown in Figure 2.

Figure 2: Upload the certificate to Azure KeyVault

Once the certificate is in place, open the “Access Policies” blade and grant “Get” permissions for Secrets and Certificates to the Automation Account Identity created earlier.

Figure 3: Grant “Get” Permissions to the Run As Account

Optional – Add the Client and Tenant ID as Secrets to the KeyVault

As a best practice, I’d recommend avoiding putting any static references inside your script. Other than the Certificate which we are storing securely, the Tenant ID and Client ID values can also be held in KeyVault to keep them secure. These values can be uploaded as Secrets as shown in Figure 4.

Figure 4: The Client ID and Tenant ID can be uploaded to KayVault also

These values can then be called from the Automation Account so they don’t need to be held within the script.


Import Modules to the Automation Account

For the authentication with Graph, the MSAL.PS PowerShell module saves a lot of code so I really recommend importing that module and using it in your script. Import the module from the Automation Account Modules Gallery as in Figure 5.

Figure 5: Import the MSAL.PS PowerShell Module

To work with KeyVault, the AZ.Accounts and AZ.KeyVault modules are also required.


Retrieve the Certificate and Acquire an Access Token in the Script

You can then use the following code to retrieve and import the Certificate within Azure Automation. Make surre to replace the name of your KeyVault and Certificate within the code on lines 5 and 6. Thanks to Thomas Rayner for his blog on retrieving Certificates from KeyVault. I strongly recommend checking it out at the link below.

How To Retrieve A Certificate From Azure Key Vault Via PowerShell – Thomas Rayner – Writing code & automating IT

import-module AZ.KeyVault
import-module AZ.Accounts
import-module MSAL.PS
$connectionName = 'AzureRunAsConnection'
$KeyVaultName = <Name of your KeyVault>
$CertificateName = <Name of your Certificate>


try
{
    # Get the connection "AzureRunAsConnection "
    $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName

    "Logging in to Azure..."
    Connect-AzAccount -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint -ApplicationId $servicePrincipalConnection.ApplicationId -Tenant $servicePrincipalConnection.TenantId  -ServicePrincipal
}
catch {
    if (!$servicePrincipalConnection)
    {
        $ErrorMessage = "Connection $connectionName not found."
        throw $ErrorMessage
    } else{
        Write-Error -Message $_.Exception
        throw $_.Exception
    }
}

##Retrieve Certificate and Secret
try
{
$CertificateSecret = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $CertificateName -AsPlainText
$CertificateBytes = [System.Convert]::FromBase64String($CertificateSecret)
$CertificateObject = New-Object System.Security.Cryptography.x509Certificates.x509Certificate2Collection
$CertificateObject.Import($CertificateBytes,$null,[System.Security.Cryptography.x509Certificates.x509KeyStorageFlags]::Exportable)
$ProtectedCertificateBytes = $CertificateObject.Export([System.Security.Cryptography.x509Certificates.x509ContentType]::Pkcs12,"")
[System.IO.File]::WriteAllBytes("c:\temp\Certificate.pfx",$ProtectedCertificateBytes)
Import-PfxCertificate -FilePath "c:\temp\Certificate.pfx" Cert:\CurrentUser\My
}
catch{
        $ErrorMessage = "Certificate could not be retrieved"
        throw $ErrorMessage
}

##Retrieve Client and Tenant IDs
try
{
$ClientID = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name "ClientID" -AsPlainText
$TenantID = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name "TenantID" -AsPlainText
}
catch{
        $ErrorMessage = "Client/Tenant ID could not be retrieved"
        throw $ErrorMessage
}
$CertificateFile = Get-Item "Cert:\CurrentUser\My\$($CertificateObject.thumbprint)"
write-output $Certificatefile
$Token = Get-MSALToken -ClientID $ClientID -TenantID $TenantID -ClientCertificate $CertificateFile
Get-Item "Cert:\CurrentUser\My\$($CertificateObject.thumbprint)" | remove-item


The token is then successfully retrieved, you can use this for Graph calls or to double check, use Write-Output to output the value of the Token variable like the sample output in Figure 6.

Figure 6: Write-Output of the Token object shows the access token is in place

Summary

While we can hold certificates directly within an Automation Account, for large environments where we want to minimize the effort and overhead on the automation, a single KeyVault with access granted to the Automation Accounts provides some great flexibility. We can also use this KeyVault with other technologies like Azure Functions to keep everything secure and in one place.

One thought on “Using Azure KeyVault to Secure Graph API Automation Scripts

  1. Pingback: Why Using App Secrets in Production is a Bad Idea

Leave a comment