PSWinDocumentation.AD.psm1
Add-Type -TypeDefinition @"
using System; namespace PSWinDocumentation { [Flags] 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) if ($null -eq $DistinguishedName) {return} $FoundObjects = foreach ($Catalog in $ADCatalog) { foreach ($Object in $DistinguishedName) { $ADObject = foreach ($_ in $Catalog) {if ($_.DistinguishedName -eq $Object) {$_}} if ($ADObject) {if ($Type -eq '') {$ADObject} else {$ADObject.$Type}} } } if ($Splitter) {return ($FoundObjects | Sort-Object) -join $Splitter} else {return $FoundObjects | Sort-Object} } function Get-WinADAccounts { [CmdletBinding()] param([Object] $UserNameList, [Array[]] $ADCatalog) $Accounts = foreach ($User in $UserNameList) {foreach ($Catalog in $ADCatalog) {foreach ($_ in $Catalog) {if ($_.SamAccountName -eq $User) {$_}}}} return $Accounts } function Get-WinADDomain { [CmdletBinding()] param([string] $Domain = $Env:USERDNSDOMAIN) $Time = Start-TimeLog Write-Verbose 'Getting forest information - Forest' try {Get-ADDomain -Server $Domain -ErrorAction Stop} catch {$null} $EndTime = Stop-TimeLog -Time $Time -Option OneLiner Write-Verbose "Getting forest information - Forest Time: $EndTime" } function Get-WinADDomainBitlocker { param([string] $Domain = $Env:USERDNSDOMAIN, [Array] $Computers) $Properties = @('Name', 'OperatingSystem', 'DistinguishedName') 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' 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 = $Env:USERDNSDOMAIN, [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' }) } Get-ADComputer -Server $Domain -Filter * -ResultPageSize 500000 -Properties $Properties -ErrorAction SilentlyContinue $EndUsers = Stop-TimeLog -Time $TimeUsers -Option OneLiner Write-Verbose "Getting domain information - $Domain DomainComputersFullList Time: $EndUsers" } function Get-WinADDomainDNSData { param([string] $Domain = $Env:USERDNSDOMAIN) $DnsRecords = "_kerberos._tcp.$Domain", "_ldap._tcp.$Domain" $DNSData = foreach ($DnsRecord in $DnsRecords) { $Value = Resolve-DnsName -Name $DnsRecord -Type SRV -Verbose:$false -ErrorAction SilentlyContinue if ($null -eq $Value) {Write-Warning 'Getting domain information - DomainDNSSRV / DomainDNSA - Failed!'} $Value } $ReturnData = @{} $ReturnData.Srv = foreach ($V in $DNSData) {if ($V.QueryType -eq 'SRV') {$V | Select-Object Target, NameTarget, Priority, Weight, Port, Name}} $ReturnData.A = foreach ($V in $DNSData) {if ($V.QueryType -ne 'SRV') {$V | Select-Object Address, IPAddress, IP4Address, Name, Type, DataLength, TTL}} return $ReturnData } function Get-WinADDomainFineGrainedPolicies { [CmdletBinding()] param([string] $Domain = $Env:USERDNSDOMAIN) $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 'Distinguished Name' = $Policy.DistinguishedName } } return $FineGrainedPolicies } function Get-WinADDomainFineGrainedPoliciesUsers { [CmdletBinding()] param([Array] $DomainFineGrainedPolicies, [Array] $DomainUsersFullList, [Array] $DomainGroupsFullList) $PolicyUsers = foreach ($Policy in $DomainFineGrainedPolicies) { $Users = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $U} $Groups = foreach ($U in $Policy.'Applies To') {Get-ADObjectFromDistingusishedName -ADCatalog $DomainGroupsFullList -DistinguishedName $U} foreach ($User in $Users) { [pscustomobject][ordered] @{'Policy Name' = $Policy.Name Name = $User.Name SamAccountName = $User.SamAccountName Type = $User.ObjectClass SID = $User.SID } } foreach ($Group in $Groups) { [pscustomobject][ordered] @{'Policy Name' = $Policy.Name Name = $Group.Name SamAccountName = $Group.SamAccountName Type = $Group.ObjectClass SID = $Group.SID } } } return $PolicyUsers } function Get-WinADDomainFineGrainedPoliciesUsersExtended { [CmdletBinding()] param([Array] $DomainFineGrainedPolicies, [Array] $DomainUsersFullList, [Array] $DomainGroupsFullList, [string] $Domain = ($Env:USERDNSDOMAIN).ToLower()) $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 $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 $DomainUsersFullList -DistinguishedName $User.Manager).Name 'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $User.Manager).EmailAddress 'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") "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 $DomainGroupsFullList -DistinguishedName $U} foreach ($Group in $Groups) { $GroupMembership = Get-ADGroupMember -Server $Domain -Identity $Group.SID -Recursive foreach ($Member in $GroupMembership) { $Object = (Get-ADObjectFromDistingusishedName -ADCatalog $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 $DomainUsersFullList -DistinguishedName $Object.Manager).Name 'ManagerEmail' = (Get-ADObjectFromDistingusishedName -ADCatalog $DomainUsersFullList -DistinguishedName $Object.Manager).EmailAddress 'DateExpiry' = Convert-ToDateTime -Timestring $($Object."msDS-UserPasswordExpiryTimeComputed") "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-WinADDomainFSMO { [CmdletBinding()] param([string] $Domain = $Env:USERDNSDOMAIN, [Microsoft.ActiveDirectory.Management.ADDomain] $DomainInformation) Write-Verbose "Getting domain information - $Domain DomainFSMO" $Time = Start-TimeLog [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 = $Env:USERDNSDOMAIN) 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([Microsoft.ActiveDirectory.Management.ADDomain] $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" foreach ($_ in $DomainGroups) {if ($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 = $Env:USERDNSDOMAIN, [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 = @{} $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, [PSWinDocumentation.ActiveDirectory[]] $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 ([PSWinDocumentation.ActiveDirectory]) } $TimeToGenerate = Start-TimeLog if ($null -eq $ForestSchemaComputers) {$ForestSchemaComputers = Get-WinADForestSchemaPropertiesComputers} if ($null -eq $ForestSchemaUsers) {$ForestSchemaUsers = Get-WinADForestSchemaPropertiesUsers} $Data = [ordered] @{} $Data.DomainRootDSE = Get-WinADRootDSE -Domain $Domain Write-Verbose "Getting domain information - $Domain DomainInformation" $Data.DomainInformation = Get-WinADDomain -Domain $Domain if ($null -eq $Data.DomainInformation -or $null -eq $Data.DomainRootDSE) {return} $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 @([PSWinDocumentation.ActiveDirectory]::DomainComputersAll, [PSWinDocumentation.ActiveDirectory]::DomainComputersAllCount, [PSWinDocumentation.ActiveDirectory]::DomainServers, [PSWinDocumentation.ActiveDirectory]::DomainServersCount, [PSWinDocumentation.ActiveDirectory]::DomainComputers, [PSWinDocumentation.ActiveDirectory]::DomainComputersCount, [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown, [PSWinDocumentation.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 [PSWinDocumentation.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 [PSWinDocumentation.ActiveDirectory]::DomainServers) { Write-Verbose "Getting domain information - $Domain DomainServers" $Data.DomainServers = $Data.DomainComputersAll | & {process {if ($_.OperatingSystem -like 'Windows Server*') {$_}}} } if ($TypesRequired -contains [PSWinDocumentation.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 [PSWinDocumentation.ActiveDirectory]::DomainComputers) { Write-Verbose "Getting domain information - $Domain DomainComputers" $Data.DomainComputers = $Data.DomainComputersAll | & {process {if ($_.OperatingSystem -notlike 'Windows Server*' -and $null -ne $_.OperatingSystem) {$_}}} } if ($TypesRequired -contains [PSWinDocumentation.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 [PSWinDocumentation.ActiveDirectory]::DomainComputersUnknown) { Write-Verbose "Getting domain information - $Domain DomainComputersUnknown" $Data.DomainComputersUnknown = $Data.DomainComputersAll | & {process {if ($null -eq $_.OperatingSystem) {$_}}} } if ($TypesRequired -contains [PSWinDocumentation.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 [PSWinDocumentation.ActiveDirectory]::DomainRIDs) {$Data.DomainRIDs = Get-WinADDomainRIDs -DomainInformation $Data.DomainInformation -Domain $Domain} if (Find-TypesNeeded -TypesRequired $TypesRequired @([PSWinDocumentation.ActiveDirectory]::DomainGUIDS, [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL, [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsExtended)) {$Data.DomainGUIDS = Get-WinADDomainGUIDs -RootDSE $Data.DomainRootDSE -Domain $Domain} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAuthenticationPolicies)) { Write-Verbose "Getting domain information - $Domain DomainAuthenticationPolicies" $Data.DomainAuthenticationPolicies = $(Get-ADAuthenticationPolicy -Server $Domain -LDAPFilter '(name=AuthenticationPolicy*)') } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainCentralAccessPolicies)) { Write-Verbose "Getting domain information - $Domain DomainCentralAccessPolicies" $Data.DomainCentralAccessPolicies = $(Get-ADCentralAccessPolicy -Server $Domain -Filter *) } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainCentralAccessRules)) { Write-Verbose "Getting domain information - $Domain DomainCentralAccessRules" $Data.DomainCentralAccessRules = $(Get-ADCentralAccessRule -Server $Domain -Filter *) } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainClaimTransformPolicies)) { Write-Verbose "Getting domain information - $Domain DomainClaimTransformPolicies" $Data.DomainClaimTransformPolicies = $(Get-ADClaimTransformPolicy -Server $Domain -Filter *) } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainClaimTypes)) { Write-Verbose "Getting domain information - $Domain DomainClaimTypes" $Data.DomainClaimTypes = $(Get-ADClaimType -Server $Domain -Filter *) } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainDNSSRV, [PSWinDocumentation.ActiveDirectory]::DomainDNSA)) { Write-Verbose "Getting domain information - $Domain DomainDNSSRV / DomainDNSA" $Data.DomainDNSData = Get-WinADDomainDNSData -Domain $Domain $Data.DomainDNSSrv = $Data.DomainDNSData.SRV $Data.DomainDNSA = $Data.DomainDNSData.A } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFSMO, [PSWinDocumentation.ActiveDirectory]::DomainTrusts, [PSWinDocumentation.ActiveDirectory]::DomainTrustsClean)) {$Data.DomainFSMO = Get-WinADDomainFSMO -Domain $Domain -DomainInformation $Data.DomainInformation} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainTrustsClean, [PSWinDocumentation.ActiveDirectory]::DomainTrusts)) {$Data.DomainTrustsClean = Get-WinADDomainTrustsClean -Domain $Domain} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainTrusts)) {$Data.DomainTrusts = Get-WinADDomainTrusts -DomainPDC $Data.DomainFSMO.'PDC Emulator' -Trusts $Data.DomainTrustsClean -Domain $Domain} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupPolicies, [PSWinDocumentation.ActiveDirectory]::DomainGroupPoliciesDetails, [PSWinDocumentation.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') 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"} if ($null -eq $XmlGPReport.GPO.User.ExtensionData) {$UserSettingsConfigured = $false} else {$UserSettingsConfigured = $true} if ($null -eq $XmlGPReport.GPO.Computer.ExtensionData) {$ComputerSettingsConfigured = $false} else {$ComputerSettingsConfigured = $true} [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' } } 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 @([PSWinDocumentation.ActiveDirectory]::DomainBitlocker)) {} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainLAPS)) {$Data.DomainLAPS = Get-WinADDomainLAPS -Domain $Domain -Computers $Data.DomainComputersFullList} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnits, [PSWinDocumentation.ActiveDirectory]::DomainContainers, [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsDN, [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsACL, [PSWinDocumentation.ActiveDirectory]::DomainOrganizationalUnitsBasicACL, [PSWinDocumentation.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 = @($Data.DomainInformation.DistinguishedName $Data.DomainOrganizationalUnitsClean.DistinguishedName $Data.DomainContainers.DistinguishedName) $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 } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainUsers, [PSWinDocumentation.ActiveDirectory]::DomainUsersAll, [PSWinDocumentation.ActiveDirectory]::DomainUsersSystemAccounts, [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiring, [PSWinDocumentation.ActiveDirectory]::DomainUsersNeverExpiringInclDisabled, [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredInclDisabled, [PSWinDocumentation.ActiveDirectory]::DomainUsersExpiredExclDisabled, [PSWinDocumentation.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} Write-Verbose "Getting domain information - $Domain DomainUsersSystemAccounts" $Data.DomainUsersSystemAccounts = $Data.DomainUsers | Where-Object {$_.PasswordNotRequired -eq $true} Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiring" $Data.DomainUsersNeverExpiring = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $true -and $_.Enabled -eq $true -and $_.PasswordNotRequired -eq $false} Write-Verbose "Getting domain information - $Domain DomainUsersNeverExpiringInclDisabled" $Data.DomainUsersNeverExpiringInclDisabled = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $true -and $_.PasswordNotRequired -eq $false} Write-Verbose "Getting domain information - $Domain DomainUsersExpiredInclDisabled" $Data.DomainUsersExpiredInclDisabled = $Data.DomainUsers | Where-Object {$_.PasswordNeverExpires -eq $false -and $_.DaysToExpire -le 0 -and $_.PasswordNotRequired -eq $false} 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} 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 @([PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPolicies)) { Write-Verbose "Getting domain information - $Domain DomainFineGrainedPolicies" $Data.DomainFineGrainedPolicies = Get-WinADDomainFineGrainedPolicies -Domain $Domain } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsers)) { Write-Verbose "Getting domain information - $Domain DomainFineGrainedPoliciesUsers" $Data.DomainFineGrainedPoliciesUsers = Get-WinADDomainFineGrainedPoliciesUsers -DomainFineGrainedPolicies $Data.DomainFineGrainedPolicies -DomainUsersFullList $Data.DomainUsersFullList -DomainGroupsFullList $Data.DomainGroupsFullList } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainFineGrainedPoliciesUsersExtended)) {$Data.DomainFineGrainedPoliciesUsersExtended = Get-WinADDomainFineGrainedPoliciesUsersExtended -DomainFineGrainedPolicies $Data.DomainFineGrainedPoliciesUsers -DomainUsersFullList $Data.DomainUsersFullList -DomainGroupsFullList $Data.DomainGroupsFullList -Domain $Domain} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroups, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainGroups, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainGroups, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviliged)) {$Data.DomainGroupsPriviliged = Get-DomainGroupsPriviliged -DomainGroups $Data.DomainGroups -DomainInformation $Data.DomainInformation} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecial, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembers, [PSWinDocumentation.ActiveDirectory]::DomainGroupsSpecialMembersRecursive)) { Write-Verbose "Getting domain information - $Domain DomainGroupMembersSpecialRecursive" $Data.DomainGroupsSpecialMembers = $Data.DomainGroupsMembers | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object * Write-Verbose "Getting domain information - $Domain DomainGroupsSpecialMembersRecursive" $Data.DomainGroupsSpecialMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {($_.'Group SID').Length -eq 12} | Select-Object * } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembers, [PSWinDocumentation.ActiveDirectory]::DomainGroupsPriviligedMembersRecursive)) { Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembers" $Data.DomainGroupsPriviligedMembers = $Data.DomainGroupsMembers | Where-Object {$Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object * Write-Verbose "Getting domain information - $Domain DomainGroupsPriviligedMembersRecursive" $Data.DomainGroupsPriviligedMembersRecursive = $Data.DomainGroupsMembersRecursive | Where-Object {$Data.DomainGroupsPriviliged.'Group SID' -contains ($_.'Group SID')} | Select-Object * } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainAdministrators, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainAdministratorsRecursive, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministrators, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainEnterpriseAdministratorsRecursive, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDataUsers, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords, [PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash, [PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires, [PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups, [PSWinDocumentation.ActiveDirectory]::DomainPasswordStats, [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDataPasswords, [PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash, [PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled, [PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires, [PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing, [PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins, [PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups, [PSWinDocumentation.ActiveDirectory]::DomainPasswordStats, [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword, [PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled, [PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainPasswordClearTextPassword)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordClearTextPassword" $Data.DomainPasswordClearTextPassword = $PasswordsQuality.DomainPasswordClearTextPassword } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordLMHash)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordLMHash" $Data.DomainPasswordLMHash = $PasswordsQuality.DomainPasswordLMHash } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordEmptyPassword)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordEmptyPassword" $Data.DomainPasswordEmptyPassword = $PasswordsQuality.DomainPasswordEmptyPassword } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPassword)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPassword" $Data.DomainPasswordWeakPassword = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPassword } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordEnabled)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordEnabled" $Data.DomainPasswordWeakPasswordEnabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordEnabled } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordDisabled)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordDisabled" $Data.DomainPasswordWeakPasswordDisabled = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordDisabled } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordWeakPasswordList)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordWeakPasswordList" $Data.DomainPasswordWeakPasswordList = $Data.DomainPasswordDataPasswords.DomainPasswordWeakPasswordList } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDefaultComputerPassword)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordDefaultComputerPassword" $Data.DomainPasswordDefaultComputerPassword = $PasswordsQuality.DomainPasswordDefaultComputerPassword } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNotRequired)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNotRequired" $Data.DomainPasswordPasswordNotRequired = $PasswordsQuality.DomainPasswordPasswordNotRequired } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPasswordNeverExpires)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordPasswordNeverExpires" $Data.DomainPasswordPasswordNeverExpires = $PasswordsQuality.DomainPasswordPasswordNeverExpires } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordAESKeysMissing)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordAESKeysMissing" $Data.DomainPasswordAESKeysMissing = $PasswordsQuality.DomainPasswordAESKeysMissing } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordPreAuthNotRequired)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordPreAuthNotRequired" $Data.DomainPasswordPreAuthNotRequired = $PasswordsQuality.DomainPasswordPreAuthNotRequired } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDESEncryptionOnly)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordDESEncryptionOnly" $Data.DomainPasswordDESEncryptionOnly = $PasswordsQuality.DomainPasswordDESEncryptionOnly } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDelegatableAdmins)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordDelegatableAdmins" $Data.DomainPasswordDelegatableAdmins = $PasswordsQuality.DomainPasswordDelegatableAdmins } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordDuplicatePasswordGroups)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordDuplicatePasswordGroups" $Data.DomainPasswordDuplicatePasswordGroups = $PasswordsQuality.DomainPasswordDuplicatePasswordGroups } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPassword" $Data.DomainPasswordHashesWeakPassword = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPassword } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordEnabled" $Data.DomainPasswordHashesWeakPasswordEnabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordEnabled } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordDisabled)) { Write-Verbose "Getting domain password information - $Domain DomainPasswordHashesWeakPasswordDisabled" $Data.DomainPasswordHashesWeakPasswordDisabled = $Data.DomainPasswordDataPasswordsHashes.DomainPasswordWeakPasswordDisabled } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPassword)) {$Stats.'Weak Passwords (HASH)' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPassword} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::DomainPasswordHashesWeakPasswordEnabled)) {$Stats.'Weak Passwords (HASH) Enabled' = Get-ObjectCount -Object $Data.DomainPasswordDataPasswordsHashes.DomainPasswordHashesWeakPasswordEnabled} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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 = $Env:USERDNSDOMAIN, [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 = $Env:USERDNSDOMAIN, [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" } function Get-WinADDomainOrganizationalUnitsACL { [cmdletbinding()] param($DomainOrganizationalUnitsClean, [string] $Domain = $Env:USERDNSDOMAIN, [string] $NetBiosName, [string] $RootDomainNamingContext) Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsBasicACL" $Time = Start-TimeLog $OUs = @(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 } }) $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 = $Env:USERDNSDOMAIN, [string] $NetBiosName, [string] $RootDomainNamingContext, $GUID) Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended" $Time = Start-TimeLog $OUs = @(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 } } }) $EndTime = Stop-TimeLog -Time $Time -Option OneLiner Write-Verbose -Message "Getting domain information - $Domain DomainOrganizationalUnitsExtended Time: $EndTime" } 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 ($null -eq $DomainInformation) { 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 {$DuplicateGroups = $Data.PasswordQuality.DuplicatePasswordGroups.ToArray() $Count = 0 $Value = foreach ($DuplicateGroup in $DuplicateGroups) { $Count++ $Name = "Duplicate $Count" foreach ($User in $DuplicateGroup) {$FoundUser = [pscustomobject] @{'Duplicate Group' = $Name} $FullUserInformation = foreach ($_ in $DomainInformation.DomainUsersAll) {if ($_.SamAccountName -eq $User) {$_}} $FullComputerInformation = foreach ($_ in $DomainInformation.DomainComputersAll) {if ($_.SamAccountName -eq $User) {$_}} if ($FullUserInformation) {$MergedObject = Merge-Objects -Object1 $FoundUser -Object2 $FullUserInformation} if ($FullComputerInformation) {$MergedObject = Merge-Objects -Object1 $MergedObject -Object2 $FullComputerInformation} $MergedObject } } 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 = $Env:USERDNSDOMAIN) $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 = $Env:USERDNSDOMAIN, [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', 'TrustType') $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 = $Env:USERDNSDOMAIN, [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 = $Env:USERDNSDOMAIN, [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' }) } Get-ADUser -Server $Domain -ResultPageSize 500000 -Filter * -Properties $Properties $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' try {Get-ADForest -ErrorAction Stop} catch {$null} $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 ([PSWinDocumentation.ActiveDirectory[]] $TypesRequired, [switch] $RequireTypes, [string] $PathToPasswords, [string] $PathToPasswordsHashes) Write-Verbose "Getting all information" $TimeToGenerateForest = Start-TimeLog if ($null -eq $TypesRequired) { Write-Verbose 'Get-WinADForestInformation - TypesRequired is null. Getting all.' $TypesRequired = Get-Types -Types ([PSWinDocumentation.ActiveDirectory]) } $Data = [ordered] @{} $Data.Forest = Get-WinADForest if ($null -eq $Data.Forest) {return} $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 if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestInformation)) {$Data.ForestInformation = Get-WinADForestInfo -Forest $Data.Forest} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestUPNSuffixes)) { Write-Verbose 'Getting forest information - Forest UPNSuffixes' $Data.ForestUPNSuffixes = Get-WinADForestUPNSuffixes -Forest $Data.Forest } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestGlobalCatalogs)) { Write-Verbose 'Getting forest information - Forest GlobalCatalogs' $Data.ForestGlobalCatalogs = $Data.Forest.GlobalCatalogs } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSPNSuffixes)) { Write-Verbose 'Getting forest information - Forest SPNSuffixes' $Data.ForestSPNSuffixes = $Data.Forest.SPNSuffixes } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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 @([PSWinDocumentation.ActiveDirectory]::ForestDomainControllers)) {$Data.ForestDomainControllers = Get-WinADForestControllers} if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites, [PSWinDocumentation.ActiveDirectory]::ForestSites1, [PSWinDocumentation.ActiveDirectory]::ForestSites2)) { Write-Verbose 'Getting forest information - Forest Sites' $Data.ForestSites = Get-WinADForestSites } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites1)) { Write-Verbose 'Getting forest information - Forest Sites1' $Data.ForestSites1 = Get-WinADForestSites1 -ForestSites $Data.ForestSites } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSites2)) { Write-Verbose 'Getting forest information - Forest Sites2' $Data.ForestSites2 = Get-WinADForestSites2 -ForestSites $Data.ForestSites } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnet , [PSWinDocumentation.ActiveDirectory]::ForestSubnets1, [PSWinDocumentation.ActiveDirectory]::ForestSubnets2)) { Write-Verbose 'Getting forest information - Forest Subnets' $Data.ForestSubnets = Get-WinADForestSubnets } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnets1)) { Write-Verbose 'Getting forest information - Forest Subnets1' $Data.ForestSubnets1 = Get-WinADForestSubnets1 -ForestSubnets $Data.ForestSubnets } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSubnets2)) { Write-Verbose 'Getting forest information - Forest Subnets2' $Data.ForestSubnets2 = Get-WinADForestSubnets2 -ForestSubnets $Data.ForestSubnets } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.ActiveDirectory]::ForestSiteLinks)) { Write-Verbose 'Getting forest information - Forest SiteLinks' $Data.ForestSiteLinks = Get-WinADForestSiteLinks } if (Find-TypesNeeded -TypesRequired $TypesRequired -TypesNeeded @([PSWinDocumentation.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" $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} $EndTimeAll = Stop-TimeLog -Time $TimeToGenerateForest Write-Verbose "Getting all information - Time to generate: $EndTimeAll" return $Data } 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) $EndTime = Stop-TimeLog -Time $Time -Option OneLiner Write-Verbose "Getting domain information - ForestSchemaPropertiesComputers Time: $EndTime" } 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 $Schema.FindClass("user").optionalproperties | Select-Object name, commonname, description, syntax) $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') 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', '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 '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 { [CmdletBinding()] 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-WinADKerberosUnconstrainedDelegation { param() Get-ADObject -filter {(UserAccountControl -BAND 0x0080000) -OR (UserAccountControl -BAND 0x1000000) -OR (msDS-AllowedToDelegateTo -like '*')} -Properties Name, ObjectClass, PrimaryGroupID, UserAccountControl, ServicePrincipalName, msDS-AllowedToDelegateTo } function Get-WinADRootDSE { [CmdletBinding()] param([string] $Domain = ($Env:USERDNSDOMAIN).ToLower()) $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 * } } 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 = $Env:USERDNSDOMAIN, [System.Object[]] $ADCatalog, [System.Object[]] $ADCatalogUsers, [ValidateSet("Recursive", "Standard")][String] $Option) [DateTime] $CurrentDate = Get-Date 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") "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 } } } 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") "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 "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 = $Env:USERDNSDOMAIN) $ReturnGroups = foreach ($Group in $Groups) { $User = foreach ($_ in $Users) {if ($_.DistinguishedName -eq $Group.ManagedBy) {$_}} [PsCustomObject][ordered] @{'Group Name' = $Group.Name '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 = $Env:USERDNSDOMAIN) [DateTime] $CurrentDate = Get-Date $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 ($null -ne $U.PasswordLastSet) {"$(-$($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 } 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)"} if ([int32]$V -band 0x00000008) {"Forest Transitive"} if ([int32]$V -band 0x00000010) {"Cross Organization (Selective Authentication enabled)"} 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 @() |