MyModule PowerShell · Active Directory Identity Multi-Domain

Get-MyUser

Retrieves Active Directory user information from any domain in the forest using dynamic DC resolution — no hardcoded server references. Returns a standardised PSCustomObject with 30+ operational attributes including Fine-Grained Password Policy resolution and replication metadata for last status change tracking.

Version1.0.0
Lines~165
ModuleMyModule
FileFunctions\AD\Get-MyUser.ps1
Depends OnGet-ADTargetServer

Parameters

ParameterTypeRequiredDescription
-Username [string] Yes · Pipeline Identity to search for. Accepts SamAccountName, UPN, email address, display name, DistinguishedName, or CanonicalName.
-DomainController [string] Optional Override the auto-resolved DC. Useful for targeting a specific server during troubleshooting or replication verification.
-GlobalCatalog [string] Optional Override the Global Catalog used for the initial forest-wide lookup. Auto-discovered in the current AD site if omitted. Append :3268 or let the function add the port automatically.
-PreferredSite [string] Optional Prefer a DC in this AD site when resolving the target domain controller. Falls back to any available writable DC if no DC exists in the specified site for the resolved domain.
-Properties [Array] Optional Bypasses the standard output object and returns a raw AD attribute selection via Select-Object. Useful for targeted attribute inspection or flexible pipeline use.

How It Works

Two-phase query design: The Global Catalog (port 3268) holds a partial replica of every domain in the forest — it is queried first to locate the object and derive the domain DNS name from the DistinguishedName. A second full query then runs against a writable DC in that domain to retrieve the complete attribute set, including attributes the GC does not replicate (msDS-ResultantPSO, all extensionAttributes, replication metadata).
Replication metadata: Get-ADReplicationAttributeMetadata is called against userAccountControl to surface LastStatusChangeDate and LastStatusChangeValue — allowing you to see exactly when an account was enabled or disabled, and which DC originated that change.

Output — PSCustomObject

Name[string]
UserPrincipalName[string]
SamAccountName[string]
DisplayName[string]
EmailAddress[string]
Department[string]
Title[string]
ExtensionAttribute2[string]
Description[string]
FGPPolicy[string]
Manager[string]
Enabled[bool]
LockedOut[bool]
LockoutTime[datetime]
PasswordExpired[bool]
PasswordNeverExpires[bool]
CannotChangePassword[bool]
PasswordLastSet[datetime]
AccountExpirationDate[datetime]
LastLogonDate[datetime]
WhenCreated[datetime]
DistinguishedName[string]
Domain[string]
ResolvedDC[string]
msExchRecipientDisplayType[int]
LastStatusChangeDate[datetime]
LastStatusChangeValue[string]

Examples

Example 1 — Basic lookup by SamAccountName
PowerShell
Get-MyUser -Username "jdoe"

Auto-discovers a GC, resolves the domain, queries the correct DC, and returns the full standardised output object.

Example 2 — Pipeline input for bulk lookups
PowerShell
"jdoe", "asmith", "john.doe@child2.corp.local" | Get-MyUser

Each identity is resolved independently. Mixed formats (SamAccountName, UPN, email) are all accepted in the same pipeline.

Example 3 — Site-aware DC targeting
PowerShell
Get-MyUser -Username "john.doe@child2.corp.local" -PreferredSite "Doha-Site"

Pins DC resolution to the Doha-Site AD site. Falls back to any writable DC in child2.corp.local if the site has none.

Example 4 — Raw property selection (bypass standard output)
PowerShell
Get-MyUser -Username "jdoe" -Properties DisplayName, Department, Title, extensionAttribute1

When -Properties is supplied the standard output object is bypassed entirely. The raw ADUser object is returned via Select-Object — useful for one-off attribute checks or feeding into other cmdlets.

Example 5 — Feed directly into Test-MyUserStatus
PowerShell
Get-MyUser -Username "jdoe" |
    Select-Object -ExpandProperty SamAccountName |
    Test-MyUserStatus -SkipCredentialTest

The two functions compose naturally via the pipeline. Get-MyUser resolves the identity; Test-MyUserStatus performs the health evaluation.

Full Source

PowerShell · Get-MyUser.ps1
function Get-MyUser {
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
        [string]$Username,

        [Parameter(Mandatory = $false)]
        [string]$DomainController,

        [Parameter(Mandatory = $false)]
        [string]$GlobalCatalog,

        [Parameter(Mandatory = $false)]
        [string]$PreferredSite,

        [Parameter(Mandatory = $false)]
        [Array]$Properties
    )

    Begin {}

    Process {

        # ── Step 1: Resolve the correct DC for this identity ─────────────────────────
        $resolveParams = @{ Identity = $Username }
        if ($GlobalCatalog) { $resolveParams['GlobalCatalog'] = $GlobalCatalog }
        if ($PreferredSite)  { $resolveParams['PreferredSite']  = $PreferredSite  }

        try {
            $resolved = Get-ADTargetServer @resolveParams -ErrorAction Stop
        }
        catch {
            Write-Error "Could not resolve a target server for '$Username'. $_"
            return
        }

        if (-not $resolved) {
            Write-Warning "No user found for '$Username'."
            return
        }

        $targetServer = if ($DomainController) { $DomainController } else { $resolved.DomainController }

        # ── Step 2: Full attribute retrieval from the resolved domain's DC ───────────
        # GC holds a partial attribute set only. Full DC query is required for
        # msDS-ResultantPSO, all extensionAttributes, and replication metadata.
        try {
            $User = Get-ADUser -Identity $resolved.SamAccountName `
                -Properties *, "msDS-UserPasswordExpiryTimeComputed", "msDS-ResultantPSO" `
                -Server $targetServer `
                -ErrorAction Stop
        }
        catch {
            Write-Error "Failed to retrieve full AD user object for '$($resolved.SamAccountName)' from '$targetServer'. $_"
            return
        }

        # ── Step 3: Replication metadata for account status change tracking ──────────
        try {
            $Metadata = Get-ADReplicationAttributeMetadata `
                -Object $User.DistinguishedName `
                -Server $targetServer `
                -ErrorAction Stop |
                Where-Object { $_.AttributeName -eq "userAccountControl" }
        }
        catch {
            Write-Warning "Could not retrieve replication metadata for '$($User.DistinguishedName)'. LastStatusChangeDate will be null."
            $Metadata = $null
        }

        # ── Step 4: Output ────────────────────────────────────────────────────────────
        if ($Properties) {
            return $User | Select-Object $Properties
        }

        [PSCustomObject]@{
            Name                       = $User.Name
            UserPrincipalName          = $User.UserPrincipalName
            SamAccountName             = $User.SamAccountName
            DisplayName                = $User.DisplayName
            EmailAddress               = $User.EmailAddress
            Department                 = $User.Department
            Title                      = $User.Title
            ExtensionAttribute2        = $User.extensionAttribute2
            Description                = $User.Description
            FGPPolicy                  = if ($User.'msDS-ResultantPSO') {
                                             ($User.'msDS-ResultantPSO' -split ',')[0] -replace '^CN=', ''
                                         } else { $null }
            Manager                    = if ($User.Manager) {
                                             ($User.Manager -split ',')[0] -replace '^CN=', ''
                                         } else { $null }
            Enabled                    = $User.Enabled
            LockedOut                  = $User.LockedOut
            LockoutTime                = $User.LockoutTime
            AccountLockoutTime         = $User.AccountLockoutTime
            PasswordExpired            = $User.PasswordExpired
            PasswordNeverExpires       = $User.PasswordNeverExpires
            CannotChangePassword       = $User.CannotChangePassword
            PasswordLastSet            = $User.PasswordLastSet
            AccountExpirationDate      = $User.AccountExpirationDate
            LastLogonDate              = $User.LastLogonDate
            WhenCreated                = $User.WhenCreated
            DistinguishedName          = $User.DistinguishedName
            Domain                     = $resolved.Domain
            ResolvedDC                 = $targetServer
            msExchRecipientDisplayType = $User.msExchRecipientDisplayType
            LastStatusChangeDate       = $Metadata.LastOriginatingChangeTime
            LastStatusChangeValue      = $Metadata.AttributeValue
        }
    }

    End {}
}