DirectoryService/Get-DSUser.ps1

<#
  .SYNOPSIS
    Get user objects in a given directory service.
  .DESCRIPTION
    Get user objects in a given directory service.
  .EXAMPLE
    C:\PS> <example usage>
    Explanation of what the example does
#>

function Get-DSUser {
    [CmdletBinding(DefaultParameterSetName='Current')]
    param(
        # Domain controller.
        [Parameter(ParameterSetName = 'Remote',
                   Mandatory = $true)]
        [string]
        $ComputerName,
        
        # Credentials to use connection.
        [Parameter(ParameterSetName = 'Remote',
                   Mandatory = $true)]
        [Parameter(ParameterSetName = 'Alternate',
                   Mandatory = $true)]
        [Management.Automation.PSCredential]
        [Management.Automation.CredentialAttribute()]
        $Credential = [Management.Automation.PSCredential]::Empty,
        
        [Parameter(Mandatory=$false,
                HelpMessage='Maximum number of Objects to pull from AD, limit is 1,000 .')]
        [int]
        $Limit = 1000,
        
        [Parameter(Mandatory=$false)]
        [string]
        $searchRoot = $null,
        
        [Parameter(Mandatory=$false)]
        [int]
        $PageSize = 100,
        
        [Parameter(Mandatory=$false,
                   HelpMessage='scope of a search as either a base, one-level, or subtree search, default is subtree.')]
        [ValidateSet('Subtree',
                     'OneLevel',
                     'Base')]
        [string]
        $SearchScope = 'Subtree',
        
        [Parameter(Mandatory=$false,
                   HelpMessage='Specifies the available options for examining security information of a directory object')]
        [ValidateSet('None',
                     'Dacl',
                     'Group',
                     'Owner',
                     'Sacl')]
        [string[]]
        $SecurityMask = 'None',
        
        [Parameter(Mandatory=$false,
                   HelpMessage='Whether the search should also return deleted objects that match the search filter.')]
        [switch]
        $Deleted,
        
        [Parameter(Mandatory=$false,
                   HelpMessage='Date to search for computers mofied on or after this date.')]
        [datetime]
        $ModifiedAfter,

        [Parameter(Mandatory=$false,
                   HelpMessage='Date to search for computers mofied on or before this date.')]
        [datetime]
        $ModifiedBefore,

        [Parameter(Mandatory=$false,
                   HelpMessage='Date to search for computers created on or after this date.')]
        [datetime]
        $CreatedAfter,

        [Parameter(Mandatory=$false,
                   HelpMessage='Date to search for computers created on or after this date.')]
        [datetime]
        $CreatedBefore,
        
        [Parameter(Mandatory=$false,
                   HelpMessage='Date to search for computers that logged on or after this date.')]
        [datetime]
        $LogOnAfter,

        [Parameter(Mandatory=$false,
            HelpMessage='Date to search for computers that logged on or after this date.')]
        [datetime]
        $LogOnBefore,
        
        [Parameter(Mandatory=$false,
                   HelpMessage='Name of user to match search on.')]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]
        $Name = $null,

        [Parameter(Mandatory=$false,
                   HelpMessage='List of only the properties to retrieve from AD.')]
        [string[]]
        $Property = $(),

        # (userAccountControl:1.2.840.113556.1.4.803:=1048574)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for accounts marked as sensitive and can not have their credentials delegated.')]
        [switch]
        $SensitiveNoDelegation,

        # (userAccountControl:1.2.840.113556.1.4.803:=32)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for accounts for wich no password is required.')]
        [switch]
        $NoPasswordRequired,
        
        # (|(accountExpires=0)(accountExpires=9223372036854775807))
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for accunts whos password does not expire.')]
        [switch]
        $PasswordNeverExpires,

        # (userAccountControl:1.2.840.113556.1.4.803:=2)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for disabled accounts.')]
        [switch]
        $Disabled,

        # (admincount>=1)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for accounts whose admincount field is not 0')]
        [switch]
        $AdminCount,

        # (servicePrincipalName=*)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for service accounts. Accounts with a Service Principal Field marked.')]
        [switch]
        $ServiceAccount,

        # (pwdLastSet=0)
        [Parameter(Mandatory=$false,
                   HelpMessage='Search for accounts that must change their password at next logon.')]
        [switch]
        $MustChangePassword,

        [Parameter(Mandatory=$false,
                   HelpMessage='Changes the logic order of all attribute filtering from instead of matching on all criteria to match on any.')]
        [switch]
        $ChangeLogicOrder
        
    )
    
    begin {

        $userAccountControlEnum = @{
            2 = 'ACCOUNT_DISABLED'
            16 = 'LOCKOUT'
            32 = 'PASSWD_NOTREQ'
            64 = 'PASSWD_CANT_CHANGE'
            128 = 'REVERSIBLE_ENCRYPTION'
            512 = 'NORMAL_ACCOUNT'
            65536 = 'DONT_EXPIRE_PASSWD'
            262144 = 'SMARTCARD_REQUIRED'
            524288 = 'TRUSTED_FOR_DELEGATION'
            1048576 = 'NOT_DELEGATED'
            2097152 = 'USE_DES_KEY_ONLY'
            4194304 = 'DONT_REQUIRE_PREAUTH'
            16777216 = 'TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION'
            33554432 = 'NO_AUTH_DATA_REQUIRED'
        }

        # Build filter
        $CompFilter = '(sAMAccountType=805306368)'
        $TempFilter = ''
        # Filter for modification time
        if ($ModifiedAfter -and $ModifiedBefore) {
            $TempFilter = "$($TempFilter)(whenChanged>=$($ModifiedAfter.ToString('yyyyMMddhhmmss.sZ')))(whenChanged<=$($ModifiedBefore.ToString('yyyyMMddhhmmss.sZ')))"}
        elseif ($ModifiedAfter) {
            $TempFilter = "$($TempFilter)(whenChanged>=$($ModifiedAfter.ToString('yyyyMMddhhmmss.sZ')))"}
        elseif ($ModifiedBefore) {
            $TempFilter = "$($TempFilter)(whenChanged<=$($ModifiedBefore.ToString('yyyyMMddhhmmss.sZ')))"}

        # Fileter for creation time
        if ($CreatedAfter -and $CreatedBefore) {
            $TempFilter = "$($TempFilter)(whencreated>=$($CreatedAfter.ToString('yyyyMMddhhmmss.sZ')))(whencreated<=$($CreatedBefore.ToString('yyyyMMddhhmmss.sZ')))"}
        elseif ($CreatedAfter) {
            $TempFilter = "$($TempFilter)(whencreated>=$($CreatedAfter.ToString('yyyyMMddhhmmss.sZ')))"}
        elseif ($CreatedBefore) {
            $TempFilter = "$($TempFilter)(whencreated<=$($CreatedBefore.ToString('yyyyMMddhhmmss.sZ')))"}
        
        # Fileter for loggon time
        if ($LogOnAfter -and $LogOnBefore) {
            $TempFilter = "$($TempFilter)(lastlogon>=$($LogOnAfter.ToFileTimeUTC()))(lastlogon<=$($LogOnBefore.ToFileTimeUTC()))"}
        elseif ($LogOnAfter) {
            $TempFilter = "$($TempFilter)(lastlogon>=$($LogOnAfter.ToFileTimeUTC()))"}
        elseif ($LogOnBefore) {
            $TempFilter = "$($TempFilter)(lastlogon<=$($LogOnBefore.ToFileTimeUTC()))"}

        if ($Name) {
            $TempFilter = "$($TempFilter)(name=$($Name))"}

        # Filter for hosts trusted for delegation.
        if ($TrustedForDelegation) {
            $TempFilter = "$($TempFilter)(userAccountControl:1.2.840.113556.1.4.803:=524288)"}

        # Filter for accounts that are marked as sensitive and can not be delegated.
        if ($SensitiveNoDelegation) {
            $TempFilter = "$($TempFilter)(userAccountControl:1.2.840.113556.1.4.803:=1048574)"}

        # Filter for accounts who do not requiere a password to logon.
        if ($NoPasswordRequired) {
            $TempFilter = "$($TempFilter)(userAccountControl:1.2.840.113556.1.4.803:=32)"}

        # Filter for accounts whose password does not expires.
        if ($PasswordNeverExpires) {
            $TempFilter = "$($TempFilter)(|(accountExpires=0)(accountExpires=9223372036854775807))"}

        # Filter for accounts that are disabled.
        if ($Disabled) {
            $TempFilter = "$($TempFilter)(userAccountControl:1.2.840.113556.1.4.803:=2)"}

        # Filter for accounts who have an adcmicount filed higher than 0.
        if ($AdminCount) {
            $TempFilter = "$($TempFilter)(admincount>=1)"}

        # Filter for accounts that have SPN set.
        if ($ServiceAccount) {
            $TempFilter = "$($TempFilter)(servicePrincipalName=*)"}

        # Filter whose users must change their passwords.
        if ($MustChangePassword) {
            $TempFilter = "$($TempFilter)(pwdLastSet=0)"}

        # Change the logic order of the filters.
        if ($TempFilter.length -gt 0) {
            if ($ChangeLogicOrder) {
                $CompFilter = "(&$($CompFilter)(|$($TempFilter)))"}
            else {
                $CompFilter = "(&$($CompFilter)(&$($TempFilter)))"} 
         } else {
            $CompFilter = "(&$($CompFilter))"
         }
        $culture = Get-Culture

    }
    
    process {

        # Main properties for objects.
        $props = @('name',
                    'displayname',
                    'adspath',
                    'distinguishedname',
                    'objectguid',
                    'objectsid',
                    'samaccountname',
                    'whenchanged',
                    'whencreated',
                    'pwdLastSet',
                    'lastlogon',
                    'lastlogoff',
                    'badpasswordtime'
                    'sidhistory',
                    'serviceprincipalname',
                    'useraccountcontrol',
                    'memberof',
                    'ntsecuritydescriptor',
                    'accountexpires')

        write-verbose -message "Executing search with filter $CompFilter"
        switch ($PSCmdlet.ParameterSetName) {
            'Remote' { 
                if ($searchRoot) {
                    $objSearcher = Get-DSDirectorySearcher -ComputerName $ComputerName -DistinguishedName $searchRoot -Credential $Credential -Filter $CompFilter

                } else {
                    $objSearcher = Get-DSDirectorySearcher -ComputerName $ComputerName -Credential $Credential -Filter $CompFilter
                }
            }
            'Alternate' {$objSearcher = Get-DSDirectorySearcher -Credential $Credential -Filter $CompFilter}
            'Current' {$objSearcher = Get-DSDirectorySearcher -Filter $CompFilter}
            Default {}
        }
        $objSearcher.SizeLimit = $Limit
        $objSearcher.PageSize = $PageSize
        $objSearcher.SearchScope = $SearchScope
        $objSearcher.Tombstone = $Deleted
        $objSearcher.SecurityMasks = [DirectoryServices.SecurityMasks]$SecurityMask

        # If properties specified add those to the searcher
        if ($Property -contains '*' -or $Property.Count -ne 0) {
            foreach ($prop in $Property) {
                $objSearcher.PropertiesToLoad.Add($prop.ToLower()) | Out-Null
            }
        } else {
            Write-Verbose -Message 'No properties specified.'
            foreach ($prop in $props) {
                $objSearcher.PropertiesToLoad.Add($prop.ToLower()) | Out-Null
            }
        }

        $objSearcher.findall() | ForEach-Object -Process {
            $objProps = [ordered]@{}
            [Collections.ArrayList]$currentProps = $_.Properties.PropertyNames
            foreach ($baseprop in $props) {
                if ($baseprop -in $currentprops) {
                    if ($baseprop -eq 'objectguid') {
                        $objProps['ObjectGuid'] = [guid]$_.properties."$($baseprop)"[0]
                        $currentProps.Remove($baseprop)
                    } elseif($baseprop -eq 'objectsid') {
                        $objProps['ObjectSid'] = "$(&{$sidobj = [byte[]]"$($_.Properties.objectsid)".split(' ');$sid = new-object System.Security.Principal.SecurityIdentifier $sidobj, 0; $sid.Value})"
                        $currentProps.Remove($baseprop)
                    } elseif($baseprop -eq 'lastlogontimestamp') {
                        $timeStamp = "$($_.Properties.lastlogontimestamp)"
                        $timeStampDate = [datetime]::FromFileTimeUtc($timeStamp)
                        $objProps['LastLogonTimeStamp'] = $timeStampDate
                        $currentProps.Remove($baseprop)
                    } elseif($baseprop -eq 'ntsecuritydescriptor') {
                        $secds = New-Object System.DirectoryServices.ActiveDirectorySecurity
                        $Desc = $_.Properties.ntsecuritydescriptor[0]
                        $secds.SetSecurityDescriptorBinaryForm($Desc)
                        $objProps['NTSecurityDescriptor'] = $secds
                        $currentProps.Remove($baseprop)
                    } elseif($baseprop -eq 'usercertificate') {
                        $certs = foreach ($cert in $_.Properties.usercertificate) {[Security.Cryptography.X509Certificates.X509Certificate2]$cert}
                        $objProps['UserCertificate'] = $certs
                        $currentProps.Remove($baseprop)
                    } elseif ($baseprop -eq 'accountexpires') {
                        Try
                        {
                            $exval = "$($_.properties.accountexpires[0])"
                            If (($exval -eq 0) -or ($exval -gt [DateTime]::MaxValue.Ticks))
                            {
                                $objProps['AccountExpires'] = '<Never>'
                                $currentProps.Remove($baseprop)
                            }
                            Else
                            {
                                $Date = [DateTime]$exval
                                $objProps['AccountExpires'] = $Date.AddYears(1600).ToLocalTime()
                                $currentProps.Remove($baseprop)
                            }
                            
                        }
                        catch
                        {
                            $objProps['AccountExpires'] = '<Never>'
                            $currentProps.Remove($baseprop)
                        }
                    }
                    elseif ($baseprop -eq 'pwdlastset')
                    {
                        $objProps.Add('PwdLastSet', [dateTime]::FromFileTime($_.properties.pwdlastset[0]))
                        $currentProps.Remove($baseprop)
                    }
                    elseif ($baseprop -eq 'lastlogon')
                    {
                        $objProps.Add('LastLogon', [dateTime]::FromFileTime($_.properties.lastlogon[0]))
                        $currentProps.Remove($baseprop)
                    }
                    elseif ($baseprop -eq 'badpasswordtime')
                    {
                        $objProps.Add('BadPasswordTime', [dateTime]::FromFileTime($_.properties.badpasswordtime[0]))
                        $currentProps.Remove($baseprop)
                    }
                
                    elseif ($baseprop -eq 'Useraccountcontrol')
                    {
                        $uac = $_.properties."$($baseprop)"[0]
                        $uacSet = @()
                        foreach ($val in $userAccountControlEnum.keys) {
                            if ([int]$uac -band $val) {
                                $uacSet += $userAccountControlEnum[$val]
                            }
                        }
                        $objProps[$culture.TextInfo.ToTitleCase($baseprop)] = $uacSet
                        $currentProps.Remove($baseprop)
                    }
                    else {
                        $objProps[$culture.TextInfo.ToTitleCase($baseprop)] = $_.properties."$($baseprop)"[0]
                    }

                    $currentProps.Remove($baseprop)
                }
            }
            foreach ($prop in $currentProps)
            {
                $objProps[$culture.TextInfo.ToTitleCase($prop)] = $_.properties."$($prop)"[0]
            }
            
            $compObj = [PSCustomObject]$objProps
            $compObj
        }
    }
    
    end {
    }
}