PSWinDocumentation.AD.psm1

Add-Type -TypeDefinition @"
public enum ActiveDirectory {
    // Forest Information - Section Main
    ForestInformation,
    ForestFSMO,
    ForestGlobalCatalogs,
    ForestOptionalFeatures,
    ForestUPNSuffixes,
    ForestSPNSuffixes,
    ForestSites,
    ForestSites1,
    ForestSites2,
    ForestSubnets,
    ForestSubnets1,
    ForestSubnets2,
    ForestSiteLinks,
    ForestDomainControllers,
 
    // Domain Information - Section Main
 
    DomainRootDSE,
    DomainRIDs,
    DomainAuthenticationPolicies, // Not yet tested
    DomainAuthenticationPolicySilos, // Not yet tested
    DomainCentralAccessPolicies, // Not yet tested
    DomainCentralAccessRules, // Not yet tested
    DomainClaimTransformPolicies, // Not yet tested
    DomainClaimTypes, // Not yet tested
    DomainFineGrainedPolicies,
    DomainFineGrainedPoliciesUsers,
    DomainFineGrainedPoliciesUsersExtended,
    DomainGUIDS,
    DomainDNSSRV,
    DomainDNSA,
    DomainInformation,
    DomainControllers,
    DomainFSMO,
    DomainDefaultPasswordPolicy,
    DomainGroupPolicies,
    DomainGroupPoliciesDetails,
    DomainGroupPoliciesACL,
    DomainOrganizationalUnits,
    DomainOrganizationalUnitsBasicACL,
    DomainOrganizationalUnitsExtended,
    DomainContainers,
    DomainTrustsClean,
    DomainTrusts,
 
    DomainBitlocker,
    DomainLAPS,
 
    // Domain Information - Group Data
    DomainGroupsFullList, // Contains all data
 
    DomainGroups,
    DomainGroupsMembers,
    DomainGroupsMembersRecursive,
 
    DomainGroupsSpecial,
    DomainGroupsSpecialMembers,
    DomainGroupsSpecialMembersRecursive,
 
    DomainGroupsPriviliged,
    DomainGroupsPriviligedMembers,
    DomainGroupsPriviligedMembersRecursive,
 
    // Domain Information - User Data
    DomainUsersFullList, // Contains all data
    DomainUsers,
    DomainUsersCount,
    DomainUsersAll,
    DomainUsersSystemAccounts,
    DomainUsersNeverExpiring,
    DomainUsersNeverExpiringInclDisabled,
    DomainUsersExpiredInclDisabled,
    DomainUsersExpiredExclDisabled,
    DomainAdministrators,
    DomainAdministratorsRecursive,
    DomainEnterpriseAdministrators,
    DomainEnterpriseAdministratorsRecursive,
 
    // Domain Information - Computer Data
    DomainComputersFullList, // Contains all data
    DomainComputersAll,
    DomainComputersAllCount,
    DomainComputers,
    DomainComputersCount,
    DomainServers,
    DomainServersCount,
    DomainComputersUnknown,
    DomainComputersUnknownCount,
 
    // This requires DSInstall PowerShell Module
    DomainPasswordDataUsers, // Gathers users data and their passwords
    DomainPasswordDataPasswords, // Compares Users Password with File
    DomainPasswordDataPasswordsHashes, // Compares Users Password with File HASH
    DomainPasswordClearTextPassword, // include both enabled / disabled accounts
    DomainPasswordClearTextPasswordEnabled, // include only enabled
    DomainPasswordClearTextPasswordDisabled, // include only disabled
    DomainPasswordLMHash,
    DomainPasswordEmptyPassword,
    DomainPasswordWeakPassword,
    DomainPasswordWeakPasswordEnabled,
    DomainPasswordWeakPasswordDisabled,
    DomainPasswordWeakPasswordList, // Password List from file..
    DomainPasswordDefaultComputerPassword,
    DomainPasswordPasswordNotRequired,
    DomainPasswordPasswordNeverExpires,
    DomainPasswordAESKeysMissing,
    DomainPasswordPreAuthNotRequired,
    DomainPasswordDESEncryptionOnly,
    DomainPasswordDelegatableAdmins,
    DomainPasswordDuplicatePasswordGroups,
    DomainPasswordHashesWeakPassword,
    DomainPasswordHashesWeakPasswordEnabled,
    DomainPasswordHashesWeakPasswordDisabled,
    DomainPasswordStats,
}
"@

function Get-ADObjectFromDistingusishedName {
    [CmdletBinding()]
    param (
        [string[]] $DistinguishedName,
        [Object[]] $ADCatalog,
        [string] $Type = '',
        [string] $Splitter # ', ' # Alternative for example [System.Environment]::NewLine
    )
    if ($DistinguishedName -eq $null) {
        return
    }
    $FoundObjects = foreach ($Catalog in $ADCatalog) {
        foreach ($Object in $DistinguishedName) {
            $ADObject = $Catalog | & { process { if ($_.DistinguishedName -eq $Object ) { $_ } } }  #| Where-Object { $_.DistinguishedName -eq $Object }
            if ($ADObject) {
                if ($Type -eq '') {
                    #Write-Verbose 'Get-ADObjectFromDistingusishedName - Whole object'
                    $ADObject
                } else {
                    #Write-Verbose 'Get-ADObjectFromDistingusishedName - Part of object'
                    $ADObject.$Type
                }
            }
        }
    }
    if ($Splitter) {
        return ($FoundObjects | Sort-Object) -join $Splitter
    } else {
        return $FoundObjects | Sort-Object
    }
}
function Get-DomainFineGrainedPoliciesUsersExtended {
    param(
        $DomainFineGrainedPolicies,
        $DomainUsersFullList,
        $DomainGroupsFullList,
        [string] $Domain

    )
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended"
    $PolicyUsers = @(
        foreach ($Policy in $DomainFineGrainedPolicies) {
            $Users = foreach ($U in $Policy.'Applies To') {
                Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $U
            }
            foreach ($User in $Users) {
                [pscustomobject][ordered] @{
                    'Policy Name'                       = $Policy.Name
                    Name                                = $User.Name
                    SamAccountName                      = $User.SamAccountName
                    Type                                = $User.ObjectClass
                    SID                                 = $User.SID
                    'High Privileged Group'             = 'N/A'
                    'Display Name'                      = $User.DisplayName
                    'Member Name'                       = $Member.Name
                    'User Principal Name'               = $User.UserPrincipalName
                    'Sam Account Name'                  = $User.SamAccountName
                    'Email Address'                     = $User.EmailAddress
                    'PasswordExpired'                   = $User.PasswordExpired
                    'PasswordLastSet'                   = $User.PasswordLastSet
                    'PasswordNotRequired'               = $User.PasswordNotRequired
                    'PasswordNeverExpires'              = $User.PasswordNeverExpires
                    'Enabled'                           = $User.Enabled
                    'MemberSID'                         = $Member.SID.Value
                    'Manager'                           = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $User.Manager).Name
                    'ManagerEmail'                      = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $User.Manager).EmailAddress
                    'DateExpiry'                        = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") # -Verbose
                    "DaysToExpire"                      = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($User."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate"             = $User.AccountExpirationDate
                    "AccountLockoutTime"                = $User.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $User.AllowReversiblePasswordEncryption
                    "BadLogonCount"                     = $User.BadLogonCount
                    "CannotChangePassword"              = $User.CannotChangePassword
                    "CanonicalName"                     = $User.CanonicalName
                    'Given Name'                        = $User.GivenName
                    'Surname'                           = $User.Surname
                    "Description"                       = $User.Description
                    "DistinguishedName"                 = $User.DistinguishedName
                    "EmployeeID"                        = $User.EmployeeID
                    "EmployeeNumber"                    = $User.EmployeeNumber
                    "LastBadPasswordAttempt"            = $User.LastBadPasswordAttempt
                    "LastLogonDate"                     = $User.LastLogonDate
                    "Created"                           = $User.Created
                    "Modified"                          = $User.Modified
                    "Protected"                         = $User.ProtectedFromAccidentalDeletion
                    "Domain"                            = $Domain
                }
            }
            $Groups = foreach ($U in $Policy.'Applies To') {
                Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainGroupsFullList -DistinguishedName $U
            }
            foreach ($Group in $Groups) {
                $GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.SID -Recursive
                foreach ($Member in $GroupMembership) {
                    $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Member.DistinguishedName)
                    [pscustomobject][ordered] @{
                        'Policy Name'                       = $Policy.Name
                        Name                                = $Group.Name
                        SamAccountName                      = $Group.SamAccountName
                        Type                                = $Group.ObjectClass
                        SID                                 = $Group.SID
                        'High Privileged Group'             = if ($Group.adminCount -eq 1) { $True } else { $False }
                        'Display Name'                      = $Object.DisplayName
                        'Member Name'                       = $Member.Name
                        'User Principal Name'               = $Object.UserPrincipalName
                        'Sam Account Name'                  = $Object.SamAccountName
                        'Email Address'                     = $Object.EmailAddress
                        'PasswordExpired'                   = $Object.PasswordExpired
                        'PasswordLastSet'                   = $Object.PasswordLastSet
                        'PasswordNotRequired'               = $Object.PasswordNotRequired
                        'PasswordNeverExpires'              = $Object.PasswordNeverExpires
                        'Enabled'                           = $Object.Enabled
                        'MemberSID'                         = $Member.SID.Value
                        'Manager'                           = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Object.Manager).Name
                        'ManagerEmail'                      = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Object.Manager).EmailAddress
                        'DateExpiry'                        = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") # -Verbose
                        "DaysToExpire"                      = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                        "AccountExpirationDate"             = $Object.AccountExpirationDate
                        "AccountLockoutTime"                = $Object.AccountLockoutTime
                        "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                        "BadLogonCount"                     = $Object.BadLogonCount
                        "CannotChangePassword"              = $Object.CannotChangePassword
                        "CanonicalName"                     = $Object.CanonicalName
                        'Given Name'                        = $Object.GivenName
                        'Surname'                           = $Object.Surname
                        "Description"                       = $Object.Description
                        "DistinguishedName"                 = $Object.DistinguishedName
                        "EmployeeID"                        = $Object.EmployeeID
                        "EmployeeNumber"                    = $Object.EmployeeNumber
                        "LastBadPasswordAttempt"            = $Object.LastBadPasswordAttempt
                        "LastLogonDate"                     = $Object.LastLogonDate
                        "Created"                           = $Object.Created
                        "Modified"                          = $Object.Modified
                        "Protected"                         = $Object.ProtectedFromAccidentalDeletion
                        "Domain"                            = $Domain
                    }
                }
            }
        }
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended Time: $EndTime"
    return $PolicyUsers
}
function Get-WinADAccounts {
    [CmdletBinding()]
    param(
        [Object] $UserNameList,
        [Object[]] $ADCatalog
    )
    $Accounts = New-ArrayList
    foreach ($User in $UserNameList) {
        foreach ($Catalog in $ADCatalog) {
            $Element = $Catalog | & { process { if ($_.SamAccountName -eq $User ) { $_ } } }  #| Where-Object { $_.SamAccountName -eq $User }
            Add-ToArrayAdvanced -Element $Element -List $Accounts -SkipNull
        }
    }
    return $Accounts
}
function Get-WinADDomainBitlocker {
    param(
        [string] $Domain,
        [Array] $Computers
    )
    $Properties = @(
        'Name',
        'OperatingSystem',
        'DistinguishedName'
    )
    [DateTime] $CurrentDate = Get-Date

    if ($null -eq $Computers) {
        $Computers = Get-ADComputer -Filter * -Properties $Properties -Server $Domain
    }
    foreach ($Computer in $Computers) {
        $Bitlockers = Get-ADObject -Filter 'objectClass -eq "msFVE-RecoveryInformation"' -SearchBase $Computer.DistinguishedName -Properties 'WhenCreated', 'msFVE-RecoveryPassword' #| Sort-Object whenCreated -Descending #| Select-Object whenCreated, msFVE-RecoveryPassword
        foreach ($Bitlocker in $Bitlockers) {
            [PSCustomObject] @{
                'Name'                        = $Computer.Name
                'Operating System'            = $Computer.'OperatingSystem'
                'Bitlocker Recovery Password' = $Bitlocker.'msFVE-RecoveryPassword'
                'Bitlocker When'              = $Bitlocker.WhenCreated
                'DistinguishedName'           = $Computer.'DistinguishedName'
            }
        }
    }
}
function Get-WinADDomainComputersFullList {
    [cmdletbinding()]
    param(
        [string] $Domain,
        [Array] $ForestSchemaComputers
    )
    Write-Verbose "Getting domain information - $Domain DomainComputersFullList"
    $TimeUsers = Start-TimeLog

    if ($Extended) {
        [string] $Properties = '*'
    } else {
        [string[]] $Properties = @(
            'SamAccountName', 'Enabled', 'OperatingSystem',
            'PasswordLastSet', 'IPv4Address', 'IPv6Address', 'Name', 'DNSHostName',
            'ManagedBy', 'OperatingSystemVersion', 'OperatingSystemHotfix',
            'OperatingSystemServicePack' , 'PasswordNeverExpires',
            'PasswordNotRequired', 'UserPrincipalName',
            'LastLogonDate', 'LockedOut', 'LogonCount',
            'CanonicalName', 'SID', 'Created', 'Modified',
            'Deleted', 'MemberOf'
            if ($ForestSchemaComputers.Name -contains 'ms-Mcs-AdmPwd') {
                'ms-Mcs-AdmPwd'
                'ms-Mcs-AdmPwdExpirationTime'
            }
        )
    }
    [string[]] $ExcludeProperty = '*Certificate', 'PropertyNames', '*Properties', 'PropertyCount', 'Certificates', 'nTSecurityDescriptor'

    Get-ADComputer -Server $Domain -Filter * -ResultPageSize 500000 -Properties $Properties -ErrorAction SilentlyContinue #| Select-Object -Property $Properties -ExcludeProperty $ExcludeProperty

    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainComputersFullList Time: $EndUsers"
}
function Get-WinADDomainFSMO {
    [CmdletBinding()]
    param(
        [string] $Domain,
        $DomainInformation
    )
    Write-Verbose "Getting domain information - $Domain DomainFSMO"
    $Time = Start-TimeLog
    # required for multiple use cases FSMO/DomainTrusts
    [ordered] @{
        'PDC Emulator'          = $DomainInformation.PDCEmulator
        'RID Master'            = $DomainInformation.RIDMaster
        'Infrastructure Master' = $DomainInformation.InfrastructureMaster
    }

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainFSMO Time: $EndTime"
}
function Get-WinADDomainGroupsFullList {
    [CmdletBinding()]
    param(
        [string] $Domain
    )
    Write-Verbose "Getting domain information - $Domain DomainGroupsFullList"
    $TimeUsers = Start-TimeLog

    [string[]] $Properties = '*'
    [string[]] $ExcludeProperty = '*Certificate', 'PropertyNames', '*Properties', 'PropertyCount', 'Certificates', 'nTSecurityDescriptor'

    Get-ADGroup -Server $Domain -Filter * -ResultPageSize 500000 -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExcludeProperty

    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGroupsFullList Time: $EndUsers"
}
function Get-DomainGroupsPriviliged {
    [cmdletbinding()]
    param(
        $DomainInformation,
        $DomainGroups
    )
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainGroupsPriviliged"
    $PrivilegedGroupsSID = "S-1-5-32-544", "S-1-5-32-548", "S-1-5-32-549", "S-1-5-32-550", "S-1-5-32-551", "S-1-5-32-552", "S-1-5-32-556", "S-1-5-32-557", "S-1-5-32-573", "S-1-5-32-578", "S-1-5-32-580", "$($DomainInformation.DomainSID)-512", "$($DomainInformation.DomainSID)-518", "$($DomainInformation.DomainSID)D-519", "$($DomainInformation.DomainSID)-520"
    $DomainGroups | Where-Object { $PrivilegedGroupsSID -contains $_.'Group SID' }

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGroupsPriviliged Time: $EndTime"
}
function Get-WinADDomainGUIDs {
    [cmdletbinding()]
    param(
        [string] $Domain,
        [Microsoft.ActiveDirectory.Management.ADEntity] $RootDSE
    )
    $Time = Start-TimeLog
    if ($null -eq $RootDSE) {
        $RootDSE = Get-ADRootDSE -Server $Domain
    }
    Write-Verbose "Getting domain information - $Domain DomainGUIDS"
    <#
    $GUID = @{ }
    Get-ADObject -SearchBase $RootDSE.schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID | ForEach-Object {
        if ($GUID.Keys -notcontains $_.schemaIDGUID ) {
            $GUID.add([System.GUID]$_.schemaIDGUID, $_.name)
        }
    }
    Get-ADObject -SearchBase "CN=Extended-Rights,$($RootDSE.configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID | ForEach-Object {
        if ($GUID.Keys -notcontains $_.rightsGUID ) {
            $GUID.add([System.GUID]$_.rightsGUID, $_.name)
        }
    }
 
#>

    $GUID = @{}
    $Schema = Get-ADObject -SearchBase $RootDSE.schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID
    foreach ($S in $Schema) {
        if ($GUID.Keys -notcontains $S.schemaIDGUID ) {
            $GUID.add([System.GUID]$S.schemaIDGUID, $S.name)
        }
    }

    $Extended = Get-ADObject -SearchBase "CN=Extended-Rights,$($RootDSE.configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID
    foreach ($S in $Extended) {
        if ($GUID.Keys -notcontains $S.rightsGUID ) {
            $GUID.add([System.GUID]$S.rightsGUID, $S.name)
        }
    }
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainGUIDS Time: $EndTime"
    return $GUID
}
function Get-WinADDomainInformation {
    [CmdletBinding()]
    param (
        [string] $Domain,
        [Object] $TypesRequired,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes,
        [switch] $Extended,
        [Array] $ForestSchemaComputers,
        [Array] $ForestSchemaUsers
    )
    if ([string]::IsNullOrEmpty($Domain)) {
        Write-Warning 'Get-WinADDomainInformation - $Domain parameter is empty. Try your domain name like ad.evotec.xyz. Skipping for now...'
        return
    }
    if ($null -eq $TypesRequired) {
        Write-Verbose 'Get-WinADDomainInformation - TypesRequired is null. Getting all.'
        $TypesRequired = Get-Types -Types ([ActiveDirectory])
    } # Gets all types
    $TimeToGenerate = Start-TimeLog

    # this is required to make sure certain properties are used in domain, such as LAPS, EXCHANGE and so on.
    # this prevents errors of asking for wrong property
    if ($null -eq $ForestSchemaComputers) {
        $ForestSchemaComputers = Get-WinADForestSchemaPropertiesComputers
    }
    if ($null -eq $ForestSchemaUsers) {
        $ForestSchemaUsers = Get-WinADForestSchemaPropertiesUsers
    }

    #$CurrentDate = Get-Date

    $Data = [ordered] @{ }
    $Data.DomainRootDSE = Get-WinADRootDSE -Domain $Domain
    Write-Verbose "Getting domain information - $Domain DomainInformation"
    $Data.DomainInformation = $(Get-ADDomain -Server $Domain)

    if ($null -eq $Data.DomainInformation -or $null -eq $Data.DomainRootDSE) {

    }

    $Data.DomainGroupsFullList = Get-WinADDomainGroupsFullList -Domain $Domain
    $Data.DomainUsersFullList = Get-WinADDomainUsersFullList -Domain $Domain -Extended:$Extended -ForestSchemaUsers $ForestSchemaUsers
    $Data.DomainComputersFullList = Get-WinADDomainComputersFullList -Domain $Domain -ForestSchemaComputers $ForestSchemaComputers

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainComputersAll,
            [ActiveDirectory]::DomainComputersAllCount,
            [ActiveDirectory]::DomainServers,
            [ActiveDirectory]::DomainServersCount,
            [ActiveDirectory]::DomainComputers,
            [ActiveDirectory]::DomainComputersCount,
            [ActiveDirectory]::DomainComputersUnknown,
            [ActiveDirectory]::DomainComputersUnknownCount
        )) {
        Write-Verbose "Getting domain information - $Domain DomainComputersAll"
        $Data.DomainComputersAll = $Data.DomainComputersFullList | Select-Object SamAccountName, Enabled, OperatingSystem, PasswordLastSet, IPv4Address, IPv6Address, Name, DNSHostName, ManagedBy, OperatingSystemVersion, OperatingSystemHotfix, OperatingSystemServicePack , PasswordNeverExpires, PasswordNotRequired, UserPrincipalName, LastLogonDate, LockedOut, LogonCount, CanonicalName, SID, Created, Modified, Deleted, MemberOf
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersAllCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersAllCount"
        $Data.DomainComputersAllCount = $Data.DomainComputersAll | Group-Object -Property OperatingSystem | Select-Object @{ L = 'System Name'; Expression = { if ($_.Name -ne '') { $_.Name } else { 'Unknown' } } } , @{ L = 'System Count'; Expression = { $_.Count } }
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainServers) {
        Write-Verbose "Getting domain information - $Domain DomainServers"
        $Data.DomainServers = $Data.DomainComputersAll  | & { process { if ($_.OperatingSystem -like 'Windows Server*') { $_ } } } #| Where-Object { $_.OperatingSystem -like 'Windows Server*' }
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainServersCount) {
        Write-Verbose "Getting domain information - $Domain DomainServersCount"
        $Data.DomainServersCount = $Data.DomainServers | Group-Object -Property OperatingSystem | Select-Object @{ L = 'System Name'; Expression = { if ($_.Name -ne '') { $_.Name } else { 'N/A' } } } , @{ L = 'System Count'; Expression = { $_.Count } }
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputers) {
        Write-Verbose "Getting domain information - $Domain DomainComputers"
        $Data.DomainComputers = $Data.DomainComputersAll | & { process { if ($_.OperatingSystem -notlike 'Windows Server*' -and $null -ne $_.OperatingSystem) { $_ } } }   # | Where-Object { $_.OperatingSystem -notlike 'Windows Server*' -and $_.OperatingSystem -ne $null }
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersCount"
        $Data.DomainComputersCount = $Data.DomainComputers | Group-Object -Property OperatingSystem | Select-Object @{ L = 'System Name'; Expression = { if ($_.Name -ne '') { $_.Name } else { 'N/A' } } } , @{ L = 'System Count'; Expression = { $_.Count } }
    }

    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersUnknown) {
        Write-Verbose "Getting domain information - $Domain DomainComputersUnknown"
        $Data.DomainComputersUnknown = $Data.DomainComputersAll | & { process { if ( $null -eq $_.OperatingSystem ) { $_ } } } # | Where-Object { $_.OperatingSystem -eq $null }
    }
    if ($TypesRequired -contains [ActiveDirectory]::DomainComputersUnknownCount) {
        Write-Verbose "Getting domain information - $Domain DomainComputersUnknownCount"
        $Data.DomainComputersUnknownCount = $Data.DomainComputersUnknown | Group-Object -Property OperatingSystem | Select-Object @{ L = 'System Name'; Expression = { if ($_.Name -ne '') { $_.Name } else { 'Unknown' } } } , @{ L = 'System Count'; Expression = { $_.Count } }
    }

    if ($TypesRequired -contains [ActiveDirectory]::DomainRIDs) {
        $Data.DomainRIDs = Get-WinADDomainRIDs -DomainInformation $Data.DomainInformation -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired @([ActiveDirectory]::DomainGUIDS, [ActiveDirectory]::DomainOrganizationalUnitsBasicACL, [ActiveDirectory]::DomainOrganizationalUnitsExtended)) {
        $Data.DomainGUIDS = Get-WinADDomainGUIDs -RootDSE $Data.DomainRootDSE -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAuthenticationPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainAuthenticationPolicies"
        $Data.DomainAuthenticationPolicies = $(Get-ADAuthenticationPolicy -Server $Domain -LDAPFilter '(name=AuthenticationPolicy*)')
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAuthenticationPolicySilos)) {
        Write-Verbose "Getting domain information - $Domain DomainAuthenticationPolicySilos"
        $Data.DomainAuthenticationPolicySilos = $(Get-ADAuthenticationPolicySilo -Server $Domain -Filter 'Name -like "*AuthenticationPolicySilo*"')
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainCentralAccessPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainCentralAccessPolicies"
        $Data.DomainCentralAccessPolicies = $(Get-ADCentralAccessPolicy -Server $Domain -Filter * )
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainCentralAccessRules)) {
        Write-Verbose "Getting domain information - $Domain DomainCentralAccessRules"
        $Data.DomainCentralAccessRules = $(Get-ADCentralAccessRule -Server $Domain -Filter * )
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainClaimTransformPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainClaimTransformPolicies"
        $Data.DomainClaimTransformPolicies = $(Get-ADClaimTransformPolicy -Server $Domain -Filter * )
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainClaimTypes)) {
        Write-Verbose "Getting domain information - $Domain DomainClaimTypes"
        $Data.DomainClaimTypes = $(Get-ADClaimType -Server $Domain -Filter * )
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainDNSSRV, [ActiveDirectory]::DomainDNSA )) {
        Write-Verbose "Getting domain information - $Domain DomainDNSSRV / DomainDNSA"
        $Data.DomainDNSData = Invoke-Command -ScriptBlock {
            $DnsSrv = @()
            $DnsA = @()

            $DnsRecords = "_kerberos._tcp.$Domain", "_ldap._tcp.$Domain"
            foreach ($DnsRecord in $DnsRecords) {
                $Value = Resolve-DnsName -Name $DnsRecord -Type SRV -Verbose:$false -ErrorAction SilentlyContinue | Select-Object *
                if ($null -eq $Value) { Write-Warning 'Getting domain information - DomainDNSSRV / DomainDNSA - Failed!' }
                foreach ($V in $Value) {
                    if ($V.QueryType -eq 'SRV') {
                        $DnsSrv += $V
                    } else {
                        $DnsA += $V
                    }
                }
            }
            $ReturnData = @{
                # QueryType, Target, NameTarget, Priority, Weight, Port, Name, Type, CharacterSet, Section
                SRV = $DnsSrv | Select-Object Target, NameTarget, Priority, Weight, Port, Name # Type, QueryType, CharacterSet, Section
                # Address, IPAddress, QueryType, IP4Address, Name, Type, CharacterSet, Section, DataLength, TTL
                A   = $DnsA | Select-Object Address, IPAddress, IP4Address, Name, Type, DataLength, TTL # QueryType, CharacterSet, Section
            }
            return $ReturnData
        }
        $Data.DomainDNSSrv = $Data.DomainDNSData.SRV
        $Data.DomainDNSA = $Data.DomainDNSData.A
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFSMO, [ActiveDirectory]::DomainTrusts, [ActiveDirectory]::DomainTrustsClean )) {
        $Data.DomainFSMO = Get-WinADDomainFSMO -Domain $Domain -DomainInformation $Data.DomainInformation
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainTrustsClean, [ActiveDirectory]::DomainTrusts)) {
        $Data.DomainTrustsClean = Get-WinADDomainTrustsClean -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainTrusts)) {
        $Data.DomainTrusts = Get-WinADDomainTrusts -DomainPDC $Data.DomainFSMO.'PDC Emulator' -Trusts $Data.DomainTrustsClean -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainGroupPolicies,
            [ActiveDirectory]::DomainGroupPoliciesDetails,
            [ActiveDirectory]::DomainGroupPoliciesACL
        )) {
        Write-Verbose "Getting domain information - $Domain DomainGroupPolicies"
        $Data.DomainGroupPoliciesClean = $(Get-GPO -Domain $Domain -All)
        $Data.DomainGroupPolicies = foreach ($gpo in $Data.DomainGroupPoliciesClean) {
            [PSCustomObject][ordered] @{
                'Display Name'      = $gpo.DisplayName
                'Gpo Status'        = $gpo.GPOStatus
                'Creation Time'     = $gpo.CreationTime
                'Modification Time' = $gpo.ModificationTime
                'Description'       = $gpo.Description
                'Wmi Filter'        = $gpo.WmiFilter
            }
        }
        $Data.DomainGroupPoliciesDetails = Invoke-Command -ScriptBlock {
            Write-Verbose -Message "Getting domain information - $Domain Group Policies Details"
            $Output = ForEach ($GPO in $Data.DomainGroupPoliciesClean) {
                [xml]$XmlGPReport = $GPO.generatereport('xml')
                #GPO version
                if ($XmlGPReport.GPO.Computer.VersionDirectory -eq 0 -and $XmlGPReport.GPO.Computer.VersionSysvol -eq 0) { $ComputerSettings = "NeverModified" }else { $ComputerSettings = "Modified" }
                if ($XmlGPReport.GPO.User.VersionDirectory -eq 0 -and $XmlGPReport.GPO.User.VersionSysvol -eq 0) { $UserSettings = "NeverModified" }else { $UserSettings = "Modified" }
                #GPO content
                if ($null -eq $XmlGPReport.GPO.User.ExtensionData) { $UserSettingsConfigured = $false } else { $UserSettingsConfigured = $true }
                if ($null -eq $XmlGPReport.GPO.Computer.ExtensionData) { $ComputerSettingsConfigured = $false } else { $ComputerSettingsConfigured = $true }
                #Output
                [PSCustomObject][ordered] @{
                    'Name'                   = $XmlGPReport.GPO.Name
                    'Links'                  = $XmlGPReport.GPO.LinksTo | Select-Object -ExpandProperty SOMPath
                    'Has Computer Settings'  = $ComputerSettingsConfigured
                    'Has User Settings'      = $UserSettingsConfigured
                    'User Enabled'           = $XmlGPReport.GPO.User.Enabled
                    'Computer Enabled'       = $XmlGPReport.GPO.Computer.Enabled
                    'Computer Settings'      = $ComputerSettings
                    'User Settings'          = $UserSettings
                    'Gpo Status'             = $GPO.GpoStatus
                    'Creation Time'          = $GPO.CreationTime
                    'Modification Time'      = $GPO.ModificationTime
                    'WMI Filter'             = $GPO.WmiFilter.name
                    'WMI Filter Description' = $GPO.WmiFilter.Description
                    'Path'                   = $GPO.Path
                    'GUID'                   = $GPO.Id
                    'SDDL'                   = $XmlGPReport.GPO.SecurityDescriptor.SDDL.'#text'
                    #'ACLs' = $XmlGPReport.GPO.SecurityDescriptor.Permissions.TrusteePermissions | ForEach-Object -Process {
                    # New-Object -TypeName PSObject -Property @{
                    # 'User' = $_.trustee.name.'#Text'
                    # 'Permission Type' = $_.type.PermissionType
                    # 'Inherited' = $_.Inherited
                    # 'Permissions' = $_.Standard.GPOGroupedAccessEnum
                    # }
                    #}
                }
            }
            return $Output
        }
        $Data.DomainGroupPoliciesACL = Invoke-Command -ScriptBlock {
            Write-Verbose -Message "Getting domain information - $Domain Group Policies ACLs"
            $Output = ForEach ($GPO in $Data.DomainGroupPoliciesClean) {
                [xml]$XmlGPReport = $GPO.generatereport('xml')
                $ACLs = $XmlGPReport.GPO.SecurityDescriptor.Permissions.TrusteePermissions
                foreach ($ACL in $ACLS) {
                    [PSCustomObject][ordered] @{
                        'GPO Name'        = $GPO.DisplayName
                        'User'            = $ACL.trustee.name.'#Text'
                        'Permission Type' = $ACL.type.PermissionType
                        'Inherited'       = $ACL.Inherited
                        'Permissions'     = $ACL.Standard.GPOGroupedAccessEnum
                    }
                }
            }
            return $Output
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainBitlocker)) {
        #$Data.DomainBitlocker = Get-WinADDomainBitlocker -Domain $Domain -Computers $Data.DomainComputersFullList
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainLAPS)) {
        $Data.DomainLAPS = Get-WinADDomainLAPS -Domain $Domain -Computers $Data.DomainComputersFullList
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainDefaultPasswordPolicy)) {
        Write-Verbose -Message "Getting domain information - $Domain DomainDefaultPasswordPolicy"
        $Data.DomainDefaultPasswordPolicy = Invoke-Command -ScriptBlock {
            $Policy = $(Get-ADDefaultDomainPasswordPolicy -Server $Domain)
            [ordered] @{
                'Complexity Enabled'            = $Policy.ComplexityEnabled
                'Lockout Duration'              = $Policy.LockoutDuration
                'Lockout Observation Window'    = $Policy.LockoutObservationWindow
                'Lockout Threshold'             = $Policy.LockoutThreshold
                'Max Password Age'              = $Policy.MaxPasswordAge
                'Min Password Length'           = $Policy.MinPasswordLength
                'Min Password Age'              = $Policy.MinPasswordAge
                'Password History Count'        = $Policy.PasswordHistoryCount
                'Reversible Encryption Enabled' = $Policy.ReversibleEncryptionEnabled
                'Distinguished Name'            = $Policy.DistinguishedName
            }
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainOrganizationalUnits,
            [ActiveDirectory]::DomainContainers,
            [ActiveDirectory]::DomainOrganizationalUnitsDN,
            [ActiveDirectory]::DomainOrganizationalUnitsACL,
            [ActiveDirectory]::DomainOrganizationalUnitsBasicACL,
            [ActiveDirectory]::DomainOrganizationalUnitsExtended
        )) {
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits Clean"
        $Data.DomainOrganizationalUnitsClean = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter * )
        $Data.DomainOrganizationalUnits = Get-WinADDomainOrganizationalUnits -Domain $Domain -OrgnaizationalUnits $Data.DomainOrganizationalUnitsClean
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsDN"
        $Data.DomainOrganizationalUnitsDN = Invoke-Command -ScriptBlock {
            $OUs = @(
                $Data.DomainInformation.DistinguishedName
                $Data.DomainOrganizationalUnitsClean.DistinguishedName
                $Data.DomainContainers.DistinguishedName
            )
            return $OUs
        }

        <#
        $OrganizationalUnitACL = Get-WinADDomainOrganizationalUnitsACL `
            -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean `
            -Domain $Domain `
            -NetBiosName $Data.DomainInformation.NetBIOSName
        #>


        $Data.DomainOrganizationalUnitsBasicACL = Get-WinADDomainOrganizationalUnitsACL  `
            -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean `
            -Domain $Domain `
            -NetBiosName $Data.DomainInformation.NetBIOSName `
            -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext

        $Data.DomainOrganizationalUnitsExtended = Get-WinADDomainOrganizationalUnitsACLExtended  `
            -DomainOrganizationalUnitsClean $Data.DomainOrganizationalUnitsClean `
            -Domain $Domain `
            -NetBiosName $Data.DomainInformation.NetBIOSName `
            -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext `
            -GUID $Data.DomainGUIDS
        #$null = $OrganizationalUnitACL # remove unneeded stuff

        #-DomainOrganizationalUnitsBasicACL $Data.DomainOrganizationalUnitsBasicACL `
        #-DomainOrganizationalUnitsExtended $Data.DomainOrganizationalUnitsExtended

        <#
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsACL"
        $Data.DomainOrganizationalUnitsACL = Invoke-Command -ScriptBlock {
            $ReportBasic = @()
            $ReportExtented = @()
            $OUs = @()
            #$OUs += @{ Name = 'Root'; Value = $Data.DomainRootDSE.rootDomainNamingContext }
            foreach ($OU in $Data.DomainOrganizationalUnitsClean) {
                $OUs += @{ Name = 'Organizational Unit'; Value = $OU.DistinguishedName }
                #Write-Verbose "1. $($Ou.DistinguishedName)"
            }
            #foreach ($OU in $Data.DomainContainers) {
            # $OUs += @{ Name = 'Container'; Value = $OU.DistinguishedName }
            # Write-Verbose "2. $($Ou.DistinguishedName)"
            #}
            $PSDriveName = $Data.DomainInformation.NetBIOSName
            New-PSDrive -Name $PSDriveName -Root "" -PsProvider ActiveDirectory -Server $Domain
 
            ForEach ($OU in $OUs) {
                #Write-Verbose "3. $($Ou.Value)"
                $ReportBasic += Get-Acl -Path "$PSDriveName`:\$($OU.Value)" | Select-Object `
                @{name = 'Distinguished Name'; expression = { $OU.Value } },
                @{name = 'Type'; expression = { $OU.Name } },
                @{name = 'Owner'; expression = { $_.Owner } },
                @{name = 'Group'; expression = { $_.Group } },
                @{name = 'Are AccessRules Protected'; expression = { $_.AreAccessRulesProtected } },
                @{name = 'Are AuditRules Protected'; expression = { $_.AreAuditRulesProtected } },
                @{name = 'Are AccessRules Canonical'; expression = { $_.AreAccessRulesCanonical } },
                @{name = 'Are AuditRules Canonical'; expression = { $_.AreAuditRulesCanonical } },
                @{name = 'Sddl'; expression = { $_.Sddl } }
 
                $ReportExtented += Get-Acl -Path "$PSDriveName`:\$($OU.Value)" | `
                    Select-Object -ExpandProperty Access | `
                    Select-Object `
                @{name = 'Distinguished Name'; expression = { $OU.Value } },
                @{name = 'Type'; expression = { $OU.Name } },
                @{name = 'AccessControlType'; expression = { $_.AccessControlType } },
                @{name = 'ObjectType Name'; expression = { if ($_.objectType.ToString() -eq '00000000-0000-0000-0000-000000000000') { 'All' } Else { $GUID.Item($_.objectType) } } },
                @{name = 'Inherited ObjectType Name'; expression = { $GUID.Item($_.inheritedObjectType) } },
                @{name = 'ActiveDirectoryRights'; expression = { $_.ActiveDirectoryRights } },
                @{name = 'InheritanceType'; expression = { $_.InheritanceType } },
                @{name = 'ObjectType'; expression = { $_.ObjectType } },
                @{name = 'InheritedObjectType'; expression = { $_.InheritedObjectType } },
                @{name = 'ObjectFlags'; expression = { $_.ObjectFlags } },
                @{name = 'IdentityReference'; expression = { $_.IdentityReference } },
                @{name = 'IsInherited'; expression = { $_.IsInherited } },
                @{name = 'InheritanceFlags'; expression = { $_.InheritanceFlags } },
                @{name = 'PropagationFlags'; expression = { $_.PropagationFlags } }
 
 
            }
            return @{ Basic = $ReportBasic; Extended = $ReportExtented }
        }
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL"
        $Data.DomainOrganizationalUnitsBasicACL = $Data.DomainOrganizationalUnitsACL.Basic
        Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended"
        $Data.DomainOrganizationalUnitsExtended = $Data.DomainOrganizationalUnitsACL.Extended
        #>

    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainUsers,
            [ActiveDirectory]::DomainUsersAll,
            [ActiveDirectory]::DomainUsersSystemAccounts,
            [ActiveDirectory]::DomainUsersNeverExpiring,
            [ActiveDirectory]::DomainUsersNeverExpiringInclDisabled,
            [ActiveDirectory]::DomainUsersExpiredInclDisabled,
            [ActiveDirectory]::DomainUsersExpiredExclDisabled,
            [ActiveDirectory]::DomainUsersCount
        )) {

        $Data.DomainUsers = Invoke-Command -ScriptBlock {
            Write-Verbose "Getting domain information - $Domain DomainUsers"
            return Get-WinUsers -Users $Data.DomainUsersFullList -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList
        }
        Write-Verbose "Getting domain information - $Domain DomainUsersAll"
        $Data.DomainUsersAll = $Data.DomainUsers | Where-Object { $_.PasswordNotRequired -eq $False } #| Select-Object * #Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain DomainUsersSystemAccounts"
        $Data.DomainUsersSystemAccounts = $Data.DomainUsers | Where-Object { $_.PasswordNotRequired -eq $true } #| Select-Object * #Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiring"
        $Data.DomainUsersNeverExpiring = $Data.DomainUsers | Where-Object { $_.PasswordNeverExpires -eq $true -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false } #| Select-Object * #Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiringInclDisabled"
        $Data.DomainUsersNeverExpiringInclDisabled = $Data.DomainUsers | Where-Object { $_.PasswordNeverExpires -eq $true -and $_.PasswordNotRequired -eq $false } #| Select-Object * #Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain DomainUsersExpiredInclDisabled"
        $Data.DomainUsersExpiredInclDisabled = $Data.DomainUsers | Where-Object { $_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.PasswordNotRequired -eq $false } #| Select-Object * #Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain DomainUsersExpiredExclDisabled"
        $Data.DomainUsersExpiredExclDisabled = $Data.DomainUsers | Where-Object { $_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false } #| Select-Object * # Name, SamAccountName, UserPrincipalName, Enabled
        Write-Verbose "Getting domain information - $Domain All Users Count"
        $Data.DomainUsersCount = [ordered] @{
            'Users Count Incl. System'            = Get-ObjectCount -Object $Data.DomainUsers
            'Users Count'                         = Get-ObjectCount -Object $Data.DomainUsersAll
            'Users Expired'                       = Get-ObjectCount -Object $Data.DomainUsersExpiredExclDisabled
            'Users Expired Incl. Disabled'        = Get-ObjectCount -Object $Data.DomainUsersExpiredInclDisabled
            'Users Never Expiring'                = Get-ObjectCount -Object $Data.DomainUsersNeverExpiring
            'Users Never Expiring Incl. Disabled' = Get-ObjectCount -Object $Data.DomainUsersNeverExpiringInclDisabled
            'Users System Accounts'               = Get-ObjectCount -Object $Data.DomainUsersSystemAccounts
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainControllers )) {
        Write-Verbose "Getting domain information - $Domain DomainControllers"
        $Data.DomainControllersClean = $(Get-ADDomainController -Server $Domain -Filter * )
        $Data.DomainControllers = Invoke-Command -ScriptBlock {
            foreach ($Policy in $Data.DomainControllersClean) {
                [PSCustomObject][ordered] @{
                    'Name'             = $Policy.Name
                    'Host Name'        = $Policy.HostName
                    'Operating System' = $Policy.OperatingSystem
                    'Site'             = $Policy.Site
                    'Ipv4'             = $Policy.Ipv4Address
                    'Ipv6'             = $Policy.Ipv6Address
                    'Global Catalog?'  = $Policy.IsGlobalCatalog
                    'Read Only?'       = $Policy.IsReadOnly
                    'Ldap Port'        = $Policy.LdapPort
                    'SSL Port'         = $Policy.SSLPort
                }
            }
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPolicies)) {
        Write-Verbose "Getting domain information - $Domain DomainFineGrainedPolicies"
        $Data.DomainFineGrainedPolicies = Invoke-Command -ScriptBlock {
            $FineGrainedPoliciesData = Get-ADFineGrainedPasswordPolicy -Filter * -Server $Domain
            $FineGrainedPolicies = foreach ($Policy in $FineGrainedPoliciesData) {
                [PSCustomObject][ordered] @{
                    'Name'                          = $Policy.Name
                    'Complexity Enabled'            = $Policy.ComplexityEnabled
                    'Lockout Duration'              = $Policy.LockoutDuration
                    'Lockout Observation Window'    = $Policy.LockoutObservationWindow
                    'Lockout Threshold'             = $Policy.LockoutThreshold
                    'Max Password Age'              = $Policy.MaxPasswordAge
                    'Min Password Length'           = $Policy.MinPasswordLength
                    'Min Password Age'              = $Policy.MinPasswordAge
                    'Password History Count'        = $Policy.PasswordHistoryCount
                    'Reversible Encryption Enabled' = $Policy.ReversibleEncryptionEnabled
                    'Precedence'                    = $Policy.Precedence
                    'Applies To'                    = $Policy.AppliesTo # get all groups / usrs and convert to data TODO
                    'Distinguished Name'            = $Policy.DistinguishedName
                }
            }
            return $FineGrainedPolicies #Format-TransposeTable $FineGrainedPolicies
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPoliciesUsers)) {
        Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsers"
        $Data.DomainFineGrainedPoliciesUsers = Invoke-Command -ScriptBlock {
            $PolicyUsers = @()
            foreach ($Policy in $Data.DomainFineGrainedPolicies) {
                $Users = @()
                $Groups = @()
                foreach ($U in $Policy.'Applies To') {
                    $Users += Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $U
                    $Groups += Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainGroupsFullList -DistinguishedName $U
                }
                foreach ($User in $Users) {
                    $PolicyUsers += [pscustomobject] @{
                        'Policy Name'  = $Policy.Name
                        Name           = $User.Name
                        SamAccountName = $User.SamAccountName
                        Type           = $User.ObjectClass
                        SID            = $User.SID
                    }
                }
                foreach ($Group in $Groups) {
                    $PolicyUsers += [pscustomobject] @{
                        'Policy Name'  = $Policy.Name
                        Name           = $Group.Name
                        SamAccountName = $Group.SamAccountName
                        Type           = $Group.ObjectClass
                        SID            = $Group.SID
                    }
                }
            }
            #Get-AdFineGrainedPassowrdPolicySubject
            #Get-AdresultantPasswordPolicy -Identity <user>
            return $PolicyUsers
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended)) {
        $Data.DomainFineGrainedPoliciesUsersExtended = Get-DomainFineGrainedPoliciesUsersExtended `
            -DomainFineGrainedPolicies $Data.DomainFineGrainedPoliciesUsers `
            -DomainUsersFullList $Data.DomainUsersFullList `
            -DomainGroupsFullList $Data.DomainGroupsFullList `
            -Domain $Domain

        <#
        Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsersExtended"
        $Data.DomainFineGrainedPoliciesUsersExtended = Invoke-Command -ScriptBlock {
            $PolicyUsers = @()
            foreach ($Policy in $Data.DomainFineGrainedPolicies) {
                $Users = @()
                $Groups = @()
                foreach ($U in $Policy.'Applies To') {
                    $Users += Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $U
                    $Groups += Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainGroupsFullList -DistinguishedName $U
                }
                foreach ($User in $Users) {
                    $PolicyUsers += [pscustomobject][ordered] @{
                        'Policy Name' = $Policy.Name
                        Name = $User.Name
                        SamAccountName = $User.SamAccountName
                        Type = $User.ObjectClass
                        SID = $User.SID
                        'High Privileged Group' = 'N/A'
                        'Display Name' = $User.DisplayName
                        'Member Name' = $Member.Name
                        'User Principal Name' = $User.UserPrincipalName
                        'Sam Account Name' = $User.SamAccountName
                        'Email Address' = $User.EmailAddress
                        'PasswordExpired' = $User.PasswordExpired
                        'PasswordLastSet' = $User.PasswordLastSet
                        'PasswordNotRequired' = $User.PasswordNotRequired
                        'PasswordNeverExpires' = $User.PasswordNeverExpires
                        'Enabled' = $User.Enabled
                        'MemberSID' = $Member.SID.Value
                        'Manager' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $User.Manager).Name
                        'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $User.Manager).EmailAddress
                        'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") # -Verbose
                        "DaysToExpire" = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($User."msDS-UserPasswordExpiryTimeComputed")))
                        "AccountExpirationDate" = $User.AccountExpirationDate
                        "AccountLockoutTime" = $User.AccountLockoutTime
                        "AllowReversiblePasswordEncryption" = $User.AllowReversiblePasswordEncryption
                        "BadLogonCount" = $User.BadLogonCount
                        "CannotChangePassword" = $User.CannotChangePassword
                        "CanonicalName" = $User.CanonicalName
                        'Given Name' = $User.GivenName
                        'Surname' = $User.Surname
                        "Description" = $User.Description
                        "DistinguishedName" = $User.DistinguishedName
                        "EmployeeID" = $User.EmployeeID
                        "EmployeeNumber" = $User.EmployeeNumber
                        "LastBadPasswordAttempt" = $User.LastBadPasswordAttempt
                        "LastLogonDate" = $User.LastLogonDate
                        "Created" = $User.Created
                        "Modified" = $User.Modified
                        "Protected" = $User.ProtectedFromAccidentalDeletion
                        "Domain" = $Domain
                    }
                }
 
                foreach ($Group in $Groups) {
                    $GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.SID -Recursive
                    foreach ($Member in $GroupMembership) {
                        $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Member.DistinguishedName)
                        $PolicyUsers += [pscustomobject][ordered] @{
                            'Policy Name' = $Policy.Name
                            Name = $Group.Name
                            SamAccountName = $Group.SamAccountName
                            Type = $Group.ObjectClass
                            SID = $Group.SID
                            'High Privileged Group' = if ($Group.adminCount -eq 1) { $True } else { $False }
                            'Display Name' = $Object.DisplayName
                            'Member Name' = $Member.Name
                            'User Principal Name' = $Object.UserPrincipalName
                            'Sam Account Name' = $Object.SamAccountName
                            'Email Address' = $Object.EmailAddress
                            'PasswordExpired' = $Object.PasswordExpired
                            'PasswordLastSet' = $Object.PasswordLastSet
                            'PasswordNotRequired' = $Object.PasswordNotRequired
                            'PasswordNeverExpires' = $Object.PasswordNeverExpires
                            'Enabled' = $Object.Enabled
                            'MemberSID' = $Member.SID.Value
                            'Manager' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Object.Manager).Name
                            'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $Object.Manager).EmailAddress
                            'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") # -Verbose
                            "DaysToExpire" = (Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                            "AccountExpirationDate" = $Object.AccountExpirationDate
                            "AccountLockoutTime" = $Object.AccountLockoutTime
                            "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                            "BadLogonCount" = $Object.BadLogonCount
                            "CannotChangePassword" = $Object.CannotChangePassword
                            "CanonicalName" = $Object.CanonicalName
                            'Given Name' = $Object.GivenName
                            'Surname' = $Object.Surname
                            "Description" = $Object.Description
                            "DistinguishedName" = $Object.DistinguishedName
                            "EmployeeID" = $Object.EmployeeID
                            "EmployeeNumber" = $Object.EmployeeNumber
                            "LastBadPasswordAttempt" = $Object.LastBadPasswordAttempt
                            "LastLogonDate" = $Object.LastLogonDate
                            "Created" = $Object.Created
                            "Modified" = $Object.Modified
                            "Protected" = $Object.ProtectedFromAccidentalDeletion
                            "Domain" = $Domain
                        }
                    }
                }
 
 
            }
            return $PolicyUsers
        }
         #>

    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsSpecial)) {
        Write-Verbose "Getting domain information - $Domain DomainGroups"
        $Data.DomainGroups = Get-WinGroups -Groups $Data.DomainGroupsFullList -Users $Data.DomainUsersFullList -Domain $Domain
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsMembers"
        $Data.DomainGroupsMembers = Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList -Option Standard
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroups, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsMembersRecursive"
        $Data.DomainGroupsMembersRecursive = Get-WinGroupMembers -Groups $Data.DomainGroups -Domain $Domain -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -ADCatalogUsers $Data.DomainUsersFullList -Option Recursive
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsPriviliged)) {
        $Data.DomainGroupsPriviliged = Get-DomainGroupsPriviliged -DomainGroups $Data.DomainGroups -DomainInformation $Data.DomainInformation
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsSpecial, [ActiveDirectory]::DomainGroupMembersRecursiveSpecial)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsSpecial"
        $Data.DomainGroupsSpecial = $Data.DomainGroups | Where-Object { ($_.'Group SID').Length -eq 12 }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsSpecialMembers, [ActiveDirectory]::DomainGroupsSpecialMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupMembersSpecialRecursive"
        $Data.DomainGroupsSpecialMembers = $Data.DomainGroupsMembers | Where-Object { ($_.'Group SID').Length -eq 12 } | Select-Object * #-Exclude Group*, 'High Privileged Group'
        Write-Verbose "Getting domain information - $Domain DomainGroupsSpecialMembersRecursive"
        $Data.DomainGroupsSpecialMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object { ($_.'Group SID').Length -eq 12 } | Select-Object * #-Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainGroupsPriviligedMembers, [ActiveDirectory]::DomainGroupsPriviligedMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembers"
        $Data.DomainGroupsPriviligedMembers = $Data.DomainGroupsMembers | Where-Object { $Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID') } | Select-Object * #-Exclude Group*, 'High Privileged Group'
        Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembersRecursive"
        $Data.DomainGroupsPriviligedMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object { $Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID') } | Select-Object * #-Exclude Group*, 'High Privileged Group'
    }
    ## Users per one group only.
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAdministrators, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainAdministrators"
        $Data.DomainAdministrators = $Data.DomainGroupsMembers | Where-Object { $_.'Group SID' -eq $('{0}-512' -f $Data.DomainInformation.DomainSID.Value) } | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainAdministratorsRecursive, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainAdministratorsRecursive"
        $Data.DomainAdministratorsRecursive = $Data.DomainGroupsMembersRecursive | Where-Object { $_.'Group SID' -eq $('{0}-512' -f $Data.DomainInformation.DomainSID.Value) } | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainEnterpriseAdministrators, [ActiveDirectory]::DomainGroupsMembers)) {
        Write-Verbose "Getting domain information - $Domain DomainEnterpriseAdministrators"
        $Data.DomainEnterpriseAdministrators = $Data.DomainGroupsMembers | Where-Object { $_.'Group SID' -eq $('{0}-519' -f $Data.DomainInformation.DomainSID.Value) } | Select-Object * -Exclude Group*, 'High Privileged Group'
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainEnterpriseAdministratorsRecursive, [ActiveDirectory]::DomainGroupsMembersRecursive)) {
        Write-Verbose "Getting domain information - $Domain DomainEnterpriseAdministratorsRecursive"
        $Data.DomainEnterpriseAdministratorsRecursive = $Data.DomainGroupsMembersRecursive | Where-Object { $_.'Group SID' -eq $('{0}-519' -f $Data.DomainInformation.DomainSID.Value) } | Select-Object * -Exclude Group*, 'High Privileged Group'
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainPasswordDataUsers,
            [ActiveDirectory]::DomainPasswordDataPasswords,
            [ActiveDirectory]::DomainPasswordClearTextPassword,
            [ActiveDirectory]::DomainPasswordLMHash,
            [ActiveDirectory]::DomainPasswordEmptyPassword,
            [ActiveDirectory]::DomainPasswordWeakPassword,
            [ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordList,
            [ActiveDirectory]::DomainPasswordDefaultComputerPassword,
            [ActiveDirectory]::DomainPasswordPasswordNotRequired,
            [ActiveDirectory]::DomainPasswordPasswordNeverExpires,
            [ActiveDirectory]::DomainPasswordAESKeysMissing,
            [ActiveDirectory]::DomainPasswordPreAuthNotRequired,
            [ActiveDirectory]::DomainPasswordDESEncryptionOnly,
            [ActiveDirectory]::DomainPasswordDelegatableAdmins,
            [ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
            [ActiveDirectory]::DomainPasswordStats,
            [ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled
        )) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataUsers - This will take a while if set!"
        $TimeToProcess = Start-TimeLog
        try {
            $Data.DomainPasswordDataUsers = Get-ADReplAccount -All -Server $Data.DomainInformation.DnsRoot -NamingContext $Data.DomainInformation.DistinguishedName
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*is not recognized as the name of a cmdlet*') {
                Write-Warning "Get-ADReplAccount - Please install module DSInternals (Install-Module DSInternals) - Error: $ErrorMessage"
            } else {
                Write-Warning "Get-ADReplAccount - Error occured: $ErrorMessage"
            }
        }
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataUsers - Time: $($TimeToProcess | Stop-TimeLog)"
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainPasswordDataPasswords,
            [ActiveDirectory]::DomainPasswordClearTextPassword,
            [ActiveDirectory]::DomainPasswordLMHash,
            [ActiveDirectory]::DomainPasswordEmptyPassword,
            [ActiveDirectory]::DomainPasswordWeakPassword,
            [ActiveDirectory]::DomainPasswordWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordDisabled,
            [ActiveDirectory]::DomainPasswordWeakPasswordList,
            [ActiveDirectory]::DomainPasswordDefaultComputerPassword,
            [ActiveDirectory]::DomainPasswordPasswordNotRequired,
            [ActiveDirectory]::DomainPasswordPasswordNeverExpires,
            [ActiveDirectory]::DomainPasswordAESKeysMissing,
            [ActiveDirectory]::DomainPasswordPreAuthNotRequired,
            [ActiveDirectory]::DomainPasswordDESEncryptionOnly,
            [ActiveDirectory]::DomainPasswordDelegatableAdmins,
            [ActiveDirectory]::DomainPasswordDuplicatePasswordGroups,
            [ActiveDirectory]::DomainPasswordStats,
            [ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled
        )) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswords - This will take a while if set!"
        Write-Verbose "Getting domain password information - $Domain Passwords Path: $PathToPasswords"
        $TimeToProcess = Start-TimeLog
        $Data.DomainPasswordDataPasswords = Get-WinADDomainPasswordQuality -FilePath $PathToPasswords -DomainInformation $Data -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswords - Time: $($TimeToProcess | Stop-TimeLog)"
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @(
            [ActiveDirectory]::DomainPasswordHashesWeakPassword,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled,
            [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled
        )) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswordsHashes - This will take a while if set!"
        Write-Verbose "Getting domain password information - $Domain Passwords Hashes Path: $PathToPasswordsHashes"
        $TimeToProcess = Start-TimeLog
        $Data.DomainPasswordDataPasswordsHashes = Get-WinADDomainPasswordQuality -FilePath $PathToPasswordsHashes -DomainInformation $Data -UseHashes -Verbose:$false -PasswordQualityUsers $Data.DomainPasswordDataUsers
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDataPasswordsHashes - Time: $($TimeToProcess | Stop-TimeLog)"
    }
    if ($Data.DomainPasswordDataPasswords) {
        $PasswordsQuality = $Data.DomainPasswordDataPasswords
    } elseif ($Data.DomainPasswordDataPasswordsHashes) {
        $PasswordsQuality = $Data.DomainPasswordDataPasswordsHashes
    } else {
        $PasswordsQuality = $null
    }

    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordClearTextPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordClearTextPassword"
        $Data.DomainPasswordClearTextPassword = $PasswordsQuality.DomainPasswordClearTextPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordLMHash)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordLMHash"
        $Data.DomainPasswordLMHash = $PasswordsQuality.DomainPasswordLMHash
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordEmptyPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordEmptyPassword"
        $Data.DomainPasswordEmptyPassword = $PasswordsQuality.DomainPasswordEmptyPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordWeakPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPassword"
        $Data.DomainPasswordWeakPassword = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordWeakPasswordEnabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordEnabled"
        $Data.DomainPasswordWeakPasswordEnabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordEnabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordDisabled"
        $Data.DomainPasswordWeakPasswordDisabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordDisabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordWeakPasswordList)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordList"
        $Data.DomainPasswordWeakPasswordList = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordList
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordDefaultComputerPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDefaultComputerPassword"
        $Data.DomainPasswordDefaultComputerPassword = $PasswordsQuality.DomainPasswordDefaultComputerPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordPasswordNotRequired)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNotRequired"
        $Data.DomainPasswordPasswordNotRequired = $PasswordsQuality.DomainPasswordPasswordNotRequired
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordPasswordNeverExpires)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNeverExpires"
        $Data.DomainPasswordPasswordNeverExpires = $PasswordsQuality.DomainPasswordPasswordNeverExpires
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordAESKeysMissing)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordAESKeysMissing"
        $Data.DomainPasswordAESKeysMissing = $PasswordsQuality.DomainPasswordAESKeysMissing
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordPreAuthNotRequired)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordPreAuthNotRequired"
        $Data.DomainPasswordPreAuthNotRequired = $PasswordsQuality.DomainPasswordPreAuthNotRequired
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordDESEncryptionOnly)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDESEncryptionOnly"
        $Data.DomainPasswordDESEncryptionOnly = $PasswordsQuality.DomainPasswordDESEncryptionOnly
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordDelegatableAdmins)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDelegatableAdmins"
        $Data.DomainPasswordDelegatableAdmins = $PasswordsQuality.DomainPasswordDelegatableAdmins
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordDuplicatePasswordGroups)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordDuplicatePasswordGroups"
        $Data.DomainPasswordDuplicatePasswordGroups = $PasswordsQuality.DomainPasswordDuplicatePasswordGroups
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordHashesWeakPassword)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPassword"
        $Data.DomainPasswordHashesWeakPassword = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPassword
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordEnabled"
        $Data.DomainPasswordHashesWeakPasswordEnabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordEnabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordDisabled"
        $Data.DomainPasswordHashesWeakPasswordDisabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordDisabled
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @( [ActiveDirectory]::DomainPasswordStats)) {
        Write-Verbose "Getting domain password information - $Domain DomainPasswordStats"
        $Data.DomainPasswordStats = Invoke-Command -ScriptBlock {
            $Stats = [ordered] @{ }
            $Stats.'Clear Text Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordClearTextPassword
            $Stats.'LM Hashes' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordLMHash
            $Stats.'Empty Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordEmptyPassword
            $Stats.'Weak Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPassword
            $Stats.'Weak Passwords Enabled' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPasswordEnabled
            $Stats.'Weak Passwords Disabled' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordWeakPasswordDisabled
            if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPassword)) {
                $Stats.'Weak Passwords (HASH)' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPassword
            }
            if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {
                $Stats.'Weak Passwords (HASH) Enabled' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPasswordEnabled
            }
            if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) {
                $Stats.'Weak Passwords (HASH) Disabled' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPasswordDisabled
            }
            $Stats.'Default Computer Passwords' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDefaultComputerPassword
            $Stats.'Password Not Required' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPasswordNotRequired
            $Stats.'Password Never Expires' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPasswordNeverExpires
            $Stats.'AES Keys Missing' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordAESKeysMissing
            $Stats.'PreAuth Not Required' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordPreAuthNotRequired
            $Stats.'DES Encryption Only' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDESEncryptionOnly
            $Stats.'Delegatable Admins' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDelegatableAdmins
            $Stats.'Duplicate Password Users' = Get-ObjectCount -Object $PasswordsQuality.DomainPasswordDuplicatePasswordGroups
            $Stats.'Duplicate Password Grouped' = Get-ObjectCount ($PasswordsQuality.DomainPasswordDuplicatePasswordGroups.'Duplicate Group' | Sort-Object -Unique)
            return $Stats
        }
    }
    $EndTime = Stop-TimeLog -Time $TimeToGenerate
    Write-Verbose "Getting domain information - $Domain - Time to generate: $EndTime"
    return $Data
}
function Get-WinADDomainLAPS {
    [CmdletBinding()]
    param(
        [string] $Domain,
        [Array] $Computers
    )
    $Properties = @(
        'Name',
        'OperatingSystem',
        'DistinguishedName',
        'ms-Mcs-AdmPwd',
        'ms-Mcs-AdmPwdExpirationTime'
    )
    [DateTime] $CurrentDate = Get-Date

    if ($null -eq $Computers -or $Computers.Count -eq 0) {
        $Computers = Get-ADComputer -Filter * -Properties $Properties
    }
    foreach ($Computer in $Computers) {
        [PSCustomObject] @{
            'Name'                 = $Computer.Name
            'Operating System'     = $Computer.'OperatingSystem'
            'Laps Password'        = $Computer.'ms-Mcs-AdmPwd'
            'Laps Expire (days)'   = Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime'))
            'Laps Expiration Time' = Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime')
            'DistinguishedName'    = $Computer.'DistinguishedName'
        }
    }
}

function Get-WinADDomainOrganizationalUnits {
    [CmdletBinding()]
    param(
        [string] $Domain,
        [Array] $OrgnaizationalUnits
    )
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits"
    if ($null -eq $OrgnaizationalUnits) {
        $OrgnaizationalUnits = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter * )
    }
    $TimeOU = Start-TimeLog
    $Output = foreach ($O in $OrgnaizationalUnits) {
        [PSCustomObject] @{
            'Canonical Name'  = $O.CanonicalName
            'Managed By'      = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).Name
            'Manager Email'   = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).EmailAddress
            'Protected'       = $O.ProtectedFromAccidentalDeletion
            Description       = $O.Description
            Created           = $O.Created
            Modified          = $O.Modified
            Deleted           = $O.Deleted
            'Postal Code'     = $O.PostalCode
            City              = $O.City
            Country           = $O.Country
            State             = $O.State
            'Street Address'  = $O.StreetAddress
            DistinguishedName = $O.DistinguishedName
            ObjectGUID        = $O.ObjectGUID
        }
    }
    $Output | Sort-Object 'Canonical Name'
    $EndOU = Stop-TimeLog -Time $TimeOU -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnits Time: $EndOU"
    <#
        $Time44 = Start-TimeLog
    for ($i = 1; $i -lt 1000; $i++) {
        $OrgnaizationalUnits | Select-Object `
        @{ n = 'Canonical Name'; e = { $_.CanonicalName } },
        @{ n = 'Managed By'; e = {
                (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).Name
            }
        },
        @{ n = 'Manager Email'; e = {
                (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList -DistinguishedName $_.ManagedBy -Verbose).EmailAddress
            }
        },
        @{ n = 'Protected'; e = { $_.ProtectedFromAccidentalDeletion } },
        Created,
        Modified,
        Deleted,
        @{ n = 'Postal Code'; e = { $_.PostalCode } },
        City,
        Country,
        State,
        @{ n = 'Street Address'; e = { $_.StreetAddress } },
        DistinguishedName,
        ObjectGUID | Sort-Object 'Canonical Name'
 
    }
    $End = Stop-TimeLog -Time $Time44 -Option OneLiner
    Write-Verbose $end
    #>

}
function Get-WinADDomainOrganizationalUnitsACL {
    [cmdletbinding()]
    param(
        $DomainOrganizationalUnitsClean,
        [string] $Domain,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext
    )
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL"
    $Time = Start-TimeLog
    $OUs = @(
        #@{ Name = 'Root'; Value = $RootDomainNamingContext }
        foreach ($OU in $DomainOrganizationalUnitsClean) {
            @{ Name = 'Organizational Unit'; Value = $OU.DistinguishedName }
        }
    )
    $null = New-PSDrive -Name $NetBiosName -Root '' -PsProvider ActiveDirectory -Server $Domain

    @(
        foreach ($OU in $OUs) {
            $ACL = Get-Acl -Path "$NetBiosName`:\$($OU.Value)"
            [PsCustomObject] @{
                'Distinguished Name'        = $OU.Value
                'Type'                      = $OU.Name
                'Owner'                     = $ACL.Owner
                'Group'                     = $ACL.Group
                'Are AccessRules Protected' = $ACL.AreAccessRulesProtected
                'Are AuditRules Protected'  = $ACL.AreAuditRulesProtected
                'Are AccessRules Canonical' = $ACL.AreAccessRulesCanonical
                'Are AuditRules Canonical'  = $ACL.AreAuditRulesCanonical
                #'Sddl' = $ACL.Sddl
            }
            <#
            Get-Acl -Path "$NetBiosName`:\$($OU.Value)" | Select-Object `
            @{name = 'Distinguished Name'; expression = { $OU.Value } },
            @{name = 'Type'; expression = { $OU.Name } },
            @{name = 'Owner'; expression = { $_.Owner } },
            @{name = 'Group'; expression = { $_.Group } },
            @{name = 'Are AccessRules Protected'; expression = { $_.AreAccessRulesProtected } },
            @{name = 'Are AuditRules Protected'; expression = { $_.AreAuditRulesProtected } },
            @{name = 'Are AccessRules Canonical'; expression = { $_.AreAccessRulesCanonical } },
            @{name = 'Are AuditRules Canonical'; expression = { $_.AreAuditRulesCanonical } },
            @{name = 'Sddl'; expression = { $_.Sddl } }
            #>

        }
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL Time: $EndTime"
}
function Get-WinADDomainOrganizationalUnitsACLExtended {
    [cmdletbinding()]
    param(
        $DomainOrganizationalUnitsClean,
        [string] $Domain,
        [string] $NetBiosName,
        [string] $RootDomainNamingContext,
        $GUID
    )
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended"
    $Time = Start-TimeLog
    $OUs = @(
        #@{ Name = 'Root'; Value = $RootDomainNamingContext }
        foreach ($OU in $DomainOrganizationalUnitsClean) {
            @{ Name = 'Organizational Unit'; Value = $OU.DistinguishedName }
        }
    )

    $null = New-PSDrive -Name $NetBiosName -Root '' -PsProvider ActiveDirectory -Server $Domain

    @(
        foreach ($OU in $OUs) {

            $ACLs = Get-Acl -Path "$NetBiosName`:\$($OU.Value)" | Select-Object -ExpandProperty Access
            foreach ($ACL in $ACLs) {
                [PSCustomObject] @{
                    'Distinguished Name'        = $OU.Value
                    'Type'                      = $OU.Name
                    'AccessControlType'         = $ACL.AccessControlType
                    'ObjectType Name'           = if ($ACL.objectType.ToString() -eq '00000000-0000-0000-0000-000000000000') { 'All' } Else { $GUID.Item($ACL.objectType) }
                    'Inherited ObjectType Name' = $GUID.Item($ACL.inheritedObjectType)
                    'ActiveDirectoryRights'     = $ACL.ActiveDirectoryRights
                    'InheritanceType'           = $ACL.InheritanceType
                    'ObjectType'                = $ACL.ObjectType
                    'InheritedObjectType'       = $ACL.InheritedObjectType
                    'ObjectFlags'               = $ACL.ObjectFlags
                    'IdentityReference'         = $ACL.IdentityReference
                    'IsInherited'               = $ACL.IsInherited
                    'InheritanceFlags'          = $ACL.InheritanceFlags
                    'PropagationFlags'          = $ACL.PropagationFlags
                }
            }

            <#
            Get-Acl -Path "$NetBiosName`:\$($OU.Value)" | `
                Select-Object -ExpandProperty Access | `
                Select-Object `
            @{name = 'Distinguished Name'; expression = { $OU.Value } },
            @{name = 'Type'; expression = { $OU.Name } },
            @{name = 'AccessControlType'; expression = { $_.AccessControlType } },
            @{name = 'ObjectType Name'; expression = { if ($_.objectType.ToString() -eq '00000000-0000-0000-0000-000000000000') { 'All' } Else { $GUID.Item($_.objectType) } } },
            @{name = 'Inherited ObjectType Name'; expression = { $GUID.Item($_.inheritedObjectType) } },
            @{name = 'ActiveDirectoryRights'; expression = { $_.ActiveDirectoryRights } },
            @{name = 'InheritanceType'; expression = { $_.InheritanceType } },
            @{name = 'ObjectType'; expression = { $_.ObjectType } },
            @{name = 'InheritedObjectType'; expression = { $_.InheritedObjectType } },
            @{name = 'ObjectFlags'; expression = { $_.ObjectFlags } },
            @{name = 'IdentityReference'; expression = { $_.IdentityReference } },
            @{name = 'IsInherited'; expression = { $_.IsInherited } },
            @{name = 'InheritanceFlags'; expression = { $_.InheritanceFlags } },
            @{name = 'PropagationFlags'; expression = { $_.PropagationFlags } }
 
            #>


        }
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended Time: $EndTime"
}

<#
$Data = @{}
$Domain = 'ad.evotec.xyz'
$Data.DomainRootDSE = $(Get-ADRootDSE -Server $Domain)
$Data.DomainInformation = $(Get-ADDomain -Server $Domain)
$Data.DomainGUIDS = Invoke-Command -ScriptBlock {
    $GUID = @{ }
    Get-ADObject -SearchBase (Get-ADRootDSE).schemaNamingContext -LDAPFilter '(schemaIDGUID=*)' -Properties name, schemaIDGUID | ForEach-Object {
        if ($GUID.Keys -notcontains $_.schemaIDGUID ) {
            $GUID.add([System.GUID]$_.schemaIDGUID, $_.name)
        }
    }
    Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).configurationNamingContext)" -LDAPFilter '(objectClass=controlAccessRight)' -Properties name, rightsGUID | ForEach-Object {
        if ($GUID.Keys -notcontains $_.rightsGUID ) {
            $GUID.add([System.GUID]$_.rightsGUID, $_.name)
        }
    }
    return $GUID
}
$OU = $(Get-ADOrganizationalUnit -Server $Domain -Properties * -Filter * )
 
 
Get-WinADDomainOrganizationalUnitsACLExtended `
    -DomainOrganizationalUnitsClean $OU `
    -Domain $Domain `
    -NetBiosName $Data.DomainInformation.NetBIOSName `
    -RootDomainNamingContext $Data.DomainRootDSE.rootDomainNamingContext `
    -GUID $Data.DomainGUIDS
 
    #>

function Get-WinADDomainPasswordQuality {
    [CmdletBinding()]
    param (
        $DomainInformation,
        $PasswordQualityUsers,
        [string] $FilePath,
        [switch] $UseHashes
    )
    if ([string]::IsNullOrEmpty($FilePath)) {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path not given, using hashes set to $UseHashes"
        return $null
    }
    if (-not (Test-Path -Path $FilePath)) {
        Write-Verbose "Get-WinADDomainPasswordQuality - File path doesn't exists, using hashes set to $UseHashes"
        return $null
    }
    if ($DomainInformation -eq $null) {
        Write-Verbose "Get-WinADDomainPasswordQuality - No DomainInformation given, no alternative approach either. Terminating password quality check."
        return $null
    }
    $Data = [ordered] @{}
    if ($PasswordQualityUsers) {
        $Data.PasswordQualityUsers = $PasswordQualityUsers
    } else {
        $Data.PasswordQualityUsers = Get-ADReplAccount -All -Server $DomainInformation.DomainInformation.DnsRoot -NamingContext $DomainInformation.DomainInformation.DistinguishedName
    }
    $Data.PasswordQuality = Invoke-Command -ScriptBlock {
        if ($UseHashes) {
            $Results = $Data.PasswordQualityUsers | Test-PasswordQuality -WeakPasswordHashesFile $FilePath -IncludeDisabledAccounts
        } else {
            $Results = $Data.PasswordQualityUsers | Test-PasswordQuality -WeakPasswordsFile $FilePath -IncludeDisabledAccounts
        }
        return $Results
    }
    $Data.DomainPasswordClearTextPassword = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.ClearTextPassword -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordClearTextPasswordEnabled = Invoke-Command -ScriptBlock {
        return $Data.DomainPasswordClearTextPassword | Where-Object { $_.Enabled -eq $true }
    }
    $Data.DomainPasswordClearTextPasswordDisabled = Invoke-Command -ScriptBlock {
        return $Data.DomainPasswordClearTextPassword | Where-Object { $_.Enabled -eq $false }
    }
    $Data.DomainPasswordLMHash = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList  $Data.PasswordQuality.LMHash  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordEmptyPassword = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.EmptyPassword  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }


    $Data.DomainPasswordWeakPassword = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList  $Data.PasswordQuality.WeakPassword  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordWeakPasswordEnabled = Invoke-Command -ScriptBlock {
        return $Data.DomainPasswordWeakPassword  | Where-Object { $_.Enabled -eq $true }
    }
    $Data.DomainPasswordWeakPasswordDisabled = Invoke-Command -ScriptBlock {
        return $Data.DomainPasswordWeakPassword  | Where-Object { $_.Enabled -eq $false }
    }
    $Data.DomainPasswordWeakPasswordList = Invoke-Command -ScriptBlock {
        if ($UseHashes) {
            return ''
        } else {
            $Passwords = Get-Content -Path $FilePath
            return $Passwords -join ', '
        }
    }
    $Data.DomainPasswordDefaultComputerPassword = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList  $Data.PasswordQuality.DefaultComputerPassword  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordPasswordNotRequired = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList  $Data.PasswordQuality.PasswordNotRequired  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordPasswordNeverExpires = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.PasswordNeverExpires  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordAESKeysMissing = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList  $Data.PasswordQuality.AESKeysMissing  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordPreAuthNotRequired = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.PreAuthNotRequired  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordDESEncryptionOnly = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.DESEncryptionOnly -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordDelegatableAdmins = Invoke-Command -ScriptBlock {
        $ADAccounts = Get-WinADAccounts -UserNameList $Data.PasswordQuality.DelegatableAdmins  -ADCatalog $DomainInformation.DomainUsersAll, $DomainInformation.DomainComputersAll
        return $ADAccounts | Select-Object 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    $Data.DomainPasswordDuplicatePasswordGroups = Invoke-Command -ScriptBlock {
        $Value = @()
        $DuplicateGroups = $Data.PasswordQuality.DuplicatePasswordGroups.ToArray()
        $Count = 0
        foreach ($DuplicateGroup in $DuplicateGroups) {
            $Count++
            $Name = "Duplicate $Count"
            foreach ($User in $DuplicateGroup) {
                $FoundUser = [pscustomobject] @{
                    'Duplicate Group' = $Name
                    #'Found User' = $User
                }
                $FullUserInformation = $DomainInformation.DomainUsersAll | Where-Object { $_.SamAccountName -eq $User }
                $FullComputerInformation = $DomainInformation.DomainComputersAll | Where-Object { $_.SamAccountName -eq $User }
                if ($FullUserInformation) {
                    $MergedObject = Merge-Objects -Object1 $FoundUser -Object2 $FullUserInformation
                }
                if ($FullComputerInformation) {
                    $MergedObject = Merge-Objects -Object1 $MergedObject -Object2 $FullComputerInformation
                }
                $Value += $MergedObject

            }
        }
        # Added 'Duplicate Group' to standard output of names - without it, it doesn't make sense
        return $Value | Select-Object 'Duplicate Group', 'Name', 'UserPrincipalName', 'Enabled', 'Password Last Changed', "DaysToExpire", `
            'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'DateExpiry', 'PasswordLastSet', 'SamAccountName', `
            'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email', `
            "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount", `
            "CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt", `
            "LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
    }
    return $Data
}
function Get-WinADDomainRIDs {
    [CmdletBinding()]
    param(
        [Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation,
        [string] $Domain
    )
    # Critical for RID Pool Depletion: https://blogs.technet.microsoft.com/askds/2011/09/12/managing-rid-pool-depletion/
    $Time = Start-TimeLog
    Write-Verbose "Getting domain information - $Domain DomainRIDs"

    if ($null -eq $DomainInformation) {
        $DomainInformation = Get-ADDomain -Server $Domain
    }
    $rID = [ordered] @{ }
    $rID.'rIDs Master' = $DomainInformation.RIDMaster

    $Property = get-adobject "cn=rid manager$,cn=system,$($DomainInformation.DistinguishedName)" -Property RidAvailablePool -Server $rID.'rIDs Master'
    [int32]$totalSIDS = $($Property.RidAvailablePool) / ([math]::Pow(2, 32))
    [int64]$temp64val = $totalSIDS * ([math]::Pow(2, 32))
    [int32]$currentRIDPoolCount = $($Property.RidAvailablePool) - $temp64val
    [int64]$RidsRemaining = $totalSIDS - $currentRIDPoolCount

    $Rid.'rIDs Available Pool' = $Property.RidAvailablePool
    $rID.'rIDs Total SIDs' = $totalSIDS
    $rID.'rIDs Issued' = $CurrentRIDPoolCount
    $rID.'rIDs Remaining' = $RidsRemaining
    $rID.'rIDs Percentage' = if ($RidsRemaining -eq 0) { $RidsRemaining.ToString("P") } else { ($currentRIDPoolCount / $RidsRemaining * 100).ToString("P") }

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainRIDs Time: $EndTime"
    return $rID
}
function Get-WinADDomainTrusts {
    [CmdletBinding()]
    param(
        [string] $Domain,
        [string] $DomainPDC,
        [Array] $Trusts
    )

    Write-Verbose "Getting domain information - $Domain DomainTrusts"
    $Time = Start-TimeLog

    if ($null -eq $Trusts) {
        $Trusts = Get-ADTrust -Server $Domain -Filter * -Properties *
    }
    if ($DomainPDC -eq '') {
        $DomainPDC = (Get-ADDomain -Server $Domain).PDCEmulator
    }

    $PropertiesTrustWMI = @(
        'FlatName',
        'SID',
        'TrustAttributes',
        'TrustDirection',
        'TrustedDCName',
        'TrustedDomain',
        'TrustIsOk',
        'TrustStatus',
        'TrustStatusString', # TrustIsOk/TrustStatus are covered by this
        'TrustType'
    )

    <# TrustWMI
        FlatName : EVOTECPL
        SID : S-1-5-21-3661168273-3802070955-2987026695
        TrustAttributes : 32
        TrustDirection : 3
        TrustedDCName : \\ADPreview2019.ad.evotec.pl
        TrustedDomain : ad.evotec.pl
        TrustIsOk : True
        TrustStatus : 0
        TrustStatusString : OK
        TrustType : 2
        PSComputerName : ad1.ad.evotec.xyz
    #>


    $TrustStatatuses = Get-CimInstance -ClassName Microsoft_DomainTrustStatus -Namespace root\MicrosoftActiveDirectory -ComputerName $DomainPDC -ErrorAction SilentlyContinue -Verbose:$false -Property $PropertiesTrustWMI

    $ReturnData = foreach ($Trust in $Trusts) {
        $TrustWMI = $TrustStatatuses | & { process { if ($_.TrustedDomain -eq $Trust.Target ) { $_ } } }
        [PSCustomObject][ordered] @{
            'Trust Source'               = $Domain
            'Trust Target'               = $Trust.Target
            'Trust Direction'            = $Trust.Direction
            'Trust Attributes'           = if ($Trust.TrustAttributes -is [int]) { Set-TrustAttributes -Value $Trust.TrustAttributes } else { 'Error - needs fixing' }
            'Trust Status'               = if ($null -ne $TrustWMI) { $TrustWMI.TrustStatusString } else { 'N/A' }
            'Forest Transitive'          = $Trust.ForestTransitive
            'Selective Authentication'   = $Trust.SelectiveAuthentication
            'SID Filtering Forest Aware' = $Trust.SIDFilteringForestAware
            'SID Filtering Quarantined'  = $Trust.SIDFilteringQuarantined
            'Disallow Transivity'        = $Trust.DisallowTransivity
            'Intra Forest'               = $Trust.IntraForest
            'Tree Parent?'               = $Trust.IsTreeParent
            'Tree Root?'                 = $Trust.IsTreeRoot
            'TGTDelegation'              = $Trust.TGTDelegation
            'TrustedPolicy'              = $Trust.TrustedPolicy
            'TrustingPolicy'             = $Trust.TrustingPolicy
            'TrustType'                  = $Trust.TrustType
            'UplevelOnly'                = $Trust.UplevelOnly
            'UsesAESKeys'                = $Trust.UsesAESKeys
            'UsesRC4Encryption'          = $Trust.UsesRC4Encryption
            'Trust Source DC'            = if ($null -ne $TrustWMI) { $TrustWMI.PSComputerName } else { 'N/A' }
            'Trust Target DC'            = if ($null -ne $TrustWMI) { $TrustWMI.TrustedDCName.Replace('\\', '') } else { 'N/A' }
            'Trust Source DN'            = $Trust.Source
            'ObjectGUID'                 = $Trust.ObjectGUID
            'Created'                    = $Trust.Created
            'Modified'                   = $Trust.Modified
            'Deleted'                    = $Trust.Deleted
            'SID'                        = $Trust.securityIdentifier
            'TrustOK'                    = if ($null -ne $TrustWMI) { $TrustWMI.TrustIsOK } else { $false }
            'TrustStatus'                = if ($null -ne $TrustWMI) { $TrustWMI.TrustStatus } else { -1 }
        }
    }

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainTrusts Time: $EndTime"

    return $ReturnData

}
function Get-WinADDomainTrustsClean {
    [CmdletBinding()]
    param(
        [string] $Domain,
        [Array] $TypesRequired
    )
    Write-Verbose "Getting domain information - $Domain DomainTrustsClean"
    $Time = Start-TimeLog

    Get-ADTrust -Server $Domain -Filter * -Properties * -ErrorAction SilentlyContinue

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainTrustsClean Time: $EndTime"
}
function Get-WinADDomainUsersFullList {
    [CmdletBinding()]
    param(
        [string] $Domain,
        [switch] $Extended,
        [Array] $ForestSchemaUsers
    )
    Write-Verbose "Getting domain information - $Domain DomainUsersFullList"
    $TimeUsers = Start-TimeLog
    if ($Extended) {
        [string] $Properties = '*'
    } else {
        $Properties = @(
            'Name'
            'UserPrincipalName'
            'SamAccountName'
            'DisplayName'
            'GivenName'
            'Surname'
            'EmailAddress'
            'PasswordExpired'
            'PasswordLastSet'
            'PasswordNotRequired'
            'PasswordNeverExpires'
            'Enabled'
            'Manager'
            'msDS-UserPasswordExpiryTimeComputed'
            'AccountExpirationDate'
            'AccountLockoutTime'
            'AllowReversiblePasswordEncryption'
            'BadLogonCount'
            'CannotChangePassword'
            'CanonicalName'
            'Description'
            'DistinguishedName'
            'EmployeeID'
            'EmployeeNumber'
            'LastBadPasswordAttempt'
            'LastLogonDate'
            'Created'
            'Modified'
            'ProtectedFromAccidentalDeletion'
            'PrimaryGroup'
            'MemberOf'
            if ($ForestSchemaUsers.Name -contains 'ExtensionAttribute1') {
                'ExtensionAttribute1'
                'ExtensionAttribute2'
                'ExtensionAttribute3'
                'ExtensionAttribute4'
                'ExtensionAttribute5'
                'ExtensionAttribute6'
                'ExtensionAttribute7'
                'ExtensionAttribute8'
                'ExtensionAttribute9'
                'ExtensionAttribute10'
                'ExtensionAttribute11'
                'ExtensionAttribute12'
                'ExtensionAttribute13'
                'ExtensionAttribute14'
                'ExtensionAttribute15'
            }
        )
    }

    [string[]] $ExcludeProperty = '*Certificate', 'PropertyNames', '*Properties', 'PropertyCount', 'Certificates', 'nTSecurityDescriptor'

    Get-ADUser -Server $Domain -ResultPageSize 500000 -Filter * -Properties $Properties #| Select-Object -Property $Properties -ExcludeProperty $ExcludeProperty

    $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner
    Write-Verbose "Getting domain information - $Domain DomainUsersFullList Time: $EndUsers"
}
function Get-WinADForest {
    [CmdletBinding()]
    param()
    $Time = Start-TimeLog
    Write-Verbose 'Getting forest information - Forest'

    Get-ADForest #| Select-Object -Property * -ExcludeProperty PropertyNames, AddedProperties, RemovedProperties, ModifiedProperties, PropertyCount

    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting forest information - Forest Time: $EndTime"
}
function Get-WinADForestInfo {
    [CmdletBinding()]
    param(
        [PSCustomObject] $Forest
    )
    $Time = Start-TimeLog
    Write-Verbose 'Getting forest information - Forest Information'

    [ordered] @{
        'Name'                    = $Forest.Name
        'Root Domain'             = $Forest.RootDomain
        'Forest Functional Level' = $Forest.ForestMode
        'Domains Count'           = ($Forest.Domains).Count
        'Sites Count'             = ($Forest.Sites).Count
        'Domains'                 = ($Forest.Domains) -join ", "
        'Sites'                   = ($Forest.Sites) -join ", "
    }
    
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting forest information - Forest Information Time: $EndTime"
}
function Get-WinADForestInformation {
    [CmdletBinding()]
    param (
        [Object] $TypesRequired,
        [switch] $RequireTypes,
        [string] $PathToPasswords,
        [string] $PathToPasswordsHashes
    )
    Write-Verbose "Getting all information"
    $TimeToGenerateForest = Start-TimeLog
    if ($null -eq $TypesRequired) {
        # Gets all types
        Write-Verbose 'Get-WinADForestInformation - TypesRequired is null. Getting all.'
        $TypesRequired = Get-Types -Types ([ActiveDirectory])
    }

    $Data = [ordered] @{ }
    $Data.Forest = Get-WinADForest
    $Data.RootDSE = Get-WinADRootDSE
    Write-Verbose 'Getting forest information - ForestName & ForestNameDN & Domains list'
    $Data.ForestName = $Data.Forest.Name
    $Data.ForestNameDN = $Data.RootDSE.defaultNamingContext
    $Data.Domains = $Data.Forest.Domains

    $Data.ForestSchemaPropertiesComputers = Get-WinADForestSchemaPropertiesComputers
    $Data.ForestSchemaPropertiesUsers = Get-WinADForestSchemaPropertiesUsers


    ## Forest Information
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestInformation)) {
        $Data.ForestInformation = Get-WinADForestInfo -Forest $Data.Forest
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestUPNSuffixes)) {
        Write-Verbose 'Getting forest information - Forest UPNSuffixes'
        $Data.ForestUPNSuffixes = Get-WinADForestUPNSuffixes -Forest $Data.Forest
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestGlobalCatalogs)) {
        Write-Verbose 'Getting forest information - Forest GlobalCatalogs'
        $Data.ForestGlobalCatalogs = $Data.Forest.GlobalCatalogs
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSPNSuffixes)) {
        Write-Verbose 'Getting forest information - Forest SPNSuffixes'
        $Data.ForestSPNSuffixes = $Data.Forest.SPNSuffixes
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestFSMO)) {
        Write-Verbose 'Getting forest information - Forest FSMO'
        $Data.ForestFSMO = [ordered] @{
            'Domain Naming Master' = $Data.Forest.DomainNamingMaster
            'Schema Master'        = $Data.Forest.SchemaMaster
        }
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestDomainControllers)) {
        # External command from PSSharedGoods
        $Data.ForestDomainControllers = Get-WinADForestControllers
    }
    # Forest Sites
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites, [ActiveDirectory]::ForestSites1, [ActiveDirectory]::ForestSites2)) {
        Write-Verbose 'Getting forest information - Forest Sites'
        $Data.ForestSites = Get-WinADForestSites
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites1)) {
        Write-Verbose 'Getting forest information - Forest Sites1'
        $Data.ForestSites1 = Get-WinADForestSites1 -ForestSites $Data.ForestSites
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSites2)) {
        Write-Verbose 'Getting forest information - Forest Sites2'
        $Data.ForestSites2 = Get-WinADForestSites2 -ForestSites $Data.ForestSites
    }
    ## Forest Subnets
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnet , [ActiveDirectory]::ForestSubnets1, [ActiveDirectory]::ForestSubnets2)) {
        Write-Verbose 'Getting forest information - Forest Subnets'
        $Data.ForestSubnets = Get-WinADForestSubnets
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnets1)) {
        Write-Verbose 'Getting forest information - Forest Subnets1'
        $Data.ForestSubnets1 = Get-WinADForestSubnets1 -ForestSubnets $ForestSubnets
    }
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSubnets2)) {
        Write-Verbose 'Getting forest information - Forest Subnets2'
        $Data.ForestSubnets2 = Get-WinADForestSubnets2 -ForestSubnets $ForestSubnets
    }
    ## Forest Site Links
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestSiteLinks)) {
        Write-Verbose 'Getting forest information - Forest SiteLinks'
        $Data.ForestSiteLinks = Get-WinADForestSiteLinks
    }
    ## Forest Optional Features
    if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([ActiveDirectory]::ForestOptionalFeatures)) {
        Write-Verbose 'Getting forest information - Forest Optional Features'
        $Data.ForestOptionalFeatures = Get-WinADForestOptionalFeatures
    }
    $EndTimeForest = Stop-TimeLog -Time $TimeToGenerateForest -Continue
    Write-Verbose "Getting forest information - Time to generate: $EndTimeForest"
    ### Generate Data from Domains
    $Data.FoundDomains = [ordered]@{ }
    foreach ($Domain in $Data.Domains) {
        $Data.FoundDomains.$Domain = Get-WinADDomainInformation -Domain $Domain `
            -TypesRequired $TypesRequired `
            -PathToPasswords $PathToPasswords `
            -PathToPasswordsHashes $PathToPasswordsHashes `
            -ForestSchemaComputers $Data.ForestSchemaPropertiesComputers  `
            -ForestSchemaUsers $Data.ForestSchemaPropertiesUsers
    }
    return $Data
    $EndTimeAll = Stop-TimeLog -Time $TimeToGenerateForest
    Write-Verbose "Getting all information - Time to generate: $EndTimeAll"
}
function Get-WinADForestOptionalFeatures {
    [CmdletBinding()]
    param()
    $OptionalFeatures = $(Get-ADOptionalFeature -Filter * )
    $Optional = [ordered]@{
        'Recycle Bin Enabled'                          = 'N/A'
        'Privileged Access Management Feature Enabled' = 'N/A'
    }
    foreach ($Feature in $OptionalFeatures) {
        if ($Feature.Name -eq 'Recycle Bin Feature') {
            $Optional.'Recycle Bin Enabled' = $Feature.EnabledScopes.Count -gt 0
        }
        if ($Feature.Name -eq 'Privileged Access Management Feature') {
            $Optional.'Privileged Access Management Feature Enabled' = $Feature.EnabledScopes.Count -gt 0
        }
    }
    return $Optional
}
function Get-WinADForestSchemaPropertiesComputers {
    [CmdletBinding()]
    param(

    )
    Write-Verbose "Getting forest information - ForestSchemaPropertiesComputers"
    $Time = Start-TimeLog
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    @(
        $Schema.FindClass("computer").mandatoryproperties | Select-Object name, commonname, description, syntax
        $Schema.FindClass("computer").optionalproperties | Select-Object name, commonname, description, syntax #| Where-Object { $_.Name -eq 'ms-Mcs-AdmPwd' } # ft -AutoSize
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - ForestSchemaPropertiesComputers Time: $EndTime"
}


<#
Get-WinADForestSchemaProperties -Verbose
 
 
function Get-WinADForestSchemaProperties {
    [CmdletBinding()]
    param(
 
    )
    Write-Verbose "Getting forest information - ForestSchemaProperties"
    $Time = Start-TimeLog
 
    $Output = @{}
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    $Output.SchemaPropertiesUser = @(
        $Mandatory = $Schema.FindClass("user").mandatoryproperties #| Select-Object name, commonname, description, syntax #| export-csv user-mandatory-attributes.csv -Delimiter ';'
        foreach ($Object in $Mandatory) {
            [PSCustomobject] @{
                Name = $Object.Name
                CommonName = $Object.CommonName
                Description = $Object.Description
                Syntax = $Object.Syntax
            }
        }
        $Optional = $Schema.FindClass("user").optionalproperties # | Select-Object name, commonname, description, syntax #| export-csv user-optional-attributes.csv -Delimiter ';'
        foreach ($Object in $Optional) {
            [PSCustomobject] @{
                Name = $Object.Name
                CommonName = $Object.CommonName
                Description = $Object.Description
                Syntax = $Object.Syntax
            }
        }
    )
    $Output.SchemaPropertiesComputer = @(
        $Mandatory = $Schema.FindClass("computer").mandatoryproperties #| Select-Object name, commonname, description, syntax
        foreach ($Object in $Mandatory) {
            [PSCustomobject] @{
                Name = $Object.Name
                CommonName = $Object.CommonName
                Description = $Object.Description
                Syntax = $Object.Syntax
            }
        }
        $Optional = $Schema.FindClass("computer").optionalproperties #| Select-Object name, commonname, description, syntax #| Where-Object { $_.Name -eq 'ms-Mcs-AdmPwd' } # ft -AutoSize
        foreach ($Object in $Optional) {
            [PSCustomobject] @{
                Name = $Object.Name
                CommonName = $Object.CommonName
                Description = $Object.Description
                Syntax = $Object.Syntax
            }
        }
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - ForestSchemaProperties Time: $EndTime"
    return $Output
}
 
Get-WinADForestSchemaProperties -Verbose
 
#>

function Get-WinADForestSchemaPropertiesUsers {
    [CmdletBinding()]
    param(

    )
    Write-Verbose "Getting forest information - ForestSchemaPropertiesUsers"
    $Time = Start-TimeLog
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    @(
        $Schema.FindClass("user").mandatoryproperties | Select-Object name, commonname, description, syntax #| export-csv user-mandatory-attributes.csv -Delimiter ';'
        $Schema.FindClass("user").optionalproperties | Select-Object name, commonname, description, syntax #| export-csv user-optional-attributes.csv -Delimiter ';'
    )
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner
    Write-Verbose "Getting domain information - ForestSchemaPropertiesUsers Time: $EndTime"
}
function Get-WinADForestSiteLinks {
    [CmdletBinding()]
    param(

    )
    $ExludedProperties = @(
        'PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount'
    )
    $Properties = @(
        'Name', 'Cost', 'ReplicationFrequencyInMinutes', 'ReplInterval',
        'ReplicationSchedule', 'Created', 'Modified', 'Deleted', 'InterSiteTransportProtocol',
        'DistinguishedName', 'ProtectedFromAccidentalDeletion'
        #siteList,nTSecurityDescriptor

    )

    return Get-ADReplicationSiteLink -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}
function Get-WinADForestSites {
    [CmdletBinding()]
    param(

    )
    $ExludedProperties = @(
        'PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount'
    )

    $Properties = @('Name',
        'DisplayName', 'Description', 'CanonicalName', 'DistinguishedName', 'Location', 'ManagedBy', 'Created', 'Modified', 'Deleted',
        'ProtectedFromAccidentalDeletion', 'RedundantServerTopologyEnabled',
        'AutomaticInterSiteTopologyGenerationEnabled',
        'AutomaticTopologyGenerationEnabled',
        'Subnets',
        #'nTSecurityDescriptor'
        #LastKnownParent
        #instanceType
        #InterSiteTopologyGenerator
        #dSCorePropagationData
        #ReplicationSchedule.RawSchedule -join ','
        #msExchServerSiteBL -join ','
        #siteObjectBL -join ','
        #systemFlags
        #ObjectGUID
        #ObjectCategory
        #ObjectClass
        #ScheduleHashingEnabled
        'sDRightsEffective',
        'TopologyCleanupEnabled',
        'TopologyDetectStaleEnabled',
        'TopologyMinimumHopsEnabled',
        'UniversalGroupCachingEnabled',
        'UniversalGroupCachingRefreshSite',
        'WindowsServer2000BridgeheadSelectionMethodEnabled',
        'WindowsServer2000KCCISTGSelectionBehaviorEnabled',
        'WindowsServer2003KCCBehaviorEnabled',
        'WindowsServer2003KCCIgnoreScheduleEnabled',
        'WindowsServer2003KCCSiteLinkBridgingEnabled'
    )
    return Get-ADReplicationSite -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}









function Get-WinADForestSites1 {
    [CmdletBinding()]
    param(
        [Array] $ForestSites
    )
    @(
        foreach ($Sites in $ForestSites) {
            [PSCustomObject][ordered] @{
                'Name'        = $Sites.Name
                'Description' = $Sites.Description
                #'sD Rights Effective' = $Sites.sDRightsEffective
                'Protected'   = $Sites.ProtectedFromAccidentalDeletion
                'Modified'    = $Sites.Modified
                'Created'     = $Sites.Created
                'Deleted'     = $Sites.Deleted
            }
        }
    )
}
function Get-WinADForestSites2 {
    [CmdletBinding()]
    param(
        [Array] $ForestSites
    )
    @(
        foreach ($Sites in $ForestSites) {
            [PSCustomObject][ordered] @{
                'Name'                                = $Sites.Name
                'Topology Cleanup Enabled'            = $Sites.TopologyCleanupEnabled
                'Topology Detect Stale Enabled'       = $Sites.TopologyDetectStaleEnabled
                'Topology Minimum Hops Enabled'       = $Sites.TopologyMinimumHopsEnabled
                'Universal Group Caching Enabled'     = $Sites.UniversalGroupCachingEnabled
                'Universal Group Caching RefreshSite' = $Sites.UniversalGroupCachingRefreshSite
            }
        }
    )
}
function Get-WinADForestSubnets {
    [CmdletBinding()]
    param(

    )
    $ExludedProperties = @(
        'PropertyNames', 'AddedProperties', 'RemovedProperties', 'ModifiedProperties', 'PropertyCount'
    )
    $Properties = @(
        'Name', 'DisplayName', 'Description', 'Site', 'ProtectedFromAccidentalDeletion', 'Created', 'Modified', 'Deleted'
    )

    return Get-ADReplicationSubnet -Filter * -Properties $Properties | Select-Object -Property $Properties -ExcludeProperty $ExludedProperties
}
function Get-WinADForestSubnets1 {
    param(
        [Array] $ForestSubnets
    )
    @(
        foreach ($Subnets in $ForestSubnets) {
            [PSCustomObject][ordered] @{
                'Name'        = $Subnets.Name
                'Description' = $Subnets.Description
                'Protected'   = $Subnets.ProtectedFromAccidentalDeletion
                'Modified'    = $Subnets.Modified
                'Created'     = $Subnets.Created
                'Deleted'     = $Subnets.Deleted
            }
        }
    )
}
function Get-WinADForestSubnets2 {
    param(
        [Array] $ForestSubnets
    )
    @(
        foreach ($Subnets in $ForestSubnets) {
            [PSCustomObject][ordered] @{
                'Name' = $Subnets.Name
                'Site' = $Subnets.Site
            }
        }
    )
}
function Get-WinADForestUPNSuffixes {
    param(
        [PSCustomObject] $Forest
    )
    @(
        [PSCustomObject] @{
            Name = $Forest.RootDomain
            Type = 'Primary / Default UPN'
        }
        foreach ($UPN in $Forest.UPNSuffixes) {
            [PSCustomObject] @{
                Name = $UPN
                Type = 'Secondary'
            }
        }
    )
}
function Get-WinADRootDSE {
    [CmdletBinding()]
    param(
        [string] $Domain
    )
    $Time = Start-TimeLog
    try {
        if ($Domain -ne '') {
            Write-Verbose "Getting domain information - $Domain DomainRootDSE"
            Get-ADRootDSE -Properties * -Server $Domain
        } else {
            Write-Verbose 'Getting forest information - RootDSE'
            Get-ADRootDSE -Properties *  #| Select-Object -Property * -ExcludeProperty PropertyNames, AddedProperties, RemovedProperties, ModifiedProperties, PropertyCount
        }
    } catch {
        Write-Warning "Getting domain information - $Domain DomainRootDSE Error: $($_.Error)"
    }
    $EndTime = Stop-TimeLog -Time $Time -Option OneLiner

    if ($Domain -ne '') {
        Write-Verbose "Getting domain information - $Domain DomainRootDSE Time: $EndTime"
    } else {
        Write-Verbose "Getting forest information - RootDSE Time: $EndTime"
    }
}
function Get-WinGroupMembers {
    [CmdletBinding()]
    param(
        [System.Object[]] $Groups,
        [string] $Domain,
        [System.Object[]] $ADCatalog,
        [System.Object[]] $ADCatalogUsers,
        [ValidateSet("Recursive", "Standard")][String] $Option
    )
    if ($Option -eq 'Recursive') {
        [Array] $GroupMembersRecursive = foreach ($Group in $Groups) {
            try {
                $GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.'Group SID' -Recursive -ErrorAction Stop
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Warning "Couldn't get information about group $($Group.Name) with SID $($Group.'Group SID') error: $ErrorMessage"
                continue
            }
            foreach ($Member in $GroupMembership) {
                $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $Member.DistinguishedName)
                [PSCustomObject][ordered] @{
                    'Group Name'                        = $Group.'Group Name'
                    'Group SID'                         = $Group.'Group SID'
                    'Group Category'                    = $Group.'Group Category'
                    'Group Scope'                       = $Group.'Group Scope'
                    'High Privileged Group'             = if ($Group.adminCount -eq 1) { $True } else { $False }
                    'Display Name'                      = $Object.DisplayName
                    'Name'                              = $Member.Name
                    'User Principal Name'               = $Object.UserPrincipalName
                    'Sam Account Name'                  = $Object.SamAccountName
                    'Email Address'                     = $Object.EmailAddress
                    'PasswordExpired'                   = $Object.PasswordExpired
                    'PasswordLastSet'                   = $Object.PasswordLastSet
                    'PasswordNotRequired'               = $Object.PasswordNotRequired
                    'PasswordNeverExpires'              = $Object.PasswordNeverExpires
                    'Enabled'                           = $Object.Enabled
                    'SID'                               = $Member.SID.Value
                    'Manager'                           = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).Name
                    'ManagerEmail'                      = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).EmailAddress
                    'DateExpiry'                        = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") # -Verbose
                    "DaysToExpire"                      = (Convert-TimeToDays -StartTime GET-DATE -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate"             = $Object.AccountExpirationDate
                    "AccountLockoutTime"                = $Object.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                    "BadLogonCount"                     = $Object.BadLogonCount
                    "CannotChangePassword"              = $Object.CannotChangePassword
                    "CanonicalName"                     = $Object.CanonicalName

                    'Given Name'                        = $Object.GivenName
                    'Surname'                           = $Object.Surname

                    "Description"                       = $Object.Description
                    "DistinguishedName"                 = $Object.DistinguishedName
                    "EmployeeID"                        = $Object.EmployeeID
                    "EmployeeNumber"                    = $Object.EmployeeNumber
                    "LastBadPasswordAttempt"            = $Object.LastBadPasswordAttempt
                    "LastLogonDate"                     = $Object.LastLogonDate

                    "Created"                           = $Object.Created
                    "Modified"                          = $Object.Modified
                    "Protected"                         = $Object.ProtectedFromAccidentalDeletion
                    "Domain"                            = $Domain
                }
                # $Member
            }
        }
        if ($GroupMembersRecursive.Count -eq 1) {
            return , $GroupMembersRecursive
        }
        return $GroupMembersRecursive
    }
    if ($Option -eq 'Standard') {
        [Array] $GroupMembersDirect = foreach ($Group in $Groups) {
            foreach ($Member in $Group.'Group Members DN') {
                $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $Member)
                [PSCustomObject][ordered] @{
                    'Group Name'                        = $Group.'Group Name'
                    'Group SID'                         = $Group.'Group SID'
                    'Group Category'                    = $Group.'Group Category'
                    'Group Scope'                       = $Group.'Group Scope'
                    'DisplayName'                       = $Object.DisplayName
                    'High Privileged Group'             = if ($Group.adminCount -eq 1) { $True } else { $False }
                    'UserPrincipalName'                 = $Object.UserPrincipalName
                    'SamAccountName'                    = $Object.SamAccountName
                    'EmailAddress'                      = $Object.EmailAddress
                    'PasswordExpired'                   = $Object.PasswordExpired
                    'PasswordLastSet'                   = $Object.PasswordLastSet
                    'PasswordNotRequired'               = $Object.PasswordNotRequired
                    'PasswordNeverExpires'              = $Object.PasswordNeverExpires
                    'Enabled'                           = $Object.Enabled
                    'Manager'                           = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).Name
                    'ManagerEmail'                      = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $Object.Manager).EmailAddress
                    'DateExpiry'                        = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") #-Verbose
                    "DaysToExpire"                      = (Convert-TimeToDays -StartTime GET-DATE -EndTime (Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed")))
                    "AccountExpirationDate"             = $Object.AccountExpirationDate
                    "AccountLockoutTime"                = $Object.AccountLockoutTime
                    "AllowReversiblePasswordEncryption" = $Object.AllowReversiblePasswordEncryption
                    "BadLogonCount"                     = $Object.BadLogonCount
                    "CannotChangePassword"              = $Object.CannotChangePassword
                    "CanonicalName"                     = $Object.CanonicalName

                    "Description"                       = $Object.Description
                    "DistinguishedName"                 = $Object.DistinguishedName
                    "EmployeeID"                        = $Object.EmployeeID
                    "EmployeeNumber"                    = $Object.EmployeeNumber
                    "LastBadPasswordAttempt"            = $Object.LastBadPasswordAttempt
                    "LastLogonDate"                     = $Object.LastLogonDate

                    'Name'                              = $Object.Name
                    'SID'                               = $Object.SID.Value
                    'GivenName'                         = $Object.GivenName
                    'Surname'                           = $Object.Surname

                    "Created"                           = $Object.Created
                    "Modified"                          = $Object.Modified
                    "Protected"                         = $Object.ProtectedFromAccidentalDeletion
                    "Domain"                            = $Domain
                }
            }
        }
        if ($GroupMembersDirect.Count -eq 1) {
            return , $GroupMembersDirect
        }
        return $GroupMembersDirect
    }
}
function Get-WinGroups {
    [CmdletBinding()]
    param (
        [System.Object[]] $Groups,
        [System.Object[]] $Users,
        [string] $Domain
    )
    $ReturnGroups = foreach ($Group in $Groups) {
        $User = $Users | & { process { if ($_.DistinguishedName -eq $Group.ManagedBy ) { $_ } } } # | Where-Object { $_.DistinguishedName -eq $Group.ManagedBy }

        [PsCustomObject][ordered] @{
            'Group Name'            = $Group.Name
            #'Group Display Name' = $Group.DisplayName
            'Group Category'        = $Group.GroupCategory
            'Group Scope'           = $Group.GroupScope
            'Group SID'             = $Group.SID.Value
            'High Privileged Group' = if ($Group.adminCount -eq 1) { $True } else { $False }
            'Member Count'          = $Group.Members.Count
            'MemberOf Count'        = $Group.MemberOf.Count
            'Manager'               = $User.Name
            'Manager Email'         = $User.EmailAddress
            'Group Members'         = (Get-ADObjectFromDistingusishedName -ADCatalog $Data.DomainUsersFullList, $Data.DomainComputersFullList, $Data.DomainGroupsFullList -DistinguishedName $Group.Members -Type 'SamAccountName')
            'Group Members DN'      = $Group.Members
            "Domain"                = $Domain
        }
    }
    return $ReturnGroups
}
function Get-WinUsers {
    [CmdletBinding()]
    param(
        [System.Object[]] $Users,
        [System.Object[]] $ADCatalog,
        [System.Object[]] $ADCatalogUsers,
        [string] $Domain
    )
    [DateTime] $CurrentDate = Get-Date # [DateTime]::Today
    $UserList = foreach ($U in $Users) {
        [PsCustomObject][Ordered] @{
            'Name'                              = $U.Name
            'UserPrincipalName'                 = $U.UserPrincipalName
            'SamAccountName'                    = $U.SamAccountName
            'Display Name'                      = $U.DisplayName
            'Given Name'                        = $U.GivenName
            'Surname'                           = $U.Surname
            'EmailAddress'                      = $U.EmailAddress
            'PasswordExpired'                   = $U.PasswordExpired
            'PasswordLastSet'                   = $U.PasswordLastSet
            'Password Last Changed'             = if ($U.PasswordLastSet -ne $Null) { "$(-$($U.PasswordLastSet - $CurrentDate).Days) days" } else { 'N/A'}
            'PasswordNotRequired'               = $U.PasswordNotRequired
            'PasswordNeverExpires'              = $U.PasswordNeverExpires
            'Enabled'                           = $U.Enabled
            'Manager'                           = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $U.Manager).Name
            'Manager Email'                     = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalogUsers -DistinguishedName $U.Manager).EmailAddress
            'DateExpiry'                        = Convert-ToDateTime -Timestring $($U."msDS-UserPasswordExpiryTimeComputed") -Verbose
            "DaysToExpire"                      = (Convert-TimeToDays -StartTime $CurrentDate -EndTime (Convert-ToDateTime -Timestring $($U."msDS-UserPasswordExpiryTimeComputed")))
            "AccountExpirationDate"             = $U.AccountExpirationDate
            "AccountLockoutTime"                = $U.AccountLockoutTime
            "AllowReversiblePasswordEncryption" = $U.AllowReversiblePasswordEncryption
            "BadLogonCount"                     = $U.BadLogonCount
            "CannotChangePassword"              = $U.CannotChangePassword
            "CanonicalName"                     = $U.CanonicalName

            "Description"                       = $U.Description
            "DistinguishedName"                 = $U.DistinguishedName
            "EmployeeID"                        = $U.EmployeeID
            "EmployeeNumber"                    = $U.EmployeeNumber
            "LastBadPasswordAttempt"            = $U.LastBadPasswordAttempt
            "LastLogonDate"                     = $U.LastLogonDate

            "Created"                           = $U.Created
            "Modified"                          = $U.Modified
            "Protected"                         = $U.ProtectedFromAccidentalDeletion

            "Primary Group"                     = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $U.PrimaryGroup -Type 'SamAccountName')
            "Member Of"                         = (Get-ADObjectFromDistingusishedName -ADCatalog $ADCatalog -DistinguishedName $U.MemberOf -Type 'SamAccountName' -Splitter ', ')
            "Domain"                            = $Domain
        }

    }
    return $UserList
}

<# List of fields
'Name', 'UserPrincipalName', 'SamAccountName', 'Enabled', 'PasswordLastSet,'Password Last Changed', 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired',
'EmailAddress', 'Display Name', 'Given Name', 'Surname', 'Manager', 'Manager Email',
'DateExpiry', "DaysToExpire", "AccountExpirationDate", "AccountLockoutTime", "AllowReversiblePasswordEncryption", "BadLogonCount",
"CannotChangePassword", "CanonicalName", "Description", "DistinguishedName", "EmployeeID", "EmployeeNumber", "LastBadPasswordAttempt",
"LastLogonDate", "Created", "Modified", "Protected", "Primary Group", "Member Of", "Domain"
#>

Function Set-TrustAttributes {
    [cmdletbinding()]
    Param(
        [parameter(Mandatory = $false, ValueFromPipeline = $True)][int32]$Value
    )
    [String[]]$TrustAttributes = @(
        Foreach ($V in $Value) {
            if ([int32]$V -band 0x00000001) { "Non Transitive" }
            if ([int32]$V -band 0x00000002) { "UpLevel" }
            if ([int32]$V -band 0x00000004) { "Quarantaine (SID Filtering enabled)" } #SID Filtering
            if ([int32]$V -band 0x00000008) { "Forest Transitive" }
            if ([int32]$V -band 0x00000010) { "Cross Organization (Selective Authentication enabled)" } #Selective Auth
            if ([int32]$V -band 0x00000020) { "Within Forest" }
            if ([int32]$V -band 0x00000040) { "Treat as External" }
            if ([int32]$V -band 0x00000080) { "Uses RC4 Encryption" }
        }
    )
    return $TrustAttributes
}


Export-ModuleMember `
    -Function @('Get-WinADDomainInformation','Get-WinADForestInformation') `
    -Alias @()