Public/Get-UserOU.ps1

function Get-UserOU {
<#
.SYNOPSIS
    Returns a user's parent OU/container in multiple useful formats.
 
.DESCRIPTION
    Looks up an AD user and derives the parent OU/container they live in.
    Outputs a structured object with:
      - DistinguishedName (OU/Container DN)
      - CanonicalPath (domain.tld/Top/Child)
      - LDAPPath (LDAP://OU=Child,OU=Top,DC=domain,DC=tld)
      - ADDrivePath (AD:\OU=Child,OU=Top,DC=domain,DC=tld)
      - ContainerType ('OU' or 'CN')
      - LeafContainerName (e.g., 'Users' or 'Corp-Staff')
      - Domain, DomainDN, plus user identifiers for convenience
 
.PARAMETER User
    The user identity (sAMAccountName, UPN, DN, GUID, SID). Accepts pipeline input by value
    or by property name (User, Username, SamAccountName, UserPrincipalName, Identity, UPN).
 
.PARAMETER Server
    Optional domain controller to query (passed to Get-ADUser -Server).
 
.EXAMPLE
    Get-UserOU -User "cathy.chen"
 
.EXAMPLE
    'cathy.chen','alex.jones' | Get-UserOU
 
.EXAMPLE
    [pscustomobject]@{ Username='cathy.chen' }, [pscustomobject]@{ UPN='alex.jones@contoso.com' } | Get-UserOU
 
.OUTPUTS
    PSCustomObject (one per input user), suitable for piping/Export-Csv/Select-Object, etc.
#>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('Username','SamAccountName','UserPrincipalName','Identity','UPN')]
        [string]$User,

        [Parameter()]
        [string]$Server
    )

    begin {
        # Ensure AD cmdlets are available
        if (-not (Get-Command -Name Get-ADUser -ErrorAction SilentlyContinue)) {
            try { Import-Module ActiveDirectory -ErrorAction Stop }
            catch {
                throw "ActiveDirectory module not available. Install RSAT or import the module, then retry."
            }
        }

        function Convert-DNToCanonical {
            param([Parameter(Mandatory)][string]$Dn)
            $parts      = $Dn -split ','
            $domainPart = $parts | Where-Object { $_ -like 'DC=*' }
            $domain     = ($domainPart | ForEach-Object { $_.Substring(3) }) -join '.'

            $nonDomain  = $parts | Where-Object { $_ -notlike 'DC=*' } |
                          ForEach-Object { ($_ -split '=',2)[1] }

            if ($nonDomain.Count -gt 0) {
                $rev = @($nonDomain)
                [array]::Reverse($rev)
                if ([string]::IsNullOrWhiteSpace($domain)) {
                    ($rev -join '/')
                } else {
                    "$domain/$($rev -join '/')"
                }
            } else {
                $domain
            }
        }
    }

    process {
        try {
            $getParams = @{ Identity = $User; ErrorAction = 'Stop' }
            if ($Server) { $getParams.Server = $Server }

            $u = Get-ADUser @getParams -Properties DistinguishedName, CanonicalName, SamAccountName, UserPrincipalName

            $userDN = $u.DistinguishedName
            if (-not $userDN) {
                Write-Error "No DistinguishedName returned for '$User'." -ErrorAction Continue
                return
            }

            # Parent container DN (everything after the user's CN=... segment)
            $parentDN = ($userDN -split ',',2)[1]

            # Container type and leaf name (e.g., OU=Corp-Staff or CN=Users)
            $leafRDN         = $parentDN.Split(',')[0]
            $containerType   = ($leafRDN -like 'OU=*') ? 'OU' : 'CN'
            $leafName        = ($leafRDN -split '=',2)[1]

            # Domain pieces
            $domainDN        = ($parentDN -split ',') | Where-Object { $_ -like 'DC=*' } -join ','
            $domainName      = (($parentDN -split ',') | Where-Object { $_ -like 'DC=*' } | ForEach-Object { $_.Substring(3) }) -join '.'

            # Canonical form of the parent container
            $canonicalParent = Convert-DNToCanonical -Dn $parentDN

            # Output object
            [pscustomobject]@{
                User                  = $u.SamAccountName
                UserPrincipalName     = $u.UserPrincipalName
                UserDistinguishedName = $userDN

                ContainerType         = $containerType              # 'OU' or 'CN'
                LeafContainerName     = $leafName                   # e.g., 'Users' or 'Corp-Staff'

                DistinguishedName     = $parentDN                    # OU=...,DC=...
                CanonicalPath         = $canonicalParent             # domain.tld/Top/Child
                LDAPPath              = "LDAP://$parentDN"           # LDAP://OU=...,DC=...
                ADDrivePath           = "AD:\$parentDN"              # AD:\OU=...,DC=...

                Domain                = $domainName                 # contoso.com
                DomainDN              = $domainDN                   # DC=contoso,DC=com
            }
        }
        catch {
            Write-Error "Get-UserOU failed for '$User': $($_.Exception.Message)" -ErrorAction Continue
        }
    }
}