Table of Contents
Do I run the script (runbook) locally or in Azure Automation?
I develop scripts or runbooks on my local machine and then run them against Microsoft Graph or Azure (REST API) and when the script is complete I upload them in Azure Automation as a runbook.
I need logic in my script to see if it’s running locally or in Azure Automation so I can import the right modules, call the right variables, and more.
When you run a runbook in PowerShell 5.1 in Azure Automation, object $PSPrivateMetadata
is available. This for example contains the JobId.
For PowerShell 7 there is (I don’t think so?) such object. At least I haven’t found it yet.
I use environment variables for this.
There is a whole list of variables in the environment variables ran in Azure Automation. The Azure Automation sandbox Id and the distribution channel where the runbook runs, and more.
[Environment]::GetEnvironmentVariables()
Name Value
---- -----
USERNAME Client
AUTOMATION_ASSET_ENDPOINT http://127.0.0.1:20043/
AUTOMATION_ASSET_KEY SZ142JlZ3K1osD/jiB6seh54YGp3/Y/wnNJxifC8gmA=
COMPUTERNAME CLIENT
AZUREPS_HOST_ENVIRONMENT AzureAutomation/
MSI_SECRET QazgMdBlQ1l6bO31fEV0eO8dhwSoC6MIdlkG0YmEBFo=
MSI_ENDPOINT http://127.0.0.1:20043/oauth2/token
IDENTITY_ENDPOINT http://127.0.0.1:20043/oauth2/token
AUTOMATION_ASSET_ACCOUNTID 01752a2d-1d81-156a-9471-1432ab3aebf0
IDENTITY_HEADER QazgMdBlQ1l6bO34fEV0eO8dhwSoC6MIdlkG0YmEBFo=
POWERSHELL_DISTRIBUTION_CHANN… AzureAutomation
SkipAzInstallationChecks true
AUTOMATION_ASSET_SANDBOX_ID 25c1b720-cae1-40a1-9dde-c6bc77504e11
The logic behind a script running locally or in Azure Automation
I assumed, and this also brings me to the problem for which I am writing the blog, that various environment variables are always available.
For that reason I assumed that when $env:AUTOMATION_ASSET_SANDBOX_ID
has a value, the script is running in Azure sandbox.
I checked that with this ‘simple trick’:
if (([string]::IsNullOrEmpty($env:AUTOMATION_ASSET_SANDBOX_ID))) {
Write-Output 'Running Locally'
}
else {
Write-Output 'Running in Azure Automation sandbox'
}
And yet every so often a runbook job had a failed state that before and after that simply had a completed job state again.
The runbook jobs that failed, which failed on a suspended state with the last verbose logging being the following:
VERBOSE: Populating RepositorySourceLocation property for module Az.
Thats weird? That’s a verbose message for when a module is imported and for the module Az it’s often Connect-AzAccount
that is called after.
So, I checked a few things and made the discovery that the environment variables not always have a value. Which means the runbook running in Azure Automation thinks it’s running locally and since Connect-AzAccount
does not use basic authentication anymore, it will simply open the default browser with the modern authentication login method for Azure & PowerShell.
The runbook would have failed with the error message that you cannot open a interactive action on a sandbox environment.
So, how can we be 100% sure the runbook is running locally or in Azure Automation? Environment variables!
I have come up with a workaround for this how you can distinguish locally and Azure Automation.
We do not run runbooks on hybrid workers and sandboxes, so all scripts that need to run, run in the sandbox environment.
So, I can continue to use the variable $env:AUTOMATION_ASSET_SANDBOX_ID
, but so look carefully at which variable best suits your scenario.
So the situation is as follows:
We check if the variable $env:AUTOMATION_ASSET_SANDBOX_ID
has a value, if so, then the runbook runs in Azure Automation or we check the variable, but it does not return a value, so you still don’t know if the runbook runs in Azure Automation.
The following should be sufficient for this:
if ([string]::IsNullOrEmpty($env:AUTOMATION_ASSET_SANDBOX_ID)) {
Write-Output 'UNKNOWN?'
}
else {
$RunningLocally = $false
}
So, we now need a 2nd check. Azure Automation has a number of cmdlets that are for internal use only. 1 of these is Start-AutomationRunbook
.
This isn’t a cmdlet I have on my local machine, so we could use it nicely.
We can use Get-Command
to see if this cmdlet is available, and if not, throw it to a catch block.
This would look like this:
if ([string]::IsNullOrEmpty($env:AUTOMATION_ASSET_SANDBOX_ID)) {
try {
$null = Get-Command 'Start-AutomationRunbook' -ErrorAction Stop
$RunningLocally = $false
}
catch {
$RunningLocally = $true
}
}
else {
$RunningLocally = $false
}
Now you can be sure that the correct values are taken per environment.
The end result I use
I have written it out in full for you below.
Includes PowerShell 5.
#region Test if the runbook really is not running in Azure Automation
# This is a workaround for Azure Automation 7, the JobId is not always available in the PSPrivateMetadata or the env:AUTOMATION_ASSET_SANDBOX_ID
if (([string]::IsNullOrEmpty($PSPrivateMetadata.JobId.Guid)) -and ([string]::IsNullOrEmpty($env:AUTOMATION_ASSET_SANDBOX_ID))) {
try {
$null = Get-Command 'Start-AutomationRunbook' -ErrorAction Stop
$RunningLocally = $false
}
catch {
$RunningLocally = $true
}
}
else {
$RunningLocally = $false
}
#endregion Test if the runbook really is not running in Azure Automation
#region Azure Automation
if ($RunningLocally -eq $false) {
Connect-AzAccount -Identity
}
#endregion Azure Automation
#region Local
else {
Connect-AzAccount
}
#endregion Local
Can it be more readable, manageable, optimized? I’m sure, but this is my way.