PowerShell

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:
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.             *
  ****************************************************************
.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.

Sponsored Content

Passwords Haven’t Disappeared Yet

123456. Qwerty. Iloveyou. No, these are not exercises for people who are brand new to typing. Shockingly, they are among the most common passwords that end users choose in 2021. Research has found that the average business user must manually type out, or copy/paste, the credentials to 154 websites per month. We repeatedly got one question that surprised us: “Why would I ever trust a third party with control of my network?

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.

I also wrote the function to accept pipelined input although this probably only works with a small set so you can correlate the results.

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.

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

Live Webinar - Thursday, December 2nd! Active Directory Masterclass: AD Configuration Strategies for Stronger SecurityREGISTER NOW - Thursday, December 2, 2021 @ 1 pm ET

Active Directory (AD) is leveraged by over 90% of enterprises worldwide as the authentication and authorization hub of their IT infrastructure—but its inherent complexity leaves it prone to misconfigurations that can allow attackers to slip into your network and wreak havoc. 

Join this session with Microsoft MVP and MCT Sander Berkouwer, who will explore:

  • Whether you should upgrade your domain controllers to Windows Server
    2019 and beyond
  • Achieving mission impossible: updating DCs within 48 hours
  • How to disable legacy protocols and outdated compatibility options in
    Active Directory

Sponsored by: