Register-VNActiveDirectoryArgumentCompleter.ps1
<#PSScriptInfo .VERSION 1.0.1 .GUID d7c13ce3-a513-4a05-8093-8e098a7e9557 .AUTHOR Matt Boren (@mtboren) .COMPANYNAME vNugglets .COPYRIGHT MIT License .TAGS vNugglets PowerShell ArgumentCompleter Parameter ActiveDirectory AD AdminOptimization NaturalExperience TabComplete TabCompletion Completion Awesome .LICENSEURI https://github.com/vNugglets/PowerShellArgumentCompleters/blob/main/License .PROJECTURI https://github.com/vNugglets/PowerShellArgumentCompleters .ICONURI https://avatars0.githubusercontent.com/u/22530966 .EXTERNALMODULEDEPENDENCIES ActiveDirectory .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES See ReadMe and other docs at https://github.com/vNugglets/PowerShellArgumentCompleters .PRIVATEDATA #> #Requires -Module ActiveDirectory <# .DESCRIPTION Script to register PowerShell argument completers for many parameters of ActiveDirectory module cmdlets, making us even more productive on the command line. This enables the tab-completion of actual ActiveDirectory objects' and properties' names as values to parameters to ActiveDirectory cmdlets -- neat! #> Param() process { ## AD object property name completer $sbPropertyNameCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) ## get the AD schema for the default AD forest; used to get given object class and its properties $oADSchema = [DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetSchema([System.DirectoryServices.ActiveDirectory.DirectoryContext]::new([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Forest, (Get-ADForest))) $strADClassName = Switch ($commandName) { "Get-ADComputer" {"computer"} "Get-ADGroup" {"group"} "Get-ADOrganizationalUnit" {"organizationalUnit"} "Get-ADUser" {"user"} } $oADSchema.FindClass($strADClassName).GetAllProperties().Where({$_.Name -like "${wordToComplete}*"}) | Sort-Object -Property Name |Foreach-Object { New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList ( $_.Name, # CompletionText $_.Name, # ListItemText [System.Management.Automation.CompletionResultType]::ParameterValue, # ResultType ("[{0}] {1} (description of '{2}')" -f $_.Syntax, $_.Name, $_.Description) # ToolTip ) } ## end Foreach-Object } ## end scriptblock ## specific cmdlets with given parameter Write-Output Get-ADComputer, Get-ADGroup, Get-ADOrganizationalUnit, Get-ADUser | Foreach-Object { Register-ArgumentCompleter -CommandName $_ -ParameterName Properties -ScriptBlock $sbPropertyNameCompleter } ## end Foreach-Object ## AD OU DN completer, searching by OU DN $sbOUDNCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) ## get OUs, supporting filtering _just_ on the OU name # Get-ADOrganizationalUnit -Filter {Name -like "${wordToCompleter}*"} | Foreach-Object { ## get OUs, supporting filtering on the OU DN itself; must be done by getting all OUs and then filtering client-side, as server-side DN filter only supports exact match, not wildcarding, seemingly # and, sorting by the parent OUs, essentially, so matches are presented in "grouped-by-OU" order, for easiest recognition by consumer $hshParmForGetADOU = @{Filter = "*"; Properties = "Name", "DistinguishedName", "whenCreated", "Description"} ## if these other params are specified to the command, pass them through Write-Output Credential Server | Foreach-Object { if ($fakeBoundParameter.ContainsKey($_)) {$hshParmForGetADOU[$_] = $fakeBoundParameter.$_} } (Get-ADOrganizationalUnit @hshParmForGetADOU).Where({$_.DistinguishedName -like "*${wordToComplete}*"}) | Sort-Object -Property {($arrlTmp = [System.Collections.ArrayList]($_.DistinguishedName).Split(",")).Reverse(); $arrlTmp -join ","} | Foreach-Object { New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList ( $(if ($_.DistinguishedName -match "[, ']") {'"{0}"' -f $_.DistinguishedName} else {$_.DistinguishedName}), # CompletionText $_.DistinguishedName, # ListItemText [System.Management.Automation.CompletionResultType]::ParameterValue, # ResultType ("{0} (created '{1}', description of '{2}')" -f $_.DistinguishedName, $_.whenCreated, $_.Description) # ToolTip ) } ## end Foreach-Object } ## end scriptblock ## cmdlets with given parameter Write-Output SearchBase, TargetPath | Foreach-Object { $strThisParamName = $_ Get-Command -Module ActiveDirectory -ParameterName $strThisParamName | ForEach-Object {Register-ArgumentCompleter -CommandName $_ -ParameterName $strThisParamName -ScriptBlock $sbOUDNCompleter} } ## end Foreach-Object ## specific Cmdlets ## param Path Register-ArgumentCompleter -CommandName (Write-Output New-ADComputer, New-ADGroup, New-ADObject, New-ADOrganizationalUnit, New-ADServiceAccount, New-ADUser) -ParameterName Path -ScriptBlock $sbOUDNCompleter ## param Identity Register-ArgumentCompleter -CommandName (Get-Command -Module ActiveDirectory -ParameterName Identity -Noun ADOrganizationalUnit).Name -ParameterName Identity -ScriptBlock $sbOUDNCompleter ## AD Group name completer $sbGroupIdentityCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) $hshParmForGetADGroup = @{Filter = "Name -like '${wordToComplete}*'"; Properties = "Name", "Description"} ## if these other params are specified to the command, pass them through Write-Output Credential Server | Foreach-Object { if ($fakeBoundParameter.ContainsKey($_)) {$hshParmForGetADGroup[$_] = $fakeBoundParameter.$_} } Get-ADGroup @hshParmForGetADGroup | Sort-Object -Property Name | Foreach-Object { $strCompletionText = if ($_.Name -match "\s") {'"{0}"' -f $_.Name} else {$_.Name} New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList ( $strCompletionText, # CompletionText $_.Name, # ListItemText [System.Management.Automation.CompletionResultType]::ParameterValue, # ResultType ("{0} ({1} {2} group, description of '{3}')" -f $_.DistinguishedName, $_.GroupScope, $_.GroupCategory, $_.Description) # ToolTip ) } ## end Foreach-Object } ## end scriptblock ## for these cmdlets Register-ArgumentCompleter -CommandName (Write-Output Get-ADGroup Remove-ADGroup Set-ADGroup Add-ADGroupMember Get-ADGroupMember Remove-ADGroupMember) -ParameterName Identity -ScriptBlock $sbGroupIdentityCompleter ## for MemberOf param Register-ArgumentCompleter -CommandName (Write-Output Add-ADPrincipalGroupMembership) -ParameterName MemberOf -ScriptBlock $sbGroupIdentityCompleter ## AD Group membership completer (say, for a principal, or for members of a group) $sbGroupMembershipCompleter = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) ## if the -Idenity param is in the command line, get the corresponding AD object and return info on its members if ($fakeBoundParameter.ContainsKey("Identity")) { ## if these other params are specified to the command, pass them through Write-Output Credential | Foreach-Object -Begin {$hshParmForGet = @{}} { if ($fakeBoundParameter.ContainsKey($_)) {$hshParmForGet[$_] = $fakeBoundParameter.$_} } ## if -Server specified, use it for Get-ADObject call; else, use the domain root / global catalog port for server, to account for getting AD objects from cross-domain in the same forest $hshParmForGet["Server"] = if ($fakeBoundParameter.ContainsKey("Server")) {$fakeBoundParameter["Server"]} else {"{0}:3268" -f (Get-ADForest).RootDomain} $strThisIdentity = $fakeBoundParameter["Identity"] $strCmdletToGetThisIdentity, $strMembershipPropertyOfInterest = Switch ($commandName) { "Remove-ADGroupMember" {"Get-ADGroup", "members"; break} ## "members" property name seems case sensitive in at least some AD envs "Remove-ADPrincipalGroupMembership" {"Get-ADObject", "MemberOf"; break} } $oThisADObject = & $strCmdletToGetThisIdentity -Properties $strMembershipPropertyOfInterest -Filter "(Name -eq '$strThisIdentity') -or (SamAccountName -eq '$strThisIdentity')" @hshParmForGet ## if this AD object has members (for group) or is a member of things (for principal), get them if (($oThisADObject.$strMembershipPropertyOfInterest | Measure-Object).Count -gt 0) { $oThisADObject.$strMembershipPropertyOfInterest | Where-Object {$_.Split(",")[0].Split("=")[1] -like "${wordToComplete}*"} | Foreach-Object {Get-ADObject @hshParmForGet -Identity $_ -Properties Description, samaccountname, DisplayName} | Sort-Object -Property Name | Foreach-Object { $strCompletionText = if ($_.samaccountname -match "\s") {'"{0}"' -f $_.samaccountname} else {$_.samaccountname} New-Object -TypeName System.Management.Automation.CompletionResult -ArgumentList ( $strCompletionText, # CompletionText $_.samaccountname, # ListItemText [System.Management.Automation.CompletionResultType]::ParameterValue, # ResultType ("[{0}] {1} (DisplayName of '{2}', description of '{3}')" -f $_.ObjectClass, $_.DistinguishedName, $_.DisplayName, $_.Description) # ToolTip ) } ## end Foreach-Object } } } ## end scriptblock ## for these cmdlets Register-ArgumentCompleter -CommandName (Write-Output Remove-ADPrincipalGroupMembership) -ParameterName MemberOf -ScriptBlock $sbGroupMembershipCompleter Register-ArgumentCompleter -CommandName (Write-Output Remove-ADGroupMember) -ParameterName Members -ScriptBlock $sbGroupMembershipCompleter } |