Register-VNActiveDirectoryArgumentCompleter.ps1

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

<#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
}