-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSearch-AdObjects.ps1
208 lines (181 loc) · 10.1 KB
/
Search-AdObjects.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<#
.SYNOPSIS
Searches for Users, Computers or Groups across multiple Active Directory domains / forests.
.DESCRIPTION
For users, matches are returned based on an OR of: Ambiguous Name Resolution, Department, Description
Searching with wildcards can cast a wider net in your search, for example *mysearch* (although a one-ended wildcard can also be used: *mysearch).
An example of this being useful is a search for '*sales*' would also return those with a department of 'Pre-sales'.
This very wide-net method of matching can also be a detriment, a search for '*ted*' might also return any users with 'converted' or 'created' in their description.
For computers and groups, matches are returned based on an OR of: Ambiguous Name Resolution, Description
See above about wildcards.
Parameters can be optionally specified at execution.
If the 'Search' parameter is not set, the user will be prompted to enter a search term interactively.
After results are returned this way, the user is prompted for another search indefinitely until the script is exited.
.PARAMETER PassThru
Return PSCustomObjects rather than formatting to a table.
Can only be used if 'Search' parameter is populated.
.PARAMETER Server
One or more servers to query. Can be the FQDN of the domain or a specific Domain Controller.
.PARAMETER Type
Type of Active Directory object to search for.
.PARAMETER Search
Term to search for in Active Directory, attributes being searched depend on the object type.
.INPUTS
None. You cannot pipe objects to Search-AdObjects.ps1.
.OUTPUTS
Formatted table of the Active Directory object(s) found.
If using 'PassThru' parameter, returns PSCustomObjects.
.EXAMPLE
.\Search-AdObjects.ps1 -Server ad1.example, ad2.example -Search bob
Searching servers / domains: ad1.example, ad2.example
Found User(s) matching 'bob' in ad1.example
No User(s) matching 'bob' in ad2.example
ParentCanonical Enabled GivenName Surname SamAccountName Department EmailAddress LastLogonDate Description
--------------- ------- --------- ------- -------------- ---------- ------------ ------------- -----------
ad1.example/ORG/Users True Bob Smith bob.smith Sales [email protected] 01/01/2000 12:00:00
.EXAMPLE
.\Search-AdObjects.ps1 -Search *sales*
Searching servers / domains: ad1.example, ad2.example
Found User(s) matching '*sales*' in ad1.example
Found User(s) matching '*sales*' in ad2.example
ParentCanonical Enabled GivenName Surname SamAccountName Department EmailAddress LastLogonDate Description
--------------- ------- --------- ------- -------------- ---------- ------------ ------------- -----------
ad1.example/ORG/Users True Bob Smith bob.smith Sales [email protected] 01/01/2000 12:00:00
ad1.example/ORG/Users True Jane Baker jane.baker Pre-sales [email protected] 01/01/2000 12:00:00
ad2.example/ORG/Users True John Green john.green Marketing [email protected] 01/01/2000 12:00:00 Interim sales
.EXAMPLE
.\Search-AdObjects.ps1 -Server ad1.example, ad2.example -Type Computer -Search '*file server*''
Searching servers / domains: ad1.example, ad2.example
Found Computers(s) matching '*file server*' in ad1.example
No Computers matching '*file server*' in ad2.example
ParentCanonical Enabled Name IPv4Address OperatingSystem LastLogonDate Description
--------------- ------- ---- ----------- --------------- ------------- -----------
ad1.example/ORG/Computers True fs01 10.0.0.10 Windows Server 2019 Standard 01/01/2000 12:00:00 ProjectA File Server
.EXAMPLE
.\Search-AdObjects.ps1 -Type Group -Search 'local admin'
Searching servers / domains: ad1.example, ad2.example
Found Group(s) matching 'local admin' in ad1.example
Found Group(s) matching 'local admin' in ad2.example
ParentCanonical Name EmailAddress GroupScope GroupCategory Description
--------------- ---- ------------ ---------- ------------- -----------
ad1.example/ORG/Security Groups/Local Admin Local Admin app01 DomainLocal Security Members are local administrator on app01
ad2.example/ORG/Security Groups/Local Admin Local Admin sync01 DomainLocal Security Members are local administrator on sync01
.EXAMPLE
.\Search-AdObjects.ps1 -PassThru -Search *sales* | Out-GridView
<PowerShell GridView GUI>
#>
param (
# Default servers / domains to search are defined here, use the 'Server' parameter to override - or change the below line
[Parameter()][Alias('Domain')][ValidateNotNullOrEmpty()][String[]]$Server = ('ad1.example', 'ad2.example'),
# Default type of object to search is User, use 'Type' parameter to override
[Parameter()][ValidateSet('User', 'Computer', 'Group')][String]$Type = 'User',
[Parameter()][ValidateNotNullOrEmpty()][String]$Search,
[Parameter()][Switch]$PassThru
)
function ConvertTo-ParentCanonical {
param (
[Parameter(Mandatory, ValueFromPipeline)][String[]]$CanonicalName
)
$CanonicalName | ForEach-Object {
$($_.Substring(0, $($_).lastIndexOf('/')))
}
}
function Search-AdObjects {
param (
[Parameter(Mandatory, ValueFromPipeline)][String[]]$Server,
[Parameter(Mandatory, ValueFromPipeline)][ValidateSet('User', 'Computer', 'Group')][String]$Type,
[Parameter(Mandatory, ValueFromPipeline)][String]$Search
)
foreach ($singleServer in $Server) {
switch ($Type) {
'User' {
$searchParameters = @{
Server = $singleServer
# For info on Active Directory's ANR feature see https://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx
LDAPFilter = "(|(anr=$Search)(department=$Search)(description=$Search))"
Properties = 'CanonicalName', 'Enabled', 'GivenName', 'Surname', 'SamAccountName', 'Department', 'EmailAddress', 'LastLogonDate', 'Description'
}
$results = Get-AdUser @searchParameters | ForEach-Object {
[PSCustomObject]@{
ParentCanonical = $_.CanonicalName | ConvertTo-ParentCanonical
Enabled = $_.Enabled
GivenName = $_.GivenName
Surname = $_.Surname
SamAccountName = $_.SamAccountName
Department = $_.Department
EmailAddress = $_.EmailAddress
LastLogonDate = $_.LastLogonDate
Description = $_.Description
}
}
}
'Computer' {
$searchParameters = @{
Server = $singleServer
# For info on Active Directory's ANR feature see https://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx
LDAPFilter = "(|(anr=$Search)(description=$Search))"
Properties = 'CanonicalName', 'Enabled', 'Name', 'IPv4Address', 'OperatingSystem', 'LastLogonDate', 'Description'
}
$results = Get-AdComputer @searchParameters | ForEach-Object {
[PSCustomObject]@{
ParentCanonical = $_.CanonicalName | ConvertTo-ParentCanonical
Enabled = $_.Enabled
Name = $_.Name
IPv4Address = $_.IPv4Address
OperatingSystem = $_.OperatingSystem
LastLogonDate = $_.LastLogonDate
Description = $_.Description
}
}
}
'Group' {
$searchParameters = @{
Server = $singleServer
# For info on Active Directory's ANR feature see https://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx
LDAPFilter = "(|(anr=$Search)(description=$Search))"
Properties = 'CanonicalName', 'Name', 'Mail', 'GroupScope', 'GroupCategory', 'Description'
}
$results = Get-AdGroup @searchParameters | ForEach-Object {
[PSCustomObject]@{
ParentCanonical = $_.CanonicalName | ConvertTo-ParentCanonical
Name = $_.Name
EmailAddress = $_.Mail
GroupScope = $_.GroupScope
GroupCategory = $_.GroupCategory
Description = $_.Description
}
}
}
}
if (($results | Measure-Object).Count -gt 0) {
Write-Host ("Found {0}(s) matching '{1}' in {2}" -f $Type, $Search, $singleServer) -ForegroundColor Green
$results
}
else {
Write-Host ("No {0}s matching '{1}' in {2}" -f $Type, $Search, $singleServer) -ForegroundColor Gray
}
}
}
Import-Module -Name ActiveDirectory -ErrorAction Stop
Write-Host ("Searching servers / domains: {0}" -f ($Server -join ', ')) -ForegroundColor Cyan
# If the $Search is populated, just return the search result
if ($Search) {
# If $PassThru is present, return the results as an object
if ($PassThru) {
Search-AdObjects -Server $Server -Type $Type -Search $Search
}
# If $PassThru is absent, return the results as a formatted table
else {
Search-AdObjects -Server $Server -Type $Type -Search $Search | Format-Table -AutoSize
}
}
# Else prompt the user to type their search, loop indefinitely unless user breaks loop
else {
if ($PassThru) {
Throw "'Search' parameter must be defined if using 'PassThru'"
}
while ($true) {
$Search = Read-Host 'Enter a search term, eg. Name, Description, Department. Press Ctrl+C to cancel'
Search-AdObjects -Server $Server -Type $Type -Search $Search | Format-Table -AutoSize
}
}