Complete your resource groups and resource parameter values like Azure PowerShell
This week I was busy with some module development, and I was wondering how the user experience could be more user-friendly. When you use the Azure PowerShell modules you get nice autocompletion on resource groups and resources, so I was wondering if I could give the same comfort to my module and script users. With this blog post I will show you, how I implement Azure PowerShell their argument completers, to give users of my scripts and modules this same experience with my parameters.
Argument Completers?
If our users interact with your script or module (outside the context of a pipeline), it can be convenient to give them relevant tab completion results on parameters.
I’ve dealt with custom argument completers for parameter arguments before, I read the excellent about_Functions_Argument_Completion article and thought I could do this the dumb way or the lazy way.
In the Learn article it’s explained that you can either hardcode the options in a ValidateSet
/ArgumentCompletions
attribute decorator, use an argument completer/scriptblock to gather the autocomplete options, or use a class-based argument completer.
Source
The first option is not feasible since we don’t know which resource exist where for each user, so that option is out. The sub-optimal way would be calling Get-AzResource
in a scriptblock on everything and then offering the result as tab completion options. But the lazy way would be to just use the already made argument completers made by Azure PowerShell. The argument completers used in Azure PowerShell are functional - we all use them every day!
The resource argument completers are stored in the Azure PowerShell modules source code at the GitHub repo. I asked for some help on why this works on the PowerShell discord, join us at https://aka.ms/PSDiscord, and I learned that the Azure PowerShell modules implemented these in public types, so if we load the correct module(s) we should be able to use these.
If you do implement argument completion it using these existing completers, mind that this feature is depending on the fact the types in the Azure Powershell Module are scoped to public for now, but Microsoft may decide otherwise in the future.
The directory has the following files containing argument completer classes:
- LocationCompleter.cs
- The
LocationCompleter
attribute will allow the user to autocomplete the -Location parameter of a cmdlet with valid locations (as determined by the list of ResourceTypes given)
- The
- ResourceGroupCompleter.cs
- The
ResourceGroupCompleter
attribute will allow the user to autocomplete the-ResourceGroup
parameter of a cmdlet with valid resource groups
- The
- ResourceIdCompleter.cs
- The
ResourceIdCompleter
attribute will allow the user to autocomplete the-ResourceId
parameter of a cmdlet with valid resource identifiers (as determined by the list of ResourceTypes given)
- The
- ResourceNameCompleter.cs
- The
ResourceNameCompleter
attribute will allow the user to autocomplete the-Location
parameter of a cmdlet with valid resource names (as determined by the list of ResourceTypes given)
- The
- ResourceTypeCompleter.cs
- The
ResourceTypeCompleter
attribute will allow the user to autocomplete the-ResourceType
parameter of a cmdlet with valid resource types
- The
- ScopeCompleter.cs
- The
ScopeCompleter
class will provide a list of scopes that are available to the user. This will then be available to the user to tab through.
- The
Let’s implement
In the following section I’ll give some examples, only the Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.*
attributes and the names of the parameters in these sections are relevant. Tab completion will work if you paste in these code sections and try it for yourself.
I do want to mention, never blindly copy-paste code you find, always read and understand it before you do, you never know what it might do.
The code below requires you to already have Azure PowerShell modules imported, like Az.Accounts
, and you have a signed in session (which requires the former).
Resource Group names
The simplest example using is the completer from ResourceGroupCompleter.cs
. If you want to provide the option to give tab-completion to users for the resource group names you can do:
1function Get-ResourceGroup{
2 [CmdletBinding()]
3 param (
4 # This attribute will allow the user to autocomplete with valid resource groups
5 [Parameter()]
6 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceGroupCompleterAttribute()]
7 [string]
8 $ResourceGroupName
9 )
10}
The resource group completer is the simplest implementation, since it doesn’t require any additional arguments. It just works when you press tab to show all options, or if you type the start of a resource group name and it’ll autocomplete.
Resource types
This example show autocompletion for resource types available, it isn’t context aware of what resource types you’ve implemented:
1function Get-ResourceTypes {
2 [CmdletBinding()]
3 param (
4 # This attribute will allow the user to autocomplete with valid resource types
5 [Parameter()]
6 [String]
7 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceTypeCompleterAttribute()]
8 $ResourceType
9 )
10}
So if we try to autocomplete the ResourceType
-parameter for the resourcetypes ending with Microsoft.Web/s
it’ll bring up the following options if we press <TAB>
twice.
I trimmed the output, but you get the idea.
Scopes
Scopes is also a cool one to enumerate all the scopes available in the current context (subscription).
1function Get-AzureResourceScopes {
2 [CmdletBinding()]
3 param (
4 # This attribute will allow the user to autocomplete with valid scopes in current context
5 [Parameter()]
6 [String]
7 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ScopeCompleterAttribute()]
8 $Scope
9 )
10}
If you want to know what options are available, without pressing tab, you can press CTRL
+ SPACEBAR
.
That way it’ll show all the options, it does not work with many options.
Resource names
The resource names completer requires additional arguments specified in the ResourceNameCompleter.cs
-file. you must supply the Azure resource type you’re looking for and ResourceGroupName
it works like a breeze. I was wondering how the parameter is aware of the value of another parameter it’s value, and then I learned about the FakeBoundParameters and relearned about PSBoundParameters
.
The code below shows you how you could fetch your Log Analytics workspaces. You could alter this to any resourcetype.
1function Get-ResourceNames {
2 [CmdletBinding()]
3 param (
4 # This attribute will allow the user to autocomplete with valid resource groups
5 [Parameter()]
6 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceGroupCompleterAttribute()]
7 [string]
8 $ResourceGroupName,
9 # This attribute will allow the user to autocomplete resourcenames within the supplied resourcegroups
10 [Parameter()]
11 [String]
12 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceNameCompleterAttribute(
13 "Microsoft.OperationalInsights/workspaces",
14 "ResourceGroupName"
15 )]
16 $WorkspaceName
17 )
18}
Resource Id completer
This one was one I find really cool, you can autocomplete resource-ids of a specified type. In many cases you want to have the full resource-id of an Azure resource, but tab completion is mostly offered to just the resource group or the resource name. Which means you’d have to do another call to find the resource-id. With this completer you can just offer tabcompletion of the whole id, yay!
1function Get-ResourceID {
2 [CmdletBinding()]
3 param (
4 # This attribute will allow the user to autocomplete resource-ids for the specified resource type
5 [Parameter()]
6 [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleter(
7 "Microsoft.OperationalInsights/workspaces"
8 )]
9 [string]$ResourceId
10 )
11}
Note: I’ve discussed this post before publishing with the fantastic Mathias (@IISResetMe) from the PowerShell discord, he proofread the article, for which thanks again. Mathias added that the
Attribute
suffix in the typenames are automatically added by the PowerShell parser, when searching for typenames in syntactical contexts. So in this context, these will be handled the same:
1[Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceNameCompleterAttribute()]
2[Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceNameCompleter()]
Conclusion
This is all there’s to it. You do create a dependency on the Azure PowerShell modules and the fact that these are public types. But these argument completers can be a very nice comfort feature/easy win for your user experience - you just have to decorate a parameter with a single or two attributes.
I hope you learned as much as I did and that your user may enjoy fancy tab completion.