Table of Contents
Use Secret Management to Store and Manage Secrets Needed by Azure Automation Runbooks
Storing hard-coded account credentials in PowerShell scripts is a big security no-no. Previously, I’ve discussed using Azure Key Vault to store passwords and other credentials that might be needed by PowerShell scripts or Azure Automation runbooks. Another method that I’ve used with runbooks is to store the credentials as an automation account resource, most recently when using the Connect-IPPSSession cmdlet to update a Microsoft 365 retention policy. Unhappily, the security and compliance cmdlets don’t currently support managed identities.
Although storing credential objects is the easiest way to make credentials available to an automation account, I would prefer to avoid duplication by using Azure Key Vault everywhere. This decision this brought me to the Secret Management PowerShell module. Essentially, the module supports an easy-to-use connection to Azure Key Vault (and other repositories) to access secrets stored in the vault, like usernames and passwords.
Installing the Secret Management Module
First, install the Secret Management module from the PowerShell gallery.
Install-Module Microsoft.PowerShell.SecretManagement -Repository PSGallery -Force -Scope AllUsers
Remember to make the module available in any Azure Automation runtime environments where runbooks will use the module to fetch secrets. The module only supports PowerShell core, so I tested runbooks with a custom PowerShell 7.4 runtime environment. I also used V3.9 of the Exchange Online management module and V6.3 of the Az.Accounts module.
Registering Azure Key Vault with Secret Management
Before you can fetch any secrets from a vault, you must register the vault for the current session. The Secret Management module supports access to Azure Key Vault through one of its default extensions, but first a connection is needed an Azure account that’s linked to a subscription. Interactively, you’d do something like this:
Connect-AzAccount -Subscription 25429342-a1a5-4427-9e2d-551840f2ad25
In an Azure automation runbook, you can use a managed identity:
Connect-AzAccount -Identity
In this case, the secrets I need to use are stored in an Azure Key Vault called “Office365ITPros.” To access Azure Key Vault, the signed-in account must have permission to the target vault granted via a legacy access policy or an appropriate Azure RBAC role. This requirement also applies to the automation account used to execute runbooks, where permission is granted to the automation account’s service principal.
With the necessary access, I can use the Register-SecretVault cmdlet to connect to Azure Key Vault for the current session as follows. The call to the Get-SecretVault cmdlet is to confirm that the registration worked.
$parameters = @{
Name = 'Azure'
ModuleName = 'Az.KeyVault'
VaultParameters = @{
AZKVaultName = ‘Office365ITPros'
SubscriptionId = (Get-AzContext).Subscription.Id
}
DefaultVault = $true
}
Register-SecretVault @parameters
Get-SecretVault
Name ModuleName IsDefaultVault
---- ---------- --------------
Azure Az.KeyVault True
Fetching and Using Secrets in an Azure Automation Runbook
Once a vault is properly registered, the Get-Secret cmdlet can fetch secrets from the target vault. We need to combine the secrets holding the username and password for an Exchange Online administrator account into a credentials object. The object can then be used with the Connect-ExchangeOnline and Connect-IPPSSession cmdlets to connect to Exchange Online and the compliance endpoint before running the cmdlets necessary to complete whatever task is required.
This example shows how to list the sensitivity labels defined in the tenant after making all the necessary connections and registrations. The full code is listed below. Figure 1 shows the output from the Azure automation test pane.
Connect-AzAccount -Identity
$parameters = @{
Name = 'Azure'
ModuleName = 'Az.KeyVault'
VaultParameters = @{
AZKVaultName = 'Office365ITPros'
SubscriptionId = (Get-AzContext).Subscription.Id
}
DefaultVault = $true
}
Register-SecretVault @parameters
Get-SecretVault
$UserName = Get-Secret -Name ExoAccountName -AsPlainText -Vault Azure
$Password = Get-Secret -Name ExoAccountPassword -Vault Azure
$Credentials = New-Object 'Management.Automation.PsCredential' $UserName, $Password
Connect-ExchangeOnline -Credential $Credentials -DisableWAM
Connect-IPPSSession -Credential $Credentials
Get-Label | Format-Table ImmutableId, DisplayName

Secret Management is an Alternative to Credential Resources
There’s no doubt that storing credential objects as Azure Automation resources is the easiest way to manage credentials used with runbooks. However, the credential objects are associated with individual automation accounts and not shared elsewhere. Putting credentials in Azure Key Vault and accessing those credentials using the Secret Management module isn’t much harder, and those credentials are available to any user or service principal that’s allowed access to the key vault. You pay your money and make your choice…
Need help to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.
@Tony: Up to line 18 of your script everything worked. Yet, connect-exchangeonline throws this error: “OperationStopped: Method not found: ‘Microsoft.Identity.Client.PublicClientApplicationBuilder Microsoft.Identity.Client.Broker.BrokerExtension.WithBroker(Microsoft.Identity.Client.PublicClientApplicationBuilder, Microsoft.Identity.Client.BrokerOptions)’.”
The exact same error is described here: https://github.com/microsoftgraph/msgraph-sdk-powershell/issues/3331
“When calling Connect-ExchangeOnline after you called Connect-AzAccount you get the error …”
How were you able to circumvent it?
What version of the modules have you loaded into the automation account? I used V3.9 for EXO and V5.3 for Az.Accounts. This is the exact code. You can see that Connect-AzAccount loads before Exchange Online.
Connect-AzAccount -Identity
$parameters = @{
Name = ‘Azure’
ModuleName = ‘Az.KeyVault’
VaultParameters = @{
AZKVaultName = ‘Office365ITPros’
SubscriptionId = (Get-AzContext).Subscription.Id
}
DefaultVault = $true
}
Register-SecretVault @parameters
Get-SecretVault
$UserName = Get-Secret -Name ExoAccountName -AsPlainText -Vault Azure
$Password = Get-Secret -Name ExoAccountPassword -Vault Azure
$Credentials = New-Object ‘Management.Automation.PsCredential’ $UserName, $Password
Connect-ExchangeOnline -Credential $Credentials -DisableWAM
Connect-IPPSSession -Credential $Credentials
Get-Label | Format-Table ImmutableId, DisplayName
Moved from 3.8.0 to 3.9 ExchangeOnlineManagement and it worked. Thanks, Tony.
Glad that we found a solution…
@Tony: I don’t quite understand the use case for a vault. Can you help me?
First, we logon with credentials A of an Azure account that’s linked to a subscription.
With this account we get the credentials B to logon to ExchangeOnline.
Why is that better/more secure then simply using a single set of credentials to logon. You have two credentials, I have one. What is more secure?
The idea of using a key vault is that it’s a secure place to store confidential information. Credentials are just one kind of information.
The value of using a key vault is that it’s a common location for apps to go to fetch the confidential information. I’m using it here for Azure Automation, but the vault data could be used by web apps or PowerShell scripts.
Most of the time, I used managed identities with Exchange Online. It’s only because the security and compliance cmdlets (Connect-IPPSSession) don’t support managed identities (yet) that I go anywhere near username/password credentials.
Like most stuff I write about, I do so to make people think. Some of the stuff works for some of my readers, some of it doesn’t.