Azure Automation PS Version Mismatch Woes
Description
When using Azure Automation, it is common for powershell to work on your computer but behave different when ran from the Azure Automation account in the portal. Windows 10 uses runtime 5.1
but the Az module version for Automation Accounts is only 8.0.0 (you can see the on the Modules blade of an Automation Account in the portal). This causes many pain points so be aware. Also reference this doc from Microsoft when you find yourself having issues coding runbooks.
Anyhow, I would like to discuss an issue I had around 2023-08 where I was trying to get runbooks to shut down/start up AKS clusters…
For the scripts like 12x5-start-aks-appg.ps1
(link coming soon), I thought I could update them because they were using code like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"
try
{
"Logging in to Azure..."
Connect-AzAccount -Identity
}
catch {
Write-Error -Message $_.Exception
throw $_.Exception
}
get-AzAksCluster | ForEach-Object {
Get-AzResource -ResourceId $_.id } | Get-AzResource | where { $_.Properties.Powerstate.code -eq "Running" -and $_.Properties.ProvisioningState -eq "Succeeded" -and $_.Tags.Schedule -eq "adhoc"} |
ForEach-Object {
Stop-AzAksCluster -Name $_.Name -ResourceGroupName $_.ResourceGroupName -AsJob
Get-AzApplicationGateway -ResourceGroupName $_.ResourceGroupName | Stop-AzApplicationGateway -AsJob}
But it turned out to me a lot more difficult than I had planned! Here is what happened:
Steps
-
So first, I tried a more modern approach like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# Step 1: Loop through all Clusters, filter based on tags, and stop them $Clusters = Get-AzAksCluster Write-Output "Processing $($Clusters.count) clusters..." $filteredClusters = $Clusters | Where-Object { $_.Powerstate.code -eq "Running" -and $_.ProvisioningState -eq "Succeeded" -and $_.Tags.Schedule -eq "adhoc" } if ( $null -eq $filteredClusters ) { Write-Output "No Clusters meet criteria to continue..." Write-Output "Completed" } Else { Foreach ($Cluster in $filteredClusters ) { Write-Output "Stopping cluster $($Cluster.Name) ..." Stop-AzAksCluster -Name $($Cluster.Name) -ResourceGroupName $($Cluster.ResourceGroupName) -AsJob Write-Output "Stopping cluster $($Cluster.Name) ...Completed" Write-Output "Stopping all App Gateways in $($Cluster.ResourceGroupName) ..." Get-AzApplicationGateway -ResourceGroupName $($Cluster.ResourceGroupName) | Stop-AzApplicationGateway -AsJob Write-Output "Stopping all App Gateways in $($Cluster.ResourceGroupName) ...Completed" } }
- Clean and simple. Works on my machine when testing. But when it runs in the portal you get:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Logging in to Azure... Mode : Process ContextDirectory : ContextFile : CacheDirectory : CacheFile : Settings : {} Logging in to Azure...Completed Processing 1 clusters... No Clusters meet criteria to continue... Completed
-
I eventually found it was because it was not able to get the power state:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# Step 1: Loop through all Clusters, filter based on tags, and stop them $Clusters = Get-AzAksCluster Write-Output "Processing $($Clusters.count) clusters..." Write-output "Clusters:" Write-Output $Clusters $filteredClusters = $Clusters | Where-Object { $_.Powerstate.code -eq "Running" -and $_.ProvisioningState -eq "Succeeded" -and $_.Tags.Schedule -eq "adhoc" } Write-Output $filteredClusters Write-OUtput "Powerstate:" Write-Output $clusters[0].Powerstate.code
- Produces
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
Logging in to Azure... Mode : Process ContextDirectory : ContextFile : CacheDirectory : CacheFile : Settings : {} Logging in to Azure...Completed Processing 1 clusters... Clusters: ProvisioningState : Succeeded MaxAgentPools : 100 KubernetesVersion : 1.25.6 DnsPrefix : aa-nonprd-scus-spk-aks Fqdn : aa-nonprd-scus-spk-aks-nwzpzdge.hcp.westus.azmk8s.io PrivateFQDN : aa-nonprd-scus-spk-aks-nwzpzdge.hcp.westus.azmk8s.io AgentPoolProfiles : {aksctrlplane, user1} WindowsProfile : Microsoft.Azure.Commands.Aks.Models.PSManagedClusterWindowsProfile AddonProfiles : {[azureKeyvaultSecretsProvider, Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAddonProfile], [azurepolicy, Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAddonProfile], [ingressApplicationGateway, Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAddonProfile], [omsagent, Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAddonProfile]} NodeResourceGroup : MC_aa-nonprd-scus-spk-aks-rg-7gmwo_aa-my-akso_westus EnableRBAC : True EnablePodSecurityPolicy : NetworkProfile : Microsoft.Azure.Commands.Aks.Models.PSContainerServiceNetworkProfile AadProfile : Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAadProfile ApiServerAccessProfile : Microsoft.Azure.Commands.Aks.Models.PSManagedClusterAPIServerAccessProfile IdentityProfile : {[kubeletidentity, Microsoft.Azure.Commands.Aks.Models.PSManagedClusterPropertiesIdentityProfile]} Identity : Microsoft.Azure.Commands.Aks.Models.PSManagedClusterIdentity LinuxProfile : ServicePrincipalProfile : Microsoft.Azure.Commands.Aks.Models.PSContainerServiceServicePrincipalProfile ResourceGroupName : aa-nonprd-scus-spk-aks-rg-7gmwo Id : /subscriptions/2cf2f1ee-99712e83048/resourcegroups/aa-nonprd-scus-spk-aks-rg-7gm wo/providers/Microsoft.ContainerService/managedClusters/aa-my-akso Name : aa-my-akso Type : Microsoft.ContainerService/ManagedClusters Location : westus Tags : {[AppEnv, nonprd], [Apppoc, me@domain.com], [CC, 113], [Creation_Time, 2023-03-16_07:52:27 PST]...} Powerstate:
-
So next, I thought, maybe you have to pipe to
Get-AzResource
by passing the ResourceId and then use that to get the power state:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
$filteredClusters = @{} Foreach ($Cluster in $Clusters ) { $currentCluster = Get-AzResource -ResourceId $($Cluster.Id) $powerstate = $($currentCluster.Properties.Powerstate.Code) $provisioning = $($Cluster.ProvisioningState) $sched = $($Cluster.Tags.Schedule) Write-Output "Processing $($Cluster.Name) ..." Write-Output "Resource ID: $($Cluster.Id) " Write-Output "Power State: $($Cluster.Powerstate.Code) " Write-Output "Provisioning State: $($Cluster.ProvisioningState) " Write-Output "Schedule Tag: $($Cluster.Tags.Schedule)" If ( $powerstate -eq "Running" -and $provisioning -eq "Succeeded" -and $sched -eq "adhoc") { Write-Output "Adding cluster to filtered clusters list: $($Cluster.Name), $($Cluster.ResourceGroupName) " $filteredClusters.Add( $($Cluster.Name), $($Cluster.ResourceGroupName) ) } }
- The above was trimmed a little to show the line
$currentCluster = Get-AzResource -ResourceId $($Cluster.Id)
. This produced an error:
1
Get-AzResource : No registered resource provider found for location 'westus' and API version '2023-08-02-preview' for type 'managedClusters'. The supported api-versions are '2017-08-31, 2018-03-31, 2018-08-01-preview, 2019-02-01, 2019-04-01, 2019-06-01, 2019-08-01, 2019-10-01, 2019-11-01, 2020-01-01, 2020-02-01, 2020-03-01, 2020-04-01, 2020-06-01, 2020-07-01, 2020-09-01, 2020-11-01, 2020-12-01, 2021-02-01, 2021-03-01, 2021-05-01, 2021-07-01, 2021-08-01, 2021-09-01, 2021-10-01, 2021-11-01-preview, 2022-01-01, 2022-01-02-preview, 2022-02-01, 2022-02-02-preview, 2022-03-01, 2022-03-02-preview, 2022-04-01, 2022-04-02-preview, 2022-05-02-preview, 2022-06-01, 2022-06-02-preview, 2022-07-01, 2022-07-02-preview, 2022-08-01, 2022-08-02-preview, 2022-08-03-preview, 2022-09-01, 2022-09-02-preview, 2022-10-02-preview, 2022-11-01, 2022-11-02-preview, 2023-01-01, 2023-01-02-preview, 2023-02-01, 2023-02-02-preview, 2023-03-01, 2023-03-02-preview, 2023-04-01, 2023-04-02-preview, 2023-05-01, 2023-05-02-preview, 2023-06-01, 2023-06-02-preview, 2023-07-01, 2023-07-02-preview'. The supported locations are 'australiacentral, australiacentral2, australiaeast, australiasoutheast, brazilsouth, brazilsoutheast, canadacentral, canadaeast, centralindia, centralus, eastasia, eastus, eastus2, francecentral, francesouth, germanynorth, germanywestcentral, japaneast, japanwest, jioindiacentral, jioindiawest, koreacentral, koreasouth, northcentralus, northeurope, norwayeast, norwaywest, polandcentral, qatarcentral, southafricanorth, southafricawest, southcentralus, southindia, southeastasia, swedencentral, switzerlandnorth, switzerlandwest, uaecentral, uaenorth, uksouth, ukwest, westcentralus, westeurope, westus, westus2, westus3'. StatusCode: 400 ReasonPhrase: Bad Request OperationID : a19e3844-c665-4246-9a2d-ba9890f9087b At line:38 char:22 + $currentCluster = Get-AzResource -ResourceId $($Cluster.Id) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : CloseError: (:) [Get-AzResource], ResourceManagerCloudException + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet
- The above was trimmed a little to show the line
-
This is frustrating! Why can’t the Get-Azresource cmdlet work with ResourceID, it is the main purpose of the cmdlet! So then I did some digging to see why the Get-AzAKSCluster cmdlet can’t get the powerstate on its own. Well the module loaded in the Automation account is 4.1.0 per the Modules blade of the Automation Account.
- So I then went to its source and downloaded the nupkg and extracted. Unfortuntely everything is in dll’s so I can’t view.
- I then wanted to look online to see what version the Automation Account returns when you run the Get-AzAks resource.
- So what you do is scroll down on the cmdlet page and go to the outputs section and you will see PSKubernetesCluster
- Next, you change the drop down on the left to
Azure - Powershell Commands => 5.9.0
. This seems to coorelate to Powershell 5 which matches the runtime of 5.1. - Now you can see where we get
ProvisioningState
andTags
but no power state. - Mystery solved. So how do we get the Power state?
-
So I went back to the Get-AzResource and found that I got the same thing on my machine when I passed in a
ResourceId
.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Foreach ($Cluster in $Clusters ) { $currentCluster = Get-AzResource -ResourceId $($Cluster.Id) $powerstate = $($currentCluster.Powerstate.Code) $provisioning = $($currentCluster.ProvisioningState) $sched = $($currentCluster.Tags.Schedule) Write-Output "Processing $($Cluster.Name) ..." Write-Output "Resource ID: $currentCluster " Write-Output "Power State: $powerstate " Write-Output "Provisioning State: $provisioning " Write-Output "Schedule Tag: $sched" If ( $powerstate -eq "Running" -and $provisioning -eq "Succeeded" -and $sched -eq "adhoc") { Write-Output "Adding cluster to filtered clusters list: $($Cluster.Name), $($Cluster.ResourceGroupName) " $filteredClusters.Add( $($Cluster.Name), $($Cluster.ResourceGroupName) ) } } Processing aa-my-akso ... Resource ID: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource Power State: Provisioning State: Schedule Tag: adhoc
- Hmm, why am I getting the class
Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource
instead of the actual resource id? - So then I decided to try
Get-AzResource -Name $($Cluster.Name) -ResourceGroupName $($Cluster.ResourceGroupName)
and that seemed to work. I got back an object. But when I checked its properties, it was null.
- Hmm, why am I getting the class
-
So then I googled “No properties returned for get-azresource” and found this post.
- The answer was to pass an
-ExpandProperties
switch to the cmdlet. Yeah right I thought, this Automation Account cmdlet is probably running an ancient version that doesn’t support that switch but ran it anyways. - To my amazement, it worked! So there you have it, a single little switch allows us to the get the Powerstate which we can then plug in to our script and now we can shut down/start up AKS clusters.
- The answer was to pass an
Comments