If you work in Azure then there’s a good chance that you will have multiple subscriptions to manage, whether this is for various companies, various products or production & test environments.
If you do alot of interactive work in the shell for these subscriptions you’ll quickly find out how annoying it is to have to find the subscription names so that you can change between your subscriptions.
Lots of Subscriptions -eq Lots of waiting
Running Get-AzSubscription
takes a while when you have lots of subscriptions
(Get-AzSubscription).count
32
Measure-Command -Expression { Get-AzSubscription } |
Select-Object Milliseconds, seconds
Milliseconds Seconds
------------ -------
408 4
Now 4 seconds doesn’t seem like a lot of time to wait but if your subscription names aren’t easy to type you tend to type Get-AzSubscription
every time you need to switch between contexts/subscriptions.
This got me thinking about how to make this quicker and a nicer experience.
PowerShell Profile
Option 1 - Bung it in your profile
The simple thing to do would be to define a variable in my profile and run Get-AzSubscription
when I load PowerShell and I could have a list to hand in each session.
Profile.ps1:
$subscriptions = Get-AzSubscription |
Select-Object -ExpandProperty Name
Profile Load Time:
Loading profiles took 8276ms.
[16:46:58] PowerShell\7-preview>
8 Seconds! 😲
Option 2 - Bung it in your profile as a job
As it turns out the Get-AzSubscription
cmdlet has an -AsJob
parameter.
It’s almost as if Microsoft know it takes a while to retrieve them by adding the ability to run it as a job
Profile.ps1:
$subscriptions = Get-AzSubscription -AsJob |
Wait-Job | Receive-Job |
Select-Object -ExpandProperty Name
Profile Load Time:
Loading profiles took 4629ms.
[16:58:11] PowerShell\7-preview>
4.6 seconds….better but still not ideal 😕
Option 3 - Bung it in your profile as a job but only receive it at runtime
So I can utilise the job to execute in the background and since I don’t need to do anything with the results straight away I will just retrieve the results of the job when I need them.
Profile.ps1:
Get-AzSubscription -AsJob | Out-Null
Profile Load Time:
Loading profiles took 2565ms.
[16:58:11] PowerShell\7-preview>
2.5 seconds. Much Better but I don’t want to have to remember to retrieve the results from the variable or I’ll just end up going straight for Get-AzSubscription
rendering this whole blog post pointless. 🤷♂️
PowerShell Profile & Argument Completer
Since I didn’t want to manually retrieve the results to be used in another command anyway I wanted to use an argument completer so I can tab complete through my subscriptions. Normally you would retrieve the results within the scriptblock of the argument completer but waiting 8 seconds for the results to return would be painstaking.
Register-ArgumentCompleter -CommandName Set-AzContext -ParameterName Subscription -ScriptBlock {
Get-AzSubscription | Select-Object -ExpandProperty Name | foreach-object {
[System.Management.Automation.CompletionResult]::new(
$_
)
}
}
If you want to read up more on Argument Completers Joel Sallow (Vexx32) has a good blog post on the available options: https://vexx32.github.io/2018/11/29/Dynamic-ValidateSet/
Solution
I decided to use both the profile job and the argument completer and since the argument completer scriptblock is not invoked until the command is being used, it won’t affect the profile load time or the execution time of the completion result.
Profile.ps1:
# Get all azure subscriptions as a background job
Get-AzSubscription -AsJob | Out-Null
Register-ArgumentCompleter -CommandName Set-AzContext -ParameterName Subscription -ScriptBlock {
# If the job exists from the command Get-AzSubscription, receive the results & remove the job
if ($job = Get-job -Command Get-AzSubscription | Wait-Job) {
$global:azSubscriptions = Receive-Job -Id $job.Id | Select-Object -ExpandProperty name
Remove-Job -Id $job.Id
}
# Add the completion results for the parameter
$global:azSubscriptions | foreach-object {
[System.Management.Automation.CompletionResult]::new(
$_
)
}
}
Profile Load Time:
Loading profiles took 2565ms.
[16:58:11] PowerShell\7-preview>
This didn’t affect the profile load time at all as it uses a background job to retrieve the results and it doesn’t have a perfomance impact on loading the completion result as they are already defined and only invoked when you use the cmdlet the completer is for.
No more copying and pasting subscription names to switch between Azure subscriptions 🎉
Comments