PowerShell Argument Completers
Dynamic tab‑completion that helps you type smarter, not harder.
I started to look into Argument Completers when I thought it would be useful to “pre-populate” one of my function arguments with a dynamically generated list of certificate templates. Then I could use Tab-Completion or the MenuComplete function from PSReadline to select available options. An argument is simply the value the user types for a parameter. The term comes from mathematics, where an argument is the value you pass into a function.
TL;DR: Custom PowerShell argument completers let you generate smart, dynamic tab‑completion lists, but they don’t enforce valid input—so they help you type faster, not validate correctness. Always validate inside your function.
What Argument Completers Do?
It provides a dynamic list of options for a a parameter. For example when creating a new item, the values for the ItemType parameter will change depending on the current location for example, the file system or registry. Check out examples below:
# Using file system
PS C:\> Get-Location
Path
----
C:\
PS C:\> New-Item -ItemType File
File Directory SymbolicLink Junction HardLink
# Change to registry
PS C:\> Set-Location hklm:
PS HKLM:\> New-Item -ItemType .\BCD00000000
BCD00000000 SAM SOFTWARE WindowsAppLockerCache
HARDWARE SECURITY SYSTEM
Note: If you use Ctrl + Spacebar you get can use the MenuComplete function from PSReadline, alternatively you can use Tab to cycle through options. Everytime you press Tab, Shift+Tab, type or delete a character the completer is rerun and provides a new set of suggestions.
Other built-in Cmdlets like Start-Service will do the same, as the the list of services depends on the computer.
Apart from the dynamic list, these parameters have validation so you can’t enter anything else that is not supported. But argument completers on it’s own just provides a list they do not enforce anything, hence you could enter whatever value, which might lead to errors.
In this post I wanted to show an example of how I used a custom argument completer to get a list of certificate templates from an internal CA.
Below are some examples
A simple example
The dynamic list could be static list. The dynamic list is assigned to the $servers variable in the code below.
# Example: Dynamic argument completer for a parameter
function Get-ServerStatus {
param(
[Parameter(Mandatory)]
[string]$ComputerName
)
Write-Host “Checking status of server: $ComputerName”
}
# Register a custom argument completer for the -ServerName parameter
Register-ArgumentCompleter -CommandName Get-ServerStatus -ParameterName ServerName -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
# Example: Dynamic list of servers (could come from a database, API, etc.)
$servers = @(”Server01”, “Server02”, “Server03”, “TestServer”)
# Filter based on what the user has typed so far
$servers |
Where-Object { $_ -like “$wordToComplete*” } |
ForEach-Object {
# Return a CompletionResult object for each match
[System.Management.Automation.CompletionResult]::new($_, $_, ‘ParameterValue’, “Server: $_”)
}
}
There is definitely more value when you dynamically get a list from somewhere.
Example with Active Directory Computers
Register-ArgumentCompleter -CommandName Get-ServerStatus -ParameterName ComputerName -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
Get-ADComputer -Filter "Name -like '$wordToComplete*'" |
Select-Object -ExpandProperty Name |
ForEach-Object {
[System.Management.Automation.CompletionResult]::new(
$_, $_, 'ParameterValue', "AD Computer: $_"
)
}
}
Now when you type:
PS C:\> Get-ServerStatus -ComputerName AZ-DC1
AZ-DC1 AZ-SVR1 AZ-SVR2 AZ-ADM1 AZ-SVR3 AZ-NODE01 AZ-NODE02 AZ-ISCSI1You get live AD computer names. As you type more it filters them further.
PS C:\> Get-ServerStatus -ComputerName AZ-SVR1
AZ-SVR1 AZ-SVR2 AZ-SVR3Example suggesting only running services
Normally when entering the name of a service you will get a list of all services whether they are running or not:
PS C:\> Stop-Service -Name
Display all 273 possibilities? (y or n) _Use an argument completer to filter for running services:
PS C:\> Register-ArgumentCompleter -CommandName Stop-Service -ParameterName Name -ScriptBlock {
param($commandName,$parameterName,$wordToComplete,$commandAst,$fakeBoundParameters)
Get-Service |
Where-Object { $_.Status -eq 'Running' -and $_.Name -like "$wordToComplete*" } |
ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_.Name,$_.Name,'ParameterValue',$_.Name)
}
}
Now when you try tab-completion or the MenuComplete function you will only see the running services:
PS C:\> Stop-Service -Name
Display all 108 possibilities? (y or n)My objective
It’s probably not a common use scenario but I needed certificates in .crt and .key (PEM) format as containers need the certificates as files, but the issue was Windows CA gives us certificates in a protected Certificate Store.
Either way you have to request a certificate from a CA and select a template.
If you use the Certificate Manager tool you will will have to select a template and certain options like subject, make private key exportable etc. Then you will need to export the public key and the private key. The private key requires a password when you export which is needed to install somewhere else, so this wasn’t ideal for containers. I would need to somehow manage the secret for a secret (ie. Password for the certificate)
Furthermore I needed to remember the correct options and manually type in the subject.
Note: PEM private keys don’t use passwords and are unencrypted so they should be handled as a secret.
I used Gemini to help me quickly create a quick and dirty function as I didn’t need anything complicated and I probably wasn’t going to share it with anyone.
For the certificates scenario I would typically chose the same template every time and I even hardcoded the name of this template into the params block so that it would be default and I didn’t have to remember or specify it each time, see below.
param (
[Parameter(Mandatory)]
[string]$CommonName,
[string[]]$DnsNames,
[string]$Template = “WebServer”,
[string]$CAName
)But in keeping with PowerShell’s ethos I decided to make the function flexible in case I ever needed to chose another template. At the end of the day the name of the template could change. The other challenge was that with the certreq commands you get text based output, so I used the argument completer to parse this and give me a clean list to use in my parameter for the template name.
Using "Native" PowerShell Method
I tried using Get-Certificate, It’s great for enrollment, but lacks an option to make the private key exportable, It depends entirely on the CA Template settings.
Also , the native Get-Certificate cmdlet doesn't have a parameter for Friendly Name which is handy later when reviewing certificates.
What I used
To me knowledge there isn’t a native Cmdlet to get templates I had to get a dynamic list using certutil with the ADTTemplate option. Since this is returned as a series of string objects and the name of the template appears before a colon (:) I needed to find the first match for one of more characters that were not a colon. Thanks to Gemini I came up with this:
# --- Appended to the bottom of your generated .psm1 ---
$CertTemplateCompleter = {
param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
# Scrape templates from certutil
$Templates = certutil.exe -ADTemplate | ForEach-Object {
if ($_ -match '^([^:]+):') { $Matches[1].Trim() }
} | Select-Object -Unique
$Templates | Where-Object { $_ -like "$WordToComplete*" } | ForEach-Object {
New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @(
$_ # completionText
$_ # listItemText
'ParameterValue'
$_ # toolTip
)
}
}
# Use splatting to bind the completer to your specific function
$CertSplat = @{
CommandName = 'New-InternalCertificate' # The name of your function from the other files
ParameterName = 'Template'
ScriptBlock = $CertTemplateCompleter
}
Register-ArgumentCompleter @CertSplatAfter this I can dynamically chose a valid certificate templates directly from Active Directory:
PS C:\> New-InternalCertificate -Template Machine
Machine MachineEnrollmentAgentIf you’re interested I have these functions saved in a GitHub Gist - Here. Excuse the New-InternalCertificate naming but as I mentioned I just needed a quick solution.
In closing
I know there are better ways and to implement certificates for containers using public certificate authorities like Let’s encrypt and DigiCert to automate renewal, I have done this in the past with NextCloud containers and Let’s Encrypt, even though they’ve been around for a while I’m still dipping my toe into the world of Containers.
This post was just to show how you can make life easier with PowerShell argument completers especially when you need to parse some text based output to use in your functions. If you can get object based output from a function and use that for your argument completer that makes things simpler.
Have you built your own argument completers? I’d love to hear what you’ve used them for — big or small. Drop an example or idea in the comments so others can learn from it too.
Further Reading
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates
https://clatent.com/2026/03/learning-validateset-in-powershell-valid-values-only/
https://metisit.com/nieuws/create-a-certificate-from-the-windows-ca/

