Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1 Register for Semperis' Hybrid Identity Protection (HIP) Conference - June 30 - July 1
PowerShell

Parallel Processing in PowerShell: Active Directory Cmdlets Revisited

In my article, An Introduction to Parallel PowerShell Processing, we looked at different ways to work with PowerShell in parallel. Specifically, we looked at how to find objects with Active Directory cmdlets by searching multiple locations at the same time. Although I complain about the AD cmdlets, they do perform quite well. All that I really need is a version of Get-ADComputer that will automatically do that for me. It would also be useful to be able to specify a search path to exclude.

Once again, my handy Copy-Command function comes to the rescue. In the ISE, I create a new version of the command.

Copy-Command Get-ADComputer Get-MyADComputer
The Get-MyADComputer function in the PowerShell ISE. (Image Credit: Jeff Hicks)
The Get-MyADComputer function in the PowerShell ISE. (Image Credit: Jeff Hicks)

The first thing is to modify the Searchbase parameter so that it can accept an array of strings.

[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='LdapFilter')]
[ValidateNotNull()]
[string[]]$SearchBase,

In the Process scriptblock, I need to reflect that if multiple locations are specified that Get-ADComputer be run for each location. Because I am splatting PSBoundParameters to Get-ADComputer, I need to tweek the Searchbase parameter because even though my function can accept an array, the original command does not.

#run Get-ADcomputer for each location in the $SearchBase parameter
if ($SearchBase) {
   $results = foreach ($location in $SearchBase) {
   $PSBoundparameters.searchBase = $location
   Write-Verbose ($PSBoundparameters | Out-String)
   Write-Verbose "Searching $location"
   Get-ADComputer @PSBoundparameters
}

If I run the command without specifying a searchbase, then the command runs normally.
else {
   Write-Verbose "Searching entire domain"
   $results = Get-ADComputer @PSBoundparameters
}

You’ll notice that I’m saving results to a variable. This is so I can exclude based on a regular expression pattern. This requires a new parameter.
[Parameter(ParameterSetName='Exclude')]
[ValidateNotNull()]
[regex]$Exclude,

Even though I’m specifying a REGEX type, you can use a simple string. However, the searchbase and exclude parameters are mutually exclusive, so I created an additional parameter set. This also means going through the other parameters and adding them to the Exclude parameter set. I made a decision to only exclude if –Filter was used.
[Parameter(ParameterSetName='Filter')]
[Parameter(ParameterSetName='Exclude')]
[ValidateNotNullOrEmpty()]
[string]$Filter,

This means that I need to remove –Exclude from $PSBoundparameters, because Get-ADComputer won’t recognize it.
if ($Exclude) {
$PSBoundParameters.Remove("Exclude") | Out-Null
}

In the End scriptblock, if an exclude value was specified, then filter the results otherwise write everything to the pipeline.
#filter if excluding
if ($Exclude) {
   Write-Verbose "Excluding results"
   $results.where({$_.distinguishedname -notmatch $exclude})
}
else {
   #Write all results to the pipeline
   $results
}

Because I’m creating my own version of Get-ADComputer, I also decided to fix the filter parameter. I want to automatically set the filter to *, unless I specify otherwise. I am constantly running Get-ADComputer and expecting it to give me all results, but instead I’m prompted to enter a filter. For me, I guess I’m expecting it to behave like Get-Service or Get-Process, so I’ll bend it to my will and add a default value.
if (-Not $PSBoundparameters.ContainsKey("Filter") -AND ($PSCmdlet.parametersetname -match "filter|exclude")) {
   Write-Verbose "Using default filter of *"
   $PSBoundparameters.Add("Filter","*")
}

This if statement says, “if the parameter set is Filter or Exclude AND the user did not specify –Filter, go ahead and set a default filter of *.” The last step of the process was to update the comment-based help to reflect the new parameters and change in behavior. Here’s the finished result.

#requires -version 4.0
#requires -module ActiveDirectory

<#
This is a copy of:

CommandType Name           ModuleName     
----------- ----           ----------     
Cmdlet      Get-ADComputer ActiveDirectory

Created: 5/29/2015
Author : Jeff

this is a proxy version of Get-ADComputer that supports
searching multiple locations. Filter parameter defaults to *.

  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************
#>


Function Get-MyADComputer {

<#

.SYNOPSIS

A custom version of Get-ADComputer to get one or more Active Directory computers.


.DESCRIPTION

The Get-MyADComputer cmdlet gets a computer or performs a search to retrieve multiple computers.

The Identity parameter specifies the Active Directory computer to retrieve. You can identify a computer by its distinguished name (DN), GUID, security identifier (SID) or Security Accounts Manager (SAM) account name. You can also set the parameter to a computer object variable, such as $<localComputerObject> or pass a computer object through the pipeline to the Identity parameter.

To search for and retrieve more than one computer, use the Filter or LDAPFilter parameters. The Filter parameter uses the PowerShell Expression Language to write query strings for Active Directory. PowerShell Expression Language syntax provides rich type conversion support for value types received by the Filter parameter. For more information about the Filter parameter syntax, type Get-Help about_ActiveDirectory_Filter. If you have existing LDAP query strings, you can use the LDAPFilter parameter.

This cmdlet retrieves a default set of computer object properties. To retrieve additional properties use the Properties parameter. For more information about the how to determine the properties for computer objects, see the Properties parameter description.

NOTE: This version supports excluding by filtering on the distinguishedname. Or you can specify multiple search locations.

.PARAMETER AuthType

Specifies the authentication method to use. The acceptable values for this parameter are:&nbsp;

-- Negotiate or 0
-- Basic or 1

The default authentication method is Negotiate.

A Secure Sockets Layer (SSL) connection is required for the Basic authentication method.

.PARAMETER Credential

Specifies the user account credentials to use to perform this task. The default credentials are the credentials of the currently logged on user unless the cmdlet is run from an Active Directory PowerShell provider drive. If the cmdlet is run from such a provider drive, the account associated with the drive is the default.

To specify this parameter, you can type a user name, such as User1 or Domain01\User01 or you can specify a PSCredential object. If you specify a user name for this parameter, the cmdlet prompts for a password.

You can also create a PSCredential object by using a script or by using the Get-Credential cmdlet. You can then set the Credential parameter to the PSCredential object.

If the acting credentials do not have directory-level permission to perform the task, Active Directory PowerShell returns a terminating error.

.PARAMETER Filter

Specifies a query string that retrieves Active Directory objects. This string uses the PowerShell Expression Language syntax. The PowerShell Expression Language syntax provides rich type-conversion support for value types received by the Filter parameter. The syntax uses an in-order representation, which means that the operator is placed between the operand and the value. For more information about the Filter parameter, type Get-Help about_ActiveDirectory_Filter.

The default behavior in this command is to return all computer accounts.

Syntax:

The following syntax uses Backus-Naur form to show how to use the PowerShell Expression Language for this parameter.

<filter>  ::= "{" <FilterComponentList> "}"

<FilterComponentList> ::= <FilterComponent> | <FilterComponent> <JoinOperator> <FilterComponent> | <NotOperator>  <FilterComponent>

<FilterComponent> ::= <attr> <FilterOperator> <value> | "(" <FilterComponent> ")"

<FilterOperator> ::= "-eq" | "-le" | "-ge" | "-ne" | "-lt" | "-gt"| "-approx" | "-bor" | "-band" | "-recursivematch" | "-like" | "-notlike"

<JoinOperator> ::= "-and" | "-or"

<NotOperator> ::= "-not"

<attr> ::= <PropertyName> | <LDAPDisplayName of the attribute>

<value>::= <compare this value with an <attr> by using the specified <FilterOperator>>

For a list of supported types for <value>, type Get-Help about_ActiveDirectory_ObjectModel.

Note: PowerShell wildcards other than *, such as ?, are not supported by the Filter syntax.

Note: To query using LDAP query strings, use the LDAPFilter parameter.

.PARAMETER Identity

Specifies an Active Directory computer object by providing one of the following property values. The identifier in parentheses is the LDAP display name for the attribute. The acceptable values for this parameter are:&nbsp;

-- A Distinguished Name
-- A GUID (objectGUID) 
-- A Security Identifier (objectSid) 
-- A Security Accounts Manager Account Name (sAMAccountName)

The cmdlet searches the default naming context or partition to find the object. If the identifier given is a DN, the partition to search will be computed from that DN. If two or more objects are found, the cmdlet returns a non-terminating error.

This parameter can also get this object through the pipeline or you can set this parameter to a computer object instance.

.PARAMETER LDAPFilter

Specifies an LDAP query string that is used to filter Active Directory objects. You can use this parameter to run your existing LDAP queries. The Filter parameter syntax supports the same functionality as the LDAP syntax. For more information, see the Filter parameter description or type Get-Help about_ActiveDirectory_Filter.

.PARAMETER Partition

Specifies the distinguished name of an Active Directory partition. The distinguished name must be one of the naming contexts on the current directory server. The cmdlet searches this partition to find the object defined by the Identity parameter.

In many cases, a default value will be used for the Partition parameter if no value is specified.  The rules for determining the default value are given below.  Note that rules listed first are evaluated first and once a default value can be determined, no further rules will be evaluated.

In AD DS environments, a default value for Partition will be set in the following cases:  

-- If the Identity parameter is set to a distinguished name, the default value of Partition is automatically generated from this distinguished name.
-- If running cmdlets from an Active Directory provider drive, the default value of Partition is automatically generated from the current path in the drive. 
-- If none of the previous cases apply, the default value of Partition will be set to the default partition or naming context of the target domain.

In AD LDS environments, a default value for Partition will be set in the following cases:

-- If the Identity parameter is set to a distinguished name, the default value of Partition is automatically generated from this distinguished name. 
-- If running cmdlets from an Active Directory provider drive, the default value of Partition is automatically generated from the current path in the drive. 
-- If the target AD LDS instance has a default naming context, the default value of Partition will be set to the default naming context.  To specify a default naming context for an AD LDS environment, set the msDS-defaultNamingContext property of the Active Directory directory service agent (DSA) object (nTDSDSA) for the AD LDS instance. 
-- If none of the previous cases apply, the Partition parameter will not take any default value.

.PARAMETER Properties

Specifies the properties of the output object to retrieve from the server. Use this parameter to retrieve properties that are not included in the default set.

Specify properties for this parameter as a comma-separated list of names. To display all of the attributes that are set on the object, specify * (asterisk).

To specify an individual extended property, use the name of the property. For properties that are not default or extended properties, you must specify the LDAP display name of the attribute.

To retrieve properties and display them for an object, you can use the Get-* cmdlet associated with the object and pass the output to the Get-Member cmdlet.

.PARAMETER ResultPageSize

Specifies the number of objects to include in one page for an Active Directory Domain Services query.

The default is 256 objects per page.

.PARAMETER ResultSetSize

Specifies the maximum number of objects to return for an Active Directory Domain Services query. If you want to receive all of the objects, set this parameter to $Null (null value). You can use Ctrl+C to stop the query and return of objects.

The default is $Null.

.PARAMETER SearchBase

Specifies an Active Directory path to search under.

When you run a cmdlet from an Active Directory provider drive, the default value of this parameter is the current path of the drive.

When you run a cmdlet outside of an Active Directory provider drive against an AD DS target, the default value of this parameter is the default naming context of the target domain.

When you run a cmdlet outside of an Active Directory provider drive against an AD LDS target, the default value is the default naming context of the target LDS instance if one has been specified by setting the msDS-defaultNamingContext property of the Active Directory directory service agent (DSA) object (nTDSDSA) for the AD LDS instance.  If no default naming context has been specified for the target AD LDS instance, then this parameter has no default value.

When the value of the SearchBase parameter is set to an empty string and you are connected to a GC port, all partitions will be searched. If the value of the SearchBase parameter is set to an empty string and you are not connected to a GC port, an error will be thrown.

Note: You can specify multiple locations to search.

.PARAMETER SearchScope

Specifies the scope of an Active Directory search. The acceptable values for this parameter are:&nbsp;

-- Base or 0
-- OneLevel or 1
-- Subtree or 2

A Base query searches only the current path or object. A OneLevel query searches the immediate children of that path or object. A Subtree query searches the current path or object and all children of that path or object.

.PARAMETER Server

Specifies the Active Directory Domain Services instance to connect to, by providing one of the following values for a corresponding domain name or directory server. The service may be any of the following:  Active Directory Lightweight Domain Services, Active Directory Domain Services or Active Directory Snapshot instance.

Specify the Active Directory Domain Services instance in one of the following ways:  

-- Domain name values:

---- Fully qualified domain name
---- NetBIOS name

-- Directory server values: 

---- Fully qualified directory server name
---- NetBIOS name
---- Fully qualified directory server name and port

The default value for this parameter is determined by one of the following methods in the order that they are listed:

-- By using the Server value from objects passed through the pipeline
-- By using the server information associated with the Active Directory Domain ServicesWindows PowerShell provider drive, when the cmdlet runs in that drive
-- By using the domain of the computer running Windows PowerShell
.PARAMETER Exclude
A regular expression pattern, or even a simple string, that represents part or an entire Active Directory location. 
This will be used to filter out matching objects using their distinguished name.
.EXAMPLE
PS C:\> get-myadcomputer -filter "name -like 'chi-*'" -Server chi-dc04 -Exclude "(CN=Computers|OU=Development),DC=Globomantics,DC=Local"

This command will find all computers that start with CHI- except those in the Computers container, or the Development OU.
.EXAMPLE
PS C:\> $DN = (Get-ADOrganizationalUnit -filter "Name -notlike 'Domain Controllers'" -Server chi-dc04).distinguishedname

Create a list of OU distinguished names to search.

PS C:\> get-myadcomputer -SearchBase $dn -filter "name -like 'chi-*'" -Server chi-dc04

Now search the list of locations for computers that start with CHI-.
.EXAMPLE

PS C:\>Get-MyADComputer -Identity "CHI-SRV1" -Properties *


AccountExpirationDate              : 
accountExpires                     : 9223372036854775807
AccountLockoutTime                 : 
AccountNotDelegated                : False
AllowReversiblePasswordEncryption  : False
BadLogonCount                      : 
CannotChangePassword               : False
CanonicalName                      : Globomantics.com/Computers/chi-srv1
Certificates                       : {}
CN                                 : chi-srv1
codePage                           : 0
countryCode                        : 0
Created                            : 3/16/2015 4:15:00 PM
createTimeStamp                    : 3/16/2015 4:15:00 PM
Deleted                            : 
Description                        : DisplayName                        : 
DistinguishedName                  : CN=chi-srv1,CN=Computers,DC=Globomantics,dc=local
DNSHostName                        : 
DoesNotRequirePreAuth              : False
dSCorePropagationData              : {3/16/2015 4:21:51 PM, 12/31/1600 4:00:01PM}
Enabled                            : True
HomedirRequired                    : False
HomePage                           : 
instanceType                       : 0
IPv4Address                        : 
IPv6Address                        : 
isCriticalSystemObject             : False
isDeleted                          : 
LastBadPasswordAttempt             : 
LastKnownParent                    : 
LastLogonDate                      : 
localPolicyFlags                   : 0
Location                           : NA/HQ/Building A
LockedOut                          : False
ManagedBy                          : CN=SQL Administrator 01,OU=UserAccounts,OU=Managed,DC=Globomantics,dc=local
MemberOf                           : {}
MNSLogonAccount                    : False
Modified                           : 3/16/2015 4:23:01 PM
modifyTimeStamp                    : 3/16/2015 4:23:01 PM
msDS-User-Account-Control-Computed : 0
Name                               : Globomantics-srv1
nTSecurityDescriptor               : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory                     : CN=Computer,CN=Schema,CN=Configuration,DC=Globomantics,dc=local
ObjectClass                        : computer
ObjectGUID                         : 828306a3-8ccd-410e-9537-e6616662c0b0
objectSid                          : S-1-5-21-41432690-3719764436-1984117282-1130
OperatingSystem                    : 
OperatingSystemHotfix              : 
OperatingSystemServicePack         : 
OperatingSystemVersion             : 
PasswordExpired                    : False
PasswordLastSet                    : 
PasswordNeverExpires               : False
PasswordNotRequired                : False
PrimaryGroup                       : CN=Domain Computers,CN=Users,DC=Globomantics,DC=local
primaryGroupID                     : 515
ProtectedFromAccidentalDeletion    : False
pwdLastSet                         : 0
SamAccountName                     : chi-srv1$
sAMAccountType                     : 805306369
sDRightsEffective                  : 0
ServiceAccount                     : {}
servicePrincipalName               : {MSOLAPSVC.3/CHI-SRV1.Globomantics.local:analyze, MSSQLSVC/chi-SRV1.Globomantics.local:1456}
ServicePrincipalNames              : {MSOLAPSVC.3/Globomantics-SRV1.Globomantics.COM:analyze, MSSQLSVC/Globomantics-SRV1.Globomantics.COM:1456}
SID                                : S-1-5-21-41432690-3719764436-1984117282-1130
SIDHistory                         : {}
TrustedForDelegation               : False
TrustedToAuthForDelegation         : False
UseDESKeyOnly                      : False
userAccountControl                 : 4096
userCertificate                    : {}
UserPrincipalName                  : 
uSNChanged                         : 36024
uSNCreated                         : 35966
whenChanged                        : 3/16/2015 4:23:01 PM
whenCreated                        : 3/16/2015 4:15:00 PM

This command gets a specific computer showing all the properties.

.EXAMPLE

PS C:\>Get-MyADComputer -Filter 'Name -like "Globomantics*"' -Properties IPv4Address | format-table Name,DNSHostName,IPv4Address -A
name          dnshostname                ipv4address
----          -----------                -----------
Globomantics-SRV1 Globomantics-SRV1.Globomantics.com 10.194.99.181
Globomantics-SRV2 Globomantics-SRV2.Globomantics.com 10.194.100.3
This command gets all the computers with a name starting by a particular string and showing the name, dns hostname and IPv4 address.

.EXAMPLE

PS C:\> $d = (Get-Date).AddDays(-90) 
PS C:\> Get-MyADComputer -Filter 'PasswordLastSet -ge $d' -Properties PasswordLastSet | format-table Name,PasswordLastSet
Name                                                        PasswordLastSet
----                                                        ---------------
Globomantics-SRV4                                               3/12/2015 6:40:37 PM
Globomantics-SRV5                                               3/12/2015 7:05:45 PM
This example gets all the computers that have changed their password in the last 90 days.

.EXAMPLE

PS C:\> Get-MyADComputer -LDAPFilter "(name=*laptop*)" -SearchBase "CN=Computers,DC=Globomantics,dc=local"
name
----
saradavi-laptop
jeffpr-laptop
This command gets the computer accounts in the location CN=Computers,DC=Globomantics,dc=local that are listed as laptops by using an LDAPFilter.

.EXAMPLE

PS C:\> Get-MyADComputer -Filter * -exclude "^CN=Computers"
This command gets all computer accounts except those in the Computers container.

.NOTES

This cmdlet does not work with AD LDS with its default schema.  By default AD LDS schema does not have a computer class, but if the schema is extended to include it, this cmdlet will work with LDS.

Version: 1.0

Learn more about PowerShell:
http://jdhitsolutions.com/blog/essential-powershell-resources/

  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************

.INPUTS

None or Microsoft.ActiveDirectory.Management.ADComputer

.OUTPUTS

Microsoft.ActiveDirectory.Management.ADComputer

.LINK
Get-ADComputer
Add-ADComputerServiceAccount
Get-ADMyComputerServiceAccount
New-ADComputer
Remove-ADComputer
Remove-ADComputerServiceAccount
Set-ADComputer

#>
[CmdletBinding(DefaultParameterSetName='Filter')]
 param(
     [Microsoft.ActiveDirectory.Management.ADAuthType]$AuthType,
      
     [ValidateNotNullOrEmpty()]
     [pscredential]
     [System.Management.Automation.CredentialAttribute()]$Credential, 

     [Parameter(ParameterSetName='Filter')]
     [Parameter(ParameterSetName='Exclude')]
     [ValidateNotNullOrEmpty()]
     [string]$Filter, 

     [Parameter(ParameterSetName='Identity', Mandatory, Position=0, ValueFromPipeline)]
     [ValidateNotNull()]
     [Microsoft.ActiveDirectory.Management.ADComputer]$Identity, 

     [Parameter(ParameterSetName='LdapFilter', Mandatory)]
     [ValidateNotNullOrEmpty()]
     [string]$LDAPFilter, 

     [Parameter(ParameterSetName='Identity')]
     [ValidateNotNullOrEmpty()]
     [string]$Partition, 

     [Alias('Property')]
     [ValidateNotNullOrEmpty()]
     [string[]]$Properties, 

     [Parameter(ParameterSetName='LdapFilter')]
     [Parameter(ParameterSetName='Filter')]
     [Parameter(ParameterSetName='Exclude')]
     [ValidateRange(0, 2147483647)]
     [ValidateNotNullOrEmpty()]
     [int]$ResultPageSize, 

     [Parameter(ParameterSetName='Filter')]
     [Parameter(ParameterSetName='LdapFilter')]
     [Parameter(ParameterSetName='Exclude')]
     [System.Nullable[int]]$ResultSetSize, 

     [Parameter(ParameterSetName='Filter')]
     [Parameter(ParameterSetName='LdapFilter')]
     [ValidateNotNull()]
     [string[]]$SearchBase, 

     [Parameter(ParameterSetName='Exclude')]
     [ValidateNotNull()]
     [regex]$Exclude, 
     
     [Parameter(ParameterSetName='LdapFilter')]
     [Parameter(ParameterSetName='Filter')]
     [Parameter(ParameterSetName='Exclude')]
     [ValidateNotNullOrEmpty()]
     [Microsoft.ActiveDirectory.Management.ADSearchScope]$SearchScope, 

     [ValidateNotNullOrEmpty()]
     [string]$Server
     ) 
 begin {
       Write-Verbose "Starting $($MyInvocation.Mycommand)"
       Write-Verbose "Using parameter set $($PSCmdlet.parameterSetName)"
       Write-Verbose ($PSBoundParameters | out-string)
       
       #if -Filter is not specified at run time insert the parameter
       #into $PSBoundParameters with a value of *

       if (-Not $PSBoundparameters.ContainsKey("Filter") -AND ($PSCmdlet.parametersetname -match "filter|exclude")) {
          Write-Verbose "Using default filter of *"
          $PSBoundparameters.Add("Filter","*")
       }

       #remove -Exclude since it is not part of the original parameter set
       if ($Exclude) {
         $PSBoundParameters.Remove("Exclude") | Out-Null
       }
 } #Begin
 
 process {
    #run Get-ADcomputer for each location in the $SearchBase parameter
    if ($SearchBase) {
     $results = foreach ($location in $SearchBase) {
        $PSBoundparameters.searchBase = $location
        Write-Verbose ($PSBoundparameters | Out-String)
        Write-Verbose "Searching $location"
        Get-ADComputer @PSBoundparameters
     }
    }
     else {
        Write-Verbose "Searching entire domain"
        $results = Get-ADComputer @PSBoundparameters
     }
 } #Process
 
 end {

    Write-Verbose "Found $($results.count) matching objects"
    #filter if excluding
    if ($Exclude) {
        Write-Verbose "Excluding results"
        $results.where({$_.distinguishedname -notmatch $exclude})
    }
    else {
        #Write all results to the pipeline
        $results
    }
    Write-Verbose "Ending $($MyInvocation.Mycommand)"  
 } #End
 
 
} #end function Get-MyADComputer

Now I can easily exclude a location:

get-myadcomputer -exclude "OU=Development"

Or search multiple locations:

get-myadcomputer -filter "name -like 'chi-*'" -SearchBase "OU=Servers,DC=globomantics,DC=local","CN=Computers,DC=Globomantics,DC=Local" -properties CanonicalName | Select CanonicalName
Searching multiple computers with Get-MyADComputer. (Image Credit: Jeff Hicks)
Searching multiple computers with Get-MyADComputer. (Image Credit: Jeff Hicks)

And since Get-ADUser is very similar to Get-ADComputer, I created a custom version, copying the changes from Get-MyADComputer.

#requires -version 4.0
#requires -module ActiveDirectory

<#
This is a copy of:

CommandType Name       ModuleName     
----------- ----       ----------     
Cmdlet      Get-ADUser ActiveDirectory

Created: 5/29/2015
Author : Jeff

this is a proxy version of Get-ADUser that supports
searching multiple locations. Filter parameter defaults to *.

  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************
#>


Function Get-MyADUser {
<#

.SYNOPSIS

A modified version of Get-ADUser to get one or more Active Directory users.


.DESCRIPTION

The Get-MyADUser cmdlet gets a user object or performs a search to retrieve multiple user objects.

The Identity parameter specifies the Active Directory user to get. You can identify a user by its distinguished name (DN), GUID, security identifier (SID), Security Accounts Manager (SAM) account name or name. You can also set the parameter to a user object variable, such as $<localUserObject> or pass a user object through the pipeline to the Identity parameter.

To search for and retrieve more than one user, use the Filter or LDAPFilter parameters. The Filter parameter uses the PowerShell Expression Language to write query strings for Active Directory. PowerShell Expression Language syntax provides rich type conversion support for value types received by the Filter parameter. For more information about the Filter parameter syntax, type Get-Help about_ActiveDirectory_Filter. If you have existing LDAP query strings, you can use the LDAPFilter parameter.

This cmdlet retrieves a default set of user object properties. To retrieve additional properties use the Properties parameter. For more information about the how to determine the properties for user objects, see the Properties parameter description.

NOTE: This version supports excluding by filtering on the distinguishedname. Or you can specify multiple search locations.


.PARAMETER AuthType

Specifies the authentication method to use. The acceptable values for this parameter are:&nbsp;

-- Negotiate or 0
-- Basic or 1

The default authentication method is Negotiate.

A Secure Sockets Layer (SSL) connection is required for the Basic authentication method.

.PARAMETER Credential

Specifies the user account credentials to use to perform this task. The default credentials are the credentials of the currently logged on user unless the cmdlet is run from an Active Directory PowerShell provider drive. If the cmdlet is run from such a provider drive, the account associated with the drive is the default.

To specify this parameter, you can type a user name, such as User1 or Domain01\User01 or you can specify a PSCredential object. If you specify a user name for this parameter, the cmdlet prompts for a password.

You can also create a PSCredential object by using a script or by using the Get-Credential cmdlet. You can then set the Credential parameter to the PSCredential object.

If the acting credentials do not have directory-level permission to perform the task, Active Directory PowerShell returns a terminating error.

.PARAMETER Filter

Specifies a query string that retrieves Active Directory objects. This string uses the PowerShell Expression Language syntax. The PowerShell Expression Language syntax provides rich type-conversion support for value types received by the Filter parameter. The syntax uses an in-order representation, which means that the operator is placed between the operand and the value. For more information about the Filter parameter, type Get-Help about_ActiveDirectory_Filter.

Syntax:

The following syntax uses Backus-Naur form to show how to use the PowerShell Expression Language for this parameter.

<filter>  ::= "{" <FilterComponentList> "}"

<FilterComponentList> ::= <FilterComponent> | <FilterComponent> <JoinOperator> <FilterComponent> | <NotOperator>  <FilterComponent>

<FilterComponent> ::= <attr> <FilterOperator> <value> | "(" <FilterComponent> ")"

<FilterOperator> ::= "-eq" | "-le" | "-ge" | "-ne" | "-lt" | "-gt"| "-approx" | "-bor" | "-band" | "-recursivematch" | "-like" | "-notlike"

<JoinOperator> ::= "-and" | "-or"

<NotOperator> ::= "-not"

<attr> ::= <PropertyName> | <LDAPDisplayName of the attribute>

<value>::= <compare this value with an <attr> by using the specified <FilterOperator>>

For a list of supported types for <value>, type Get-Help about_ActiveDirectory_ObjectModel.

Note: PowerShell wildcards other than *, such as ?, are not supported by the Filter syntax.

Note: To query using LDAP query strings, use the LDAPFilter parameter.

Note: The default behavior in this command is to return all user accounts.

.PARAMETER Identity

Specifies an Active Directory user object by providing one of the following property values. The identifier in parentheses is the LDAP display name for the attribute. The acceptable values for this parameter are:&nbsp;

-- A Distinguished Name
-- A GUID (objectGUID) 
-- A Security Identifier (objectSid) 
-- A SAM Account Name (sAMAccountName)

The cmdlet searches the default naming context or partition to find the object. If two or more objects are found, the cmdlet returns a non-terminating error.

This parameter can also get this object through the pipeline or you can set this parameter to an object instance.

.PARAMETER LDAPFilter

Specifies an LDAP query string that is used to filter Active Directory objects. You can use this parameter to run your existing LDAP queries. The Filter parameter syntax supports the same functionality as the LDAP syntax. For more information, see the Filter parameter description or type Get-Help about_ActiveDirectory_Filter.

.PARAMETER Partition

Specifies the distinguished name of an Active Directory partition. The distinguished name must be one of the naming contexts on the current directory server. The cmdlet searches this partition to find the object defined by the Identity parameter.

In many cases, a default value will be used for the Partition parameter if no value is specified.  The rules for determining the default value are given below.  Note that rules listed first are evaluated first and once a default value can be determined, no further rules will be evaluated.

In AD DS environments, a default value for Partition will be set in the following cases: 

-- If the Identity parameter is set to a distinguished name, the default value of Partition is automatically generated from this distinguished name.
-- If running cmdlets from an Active Directory provider drive, the default value of Partition is automatically generated from the current path in the drive. 
-- If none of the previous cases apply, the default value of Partition will be set to the default partition or naming context of the target domain.

In AD LDS environments, a default value for Partition will be set in the following cases:

-- If the Identity parameter is set to a distinguished name, the default value of Partition is automatically generated from this distinguished name. 
-- If running cmdlets from an Active Directory provider drive, the default value of Partition is automatically generated from the current path in the drive. 
-- If the target AD LDS instance has a default naming context, the default value of Partition will be set to the default naming context. To specify a default naming context for an AD LDS environment, set the msDS-defaultNamingContext property of the Active Directory directory service agent (DSA) object (nTDSDSA) for the AD LDS instance. 
-- If none of the previous cases apply, the Partition parameter will not take any default value.

.PARAMETER Properties

Specifies the properties of the output object to retrieve from the server. Use this parameter to retrieve properties that are not included in the default set.

Specify properties for this parameter as a comma-separated list of names. To display all of the attributes that are set on the object, specify * (asterisk).

To specify an individual extended property, use the name of the property. For properties that are not default or extended properties, you must specify the LDAP display name of the attribute.

To retrieve properties and display them for an object, you can use the Get-* cmdlet associated with the object and pass the output to the Get-Member cmdlet.

.PARAMETER ResultPageSize

Specifies the number of objects to include in one page for an Active Directory Domain Services query.

The default is 256 objects per page.

.PARAMETER ResultSetSize

Specifies the maximum number of objects to return for an Active Directory Domain Services query. If you want to receive all of the objects, set this parameter to $Null (null value). You can use Ctrl+C to stop the query and return of objects.

The default is $Null.

.PARAMETER SearchBase

Specifies an Active Directory path to search under.

When you run a cmdlet from an Active Directory provider drive, the default value of this parameter is the current path of the drive.

When you run a cmdlet outside of an Active Directory provider drive against an AD DS target, the default value of this parameter is the default naming context of the target domain.

When you run a cmdlet outside of an Active Directory provider drive against an AD LDS target, the default value is the default naming context of the target LDS instance if one has been specified by setting the msDS-defaultNamingContext property of the Active Directory directory service agent (DSA) object (nTDSDSA) for the AD LDS instance. If no default naming context has been specified for the target AD LDS instance, then this parameter has no default value.

When the value of the SearchBase parameter is set to an empty string and you are connected to a GC port, all partitions will be searched. If the value of the SearchBase parameter is set to an empty string and you are not connected to a GC port, an error will be thrown.

Note: You can specify multiple locations to search.

.PARAMETER SearchScope

Specifies the scope of an Active Directory search. The acceptable values for this parameter are:&nbsp;

-- Base or 0
-- OneLevel or 1
-- Subtree or 2

A Base query searches only the current path or object. A OneLevel query searches the immediate children of that path or object. A Subtree query searches the current path or object and all children of that path or object.

.PARAMETER Server

Specifies the Active Directory Domain Services instance to connect to, by providing one of the following values for a corresponding domain name or directory server. The service may be any of the following:  Active Directory Lightweight Domain Services, Active Directory Domain Services or Active Directory Snapshot instance.

Specify the Active Directory Domain Services instance in one of the following ways:  

-- Domain name values:

---- Fully qualified domain name
---- NetBIOS name

-- Directory server values: 

---- Fully qualified directory server name
---- NetBIOS name
---- Fully qualified directory server name and port

The default value for this parameter is determined by one of the following methods in the order that they are listed:

-- By using the Server value from objects passed through the pipeline
-- By using the server information associated with the Active Directory Domain ServicesWindows PowerShell provider drive, when the cmdlet runs in that drive
-- By using the domain of the computer running Windows PowerShell

.PARAMETER Exclude
A regular expression pattern, or even a simple string, that represents part or an entire Active Directory location. 
This will be used to filter out matching objects using their distinguished name.

.EXAMPLE
PS C:\> get-myaduser -SearchBase "OU=IT,OU=Departments,OU=Employees,DC=globomantics,dc=local","OU=Engineering,OU=Departments,OU=Employees,DC=globomantics,DC=local"

Find all user accounts in two different OUs. The Filter parameter defaults to *.
.EXAMPLE

PS C:\> Get-MyADUser -Filter * -SearchBase "OU=Finance,OU=UserAccounts,DC=Globomantics,DC=local"
This command gets all users under the container OU=Finance,OU=UserAccounts,DC=Globomantics,DC=local.

.EXAMPLE

PS C:\>Get-MyADUser -Filter 'Name -like "*SvcAccount"' | format-table Name,SamAccountName -Autosize
Name             SamAccountName
----             --------------
SQL01 SvcAccount SQL01
SQL02 SvcAccount SQL02
IIS01 SvcAccount IIS01
This command gets all users that have a name that ends with SvcAccount.

.EXAMPLE

PS C:\>Get-MyADUser -Identity GlenJohn -Properties *
Surname           : John
Name              : Glen John
UserPrincipalName : 
GivenName         : Glen
Enabled           : False
SamAccountName    : GlenJohn
ObjectClass       : user
SID               : S-1-5-21-2889043008-4136710315-2444824263-3544
ObjectGUID        : e1418d64-096c-4cb0-b903-ebb66562d99d
DistinguishedName : CN=Glen John,OU=NorthAmerica,OU=Sales,OU=UserAccounts,DC=Globomantics,DC=local
This command gets all properties of the user with samAccountName GlenJohn.

.EXAMPLE

PS C:\> Get-MyADUser -Filter {Name -eq "GlenJohn"} -SearchBase "DC=AppNC" -Properties mail -Server lds.Globomantics.com:50000
This command gets the user with name GlenJohn on the AD LDS instance.

.EXAMPLE
PS C:\> get-myaduser -filter "givenname -like 'joseph'" -Exclude "OU=Testing"

Find all user accounts with a first name of Joseph but exclude any in the Testing OU.
.NOTES

This cmdlet does not work with an Active Directory Snapshot.

Version: 1.0

Learn more about PowerShell:
http://jdhitsolutions.com/blog/essential-powershell-resources/

  ****************************************************************
  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
  ****************************************************************

.INPUTS

None or Microsoft.ActiveDirectory.Management.ADUser


.OUTPUTS

Microsoft.ActiveDirectory.Management.ADUser

.LINK
Get-ADUser
New-ADUser
Remove-ADUser
Set-ADUser

#>
[CmdletBinding(DefaultParameterSetName='Filter')]
Param(

    [Microsoft.ActiveDirectory.Management.ADAuthType]$AuthType,
    [ValidateNotNullOrEmpty()]
    [pscredential]
    [System.Management.Automation.CredentialAttribute()]$Credential,

    [Parameter(ParameterSetName='Filter')]
    [Parameter(ParameterSetName='Exclude')]
    [ValidateNotNullOrEmpty()]
    [string]$Filter,

    [Parameter(ParameterSetName='Identity', Mandatory, Position=0, ValueFromPipeline)]
    [ValidateNotNull()]
    [Microsoft.ActiveDirectory.Management.ADUser]$Identity,

    [Parameter(ParameterSetName='LdapFilter', Mandatory)]
    [ValidateNotNullOrEmpty()]
    [string]$LDAPFilter,

    [Parameter(ParameterSetName='Identity')]
    [ValidateNotNullOrEmpty()]
    [string]$Partition,

    [Alias('Property')]
    [ValidateNotNullOrEmpty()]
    [string[]]$Properties,

    [Parameter(ParameterSetName='LdapFilter')]
    [Parameter(ParameterSetName='Filter')]
    [Parameter(ParameterSetName='Exclude')]
    [ValidateRange(0, 2147483647)]
    [ValidateNotNullOrEmpty()]
    [int]$ResultPageSize,

    [Parameter(ParameterSetName='LdapFilter')]
    [Parameter(ParameterSetName='Filter')]
    [Parameter(ParameterSetName='Exclude')]
    [System.Nullable[int]]$ResultSetSize,

    [Parameter(ParameterSetName='Filter')]
    [Parameter(ParameterSetName='LdapFilter')]
    [ValidateNotNull()]
    [string[]]$SearchBase,

    [Parameter(ParameterSetName='Exclude')]
    [ValidateNotNull()]
    [regex]$Exclude, 

    [Parameter(ParameterSetName='Filter')]
    [Parameter(ParameterSetName='LdapFilter')]
    [Parameter(ParameterSetName='Exclude')]
    [ValidateNotNullOrEmpty()]
    [Microsoft.ActiveDirectory.Management.ADSearchScope]$SearchScope,

    [ValidateNotNullOrEmpty()]
    [string]$Server
)

Begin {

    Write-Verbose "Starting $($MyInvocation.Mycommand)"
    Write-Verbose "Using parameter set $($PSCmdlet.ParameterSetName)"
    Write-Verbose ($PSBoundParameters | out-string)

    #if -Filter is not specified at run time insert the parameter
    #into $PSBoundParameters with a value of *
       
    if (-Not $PSBoundparameters.ContainsKey("Filter") -AND ($PSCmdlet.parametersetname -match "filter|exclude")) {
        Write-verbose "Using default filter of *"
        $PSBoundparameters.Add("Filter","*")
    }
       
    #remove -Exclude since it is not part of the original parameter set
    if ($Exclude) {
        $PSBoundParameters.Remove("Exclude") | Out-Null
    }

} #begin

Process {

    #run Get-ADUser for each location in the $SearchBase parameter
    if ($SearchBase) {
     $results = foreach ($location in $SearchBase) {
        $PSBoundparameters.searchBase = $location
        Write-Verbose ($PSBoundparameters | Out-String)
        Write-Verbose "Searching $location"
        Get-ADUser @PSBoundparameters
     }
    }
     else {
        Write-Verbose "Searching entire domain"
        $results = Get-ADUser @PSBoundparameters
     }

} #process

End {
    Write-Verbose "Found $($results.count) matching objects"
    #filter if excluding
    if ($Exclude) {
        Write-Verbose "Excluding results"
        $results.where({$_.distinguishedname -notmatch $exclude})
    }
    else {
        #Write all results to the pipeline
        $results
    }
    Write-Verbose "Ending $($MyInvocation.Mycommand)"

} #end

} #end function Get-MyADUser

I can use this much the same way.

get-myaduser -Exclude "OU=Sample" -verb -Properties description -filter "surname -like 'hi*'"

I hope these articles give you the confidence to try creating some of your own tools. And even though what you see here looks like a lot of work, most of the code was created by my Copy-Command function. The amount of PowerShell that I had to actually write and test was much smaller.

As always, I welcome your comments.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply

Register for the Hybrid Identity Protection (HIP) Europe Conference!

Hybrid Identity Protection (HIP) Europe 2021 - Virtual Conference

Mobile workforces, cloud applications, and digitalization are changing every aspect of the modern enterprise. And with radical transformation come new business risks. Hybrid Identity Protection (HIP) is the premier educational forum for identity-centric practitioners. At the inaugural HIP Europe, join your local IAM experts and Microsoft MVPs to learn all the latest from the Hybrid Identity world.