Test Active Directory User Accounts with PowerShell

It is probably no surprise that Active Directory is something that IT pros love to automate with PowerShell. Certainly, creating 100 new user accounts from a CSV file with PowerShell is pretty simple. Part of that process may be testing for an existing user account, which I’ll show you how to do in this article.

The easy approach is to run Get-ADUser and if there is an error because the user isn’t found, it is okay to proceed.

Try {
    $user =  Get-ADUser fbar -ErrorAction Stop
    $True
}
catch {
    $False
}

Another test you might want to run is to test a specific property. You might want to know whether or not it’s defined, or perhaps you need to test if it is a certain value. It isn’t too difficult to get the user account and property.

$user = get-aduser jfrost -Properties department
if ($user.department) {
  $True
}
else {
 $False
}

Given these needs, I decided to write a Test-ADUser function, which I am happy to share with you.

#requires -version 4.0
#requires -module ActiveDirectory
Function Test-ADUser {
<#
.Synopsis
Test an Active Directory User Account
.Description
This command will test if a given Active Directory user account exists. You can also use it to test a specific property. You can test if the property has any value (Exist), no value (NotExist) or is equal to a certain value.
.Parameter Identity
The name of an Active Directory user account. This should be either the samAccountName or the DistinguishedName.
.Example
PS C:\> Test-ADUser administrator
True
.Example
PS C:\> Test-ADUser jfrost -property Department -exist
True
.Notes
Last Updated: April 30, 2015
Version     : 0.9
Learn more about PowerShell:
Essential PowerShell Learning 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. * **************************************************************** .Link Get-ADUser .Inputs [string] .Outputs [Boolean] #> [cmdletbinding(DefaultParameterSetName="User")] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter an AD user name", ValueFromPipeline)] [ValidateNotNullorEmpty()] [Alias("Name")] [Microsoft.ActiveDirectory.Management.ADUser]$Identity, [string]$Server, [Alias("RunAs")] [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(ParameterSetName="Exist")] [Parameter(ParameterSetName="NotExist")] [Parameter(ParameterSetName="Value")] [ValidateNotNullorEmpty()] [string]$Property, [Parameter(ParameterSetName="Exist")] [switch]$Exist, [Parameter(ParameterSetName="NotExist")] [switch]$NotExist, [Parameter(ParameterSetName="Value")] [ValidateNotNullorEmpty()] [object]$Value ) Begin { Write-Verbose "Starting $($MyInvocation.Mycommand)" Write-Verbose "Detected parameter set $($PSCmdlet.ParameterSetName)" Write-Verbose ($PSBoundParameters | out-string) } #begin Process { #remove bound parameters that don't match up with Get-ADUser "Exist","NotExist","Value","Passthru" | foreach { $PSBoundParameters.Remove($_) | Out-Null } Write-Verbose "Searching for user $Identity" Try { $user = Get-ADuser @PSBoundparameters -ErrorAction Stop Write-Verbose "Found: $(($user | out-string).TrimEnd())" } Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { Write-Verbose "No user found exception" if ($PSCmdlet.ParameterSetName -eq "User") { $False } else { #user not found so all other tests are invalid Write-Warning "Failed to find user $Identity so tests are invalid." #Bail out Return } } Catch { #handle all other errors Throw $_ #bail out Return } Write-Verbose "Processing results for $Identity" Switch ($PSCmdlet.ParameterSetName) { "User" { If ($user) { $True } Else { #this should probably never be reached but just in case $False } } "Exist" { Write-Verbose "Testing if $property exists" if ($user.$property) { $True } else { $False } } "NotExist" { Write-Verbose "Testing if $property doesn't exist" if (-NOT ($user.$property)) { $True } else { $False } } "Value" { Write-Verbose "Testing $property for $value" if ($user.$property -eq $Value) { $True } else { $False } } Default { #this should probably never by reached Write-Warning "Could not determine validity of $Identity" } } #close Switch #reset $user just in case Remove-Variable user -ErrorAction SilentlyContinue } #process End { Write-Verbose "Ending $($MyInvocation.Mycommand)" } #end }

The function requires the ActiveDirectory module. If it isn’t running when you load the function, PowerShell will automatically load it. I’ve written the function using many of the same parameters as Get-ADUser because when it comes time to getting the user account, I can splat the $PSBoundParameters value to Get-ADUser, after first removing the parameters that don’t belong.

If you look at the param section, you’ll see that I am using parameter sets. The default parameter set tests if the user account exists or not. You can also specify a single user property, and perform one out of three tasks:

  • Test if it exists, meaning something is defined.
  • Test if it doesn’t exist, meaning it is empty or null
  • Test if the value is equal to something you specify.

Each of these scenarios is a separate parameter set. Notice that the property parameter belongs to multiple sets. I actually didn’t specify any parameter set for identity because that is part of all the sets. I suppose I could have explicitly stated that fact, however.
I’m using a try-catch statement when attempting to get the user account. You can have multiple catch statements to handle different types of errors. If Get-ADUser can’t find the user account that is an exception, that’s a good thing in this case. If that exception happens, then I know I can write $False to the pipeline. That’s why I have a catch block designated to handle not found exceptions. Any other types of errors, such invalid credentials, bad server name, or bad property name, will be handled by the other catch block. If something like that happens, then the function can’t test anything so the function displays a warning and bails out.
Assuming the user is found, the function finally uses a switch statement and does a different comparison depending on the parameter set name. Because switch by default attempts to process all possibilities, I could have included a break statement within each condition.

"Exist" {
        Write-Verbose "Testing if $property exists"
        if ($user.$property) {
            $Test = $True
        }
        else {
            $Test = $False
        }
Break  #Don't test anything else
    }

But my switch statement is so simple that any performance gains would be insignificant.
Once the function is loaded into my PowerShell session, I can easily test if users exist or if key properties meet certain criteria.
050115 2039 TestActiveD1
I also wrote the function to accept pipelined input although this probably only works with a small set so you can correlate the results.
050115 2039 TestActiveD2
If the user can’t be found, then the function doesn’t try to test properties.

As I was developing the function, I thought about how someone might use it, which is a key step to consider when building a PowerShell script or tool. Since the function only writes Boolean values to the pipeline, I thought maybe there might be other use cases. So I experimented with adding a –Passthru parameter to write the user account to the pipeline. But how do you handle something that isn’t found is exactly what this function is all about. I didn’t like the results so I pulled it.
I hope you’ll let me know what you think.