# Based on the script by lwhitelock, which is based on the original script by Kelvin Tegelaar https://github.com/KelvinTegelaar/AutomaticDocumentation ##################################################################### # # Active Directory Details to Hudu # # $HuduAPIKey must be supplied by RMM script # $HuduBaseDomain is the base domain of your Hudu instance (without a trailing /) # and must be supplied by RMM script # $CompanyName must exactly match the name of the company in Hudu # and must be supplied by RMM script # This is the name of the Hudu Asset Layout you want to use. $HuduAssetLayoutName = "Active Directory" ##################################################################### # Get the Hudu API Module if not installed if (Get-Module -ListAvailable -Name HuduAPI) { Import-Module HuduAPI } else { Install-Module HuduAPI -Force Import-Module HuduAPI } # Set Hudu logon information New-HuduAPIKey $HuduAPIKey New-HuduBaseUrl $HuduBaseDomain Function Get-RegistryValue { # Gets the specified registry value or $Null if it is missing [CmdletBinding()] Param ( [String] $path, [String] $name, [String] $ComputerName ) If($ComputerName -eq $env:computername -or $ComputerName -eq "LocalHost") { $key = Get-Item -LiteralPath $path -EA 0 If($key) { Return $key.GetValue($name, $Null) } Else { Return $Null } } # Path needed here is different for remote registry access $path1 = $path.SubString( 6 ) $path2 = $path1.Replace( '\', '\\' ) $registry = $null try { ## Use the Remote Registry service $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey( [Microsoft.Win32.RegistryHive]::LocalMachine, $ComputerName ) } catch { #$e = $error[ 0 ] #3.06, remove the verbose message as it confised some people #wv "Could not open registry on computer $ComputerName ($e)" } $val = $null If( $registry ) { $key = $registry.OpenSubKey( $path2 ) If( $key ) { $val = $key.GetValue( $name ) $key.Close() } $registry.Close() } Return $val } Function GetBasicDCInfo { Param ( [Parameter( Mandatory = $true )] [String] $dn # distinguishedName of a DC ) $DCName = $dn.SubString( 0, $dn.IndexOf( '.' ) ) $SrvName = $dn.SubString( $dn.IndexOf( '.' ) + 1 ) $Results = Get-ADDomainController -Identity $DCName -Server $SrvName -EA 0 If($? -and $Null -ne $Results) { $GC = $Results.IsGlobalCatalog.ToString() $ReadOnly = $Results.IsReadOnly.ToString() $IPv4Address = $Results.IPv4Address -join ", " $IPv6Address = $Results.IPv6Address -join ", " $ServerOS = $Results.OperatingSystem $tmp = Get-RegistryValue "HKLM:\software\microsoft\windows nt\currentversion" "installationtype" $DCName If( $null -eq $tmp ) { $ServerCore = 'Unknown' } ElseIf( $tmp -eq 'Server Core') { $ServerCore = 'Yes' } Else { $ServerCore = 'No' } } Else { $GC = 'Unable to retrieve status' $ReadOnly = $GC $ServerOS = $GC $ServerCore = $GC $IPv4Address = $GC $IPv6Address = $GC } $obj = [PSCustomObject] @{ DCName = $DCName GC = $GC ReadOnly = $ReadOnly ServerOS = $ServerOS ServerCore = $ServerCore IPv4Address = $IPv4Address IPv6Address = $IPv6Address } Return $obj } Function GetTimeServerRegistryKeys { Param ( [String] $DCName ) $AnnounceFlags = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "AnnounceFlags" $DCName If( $null -eq $AnnounceFlags ) { ## DCName can't be contacted or DCName is an appliance with no registry $AnnounceFlags = 'n/a' $MaxNegPhaseCorrection = 'n/a' $MaxPosPhaseCorrection = 'n/a' $NtpServer = 'n/a' $NtpType = 'n/a' $SpecialPollInterval = 'n/a' $VMICTimeProviderEnabled = 'n/a' $NTPSource = 'Cannot retrieve data from registry' } Else { $MaxNegPhaseCorrection = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxNegPhaseCorrection" $DCName $MaxPosPhaseCorrection = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxPosPhaseCorrection" $DCName $NtpServer = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "NtpServer" $DCName $NtpType = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "Type" $DCName $SpecialPollInterval = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" "SpecialPollInterval" $DCName $VMICTimeProviderEnabled = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" "Enabled" $DCName $NTPSource = Invoke-Command -ComputerName $DCName {w32tm /query /computer:$DCName /source} } If( $VMICTimeProviderEnabled -eq 'n/a' ) { $VMICEnabled = 'n/a' } ElseIf( $VMICTimeProviderEnabled -eq 0 ) { $VMICEnabled = 'Disabled' } Else { $VMICEnabled = 'Enabled' } $obj = [PSCustomObject] @{ DCName = $DCName.Substring(0, $_.IndexOf( '.')) TimeSource = $NTPSource AnnounceFlags = $AnnounceFlags MaxNegPhaseCorrection = $MaxNegPhaseCorrection MaxPosPhaseCorrection = $MaxPosPhaseCorrection NtpServer = $NtpServer NtpType = $NtpType SpecialPollInterval = $SpecialPollInterval VMICTimeProvider = $VMICEnabled } Return $obj } function Get-WinADForestInformation { $Data = @{ } $ForestInformation = $(Get-ADForest) $Data.Forest = $ForestInformation $Data.RootDSE = $(Get-ADRootDSE -Properties *) $Data.ForestName = $ForestInformation.Name $Data.ForestNameDN = $Data.RootDSE.defaultNamingContext $Data.Domains = $ForestInformation.Domains $Data.ForestInformation = @{ 'Forest Name' = $ForestInformation.Name 'Root Domain' = $ForestInformation.RootDomain 'Forest Functional Level' = $ForestInformation.ForestMode '# of Domains' = ($ForestInformation.Domains).Count 'Sites Count' = ($ForestInformation.Sites).Count 'Forest Domains' = ($ForestInformation.Domains) -join ", " 'Sites' = ($ForestInformation.Sites) -join ", " } $Data.UPNSuffixes = Invoke-Command -ScriptBlock { $UPNSuffixList = [PSCustomObject] @{ "Primary UPN" = $ForestInformation.RootDomain "UPN Suffixes" = $ForestInformation.UPNSuffixes -join "," } return $UPNSuffixList } $Data.GlobalCatalogs = $ForestInformation.GlobalCatalogs $Data.SPNSuffixes = $ForestInformation.SPNSuffixes $Data.Sites = Invoke-Command -ScriptBlock { $Sites = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | Sort-Object $SiteData = foreach ($Site in $Sites) { [PSCustomObject] @{ "Site Name" = $site.Name "Subnets" = ($site.Subnets | Sort-Object) -join ", " "Servers" = ($Site.Servers) -join ", " } } Return $SiteData } $Data.FSMO = Invoke-Command -ScriptBlock { [PSCustomObject] @{ "Domain" = $ForestInformation.RootDomain "Role" = 'Domain Naming Master' "Holder" = $ForestInformation.DomainNamingMaster } [PSCustomObject] @{ "Domain" = $ForestInformation.RootDomain "Role" = 'Schema Master' "Holder" = $ForestInformation.SchemaMaster } foreach ($Domain in $ForestInformation.Domains) { $DomainFSMO = Get-ADDomain $Domain | Select-Object PDCEmulator, RIDMaster, InfrastructureMaster [PSCustomObject] @{ "Domain" = $Domain "Role" = 'PDC Emulator' "Holder" = $DomainFSMO.PDCEmulator } [PSCustomObject] @{ "Domain" = $Domain "Role" = 'Infrastructure Master' "Holder" = $DomainFSMO.InfrastructureMaster } [PSCustomObject] @{ "Domain" = $Domain "Role" = 'RID Master' "Holder" = $DomainFSMO.RIDMaster } } Return $FSMO } $Data.OptionalFeatures = Invoke-Command -ScriptBlock { $OptionalFeatures = $(Get-ADOptionalFeature -Filter * ) $Optional = @{ 'Recycle Bin Enabled' = '' 'Privileged Access Management Feature Enabled' = '' } ### Fix Optional Features foreach ($Feature in $OptionalFeatures) { if ($Feature.Name -eq 'Recycle Bin Feature') { if ("$($Feature.EnabledScopes)" -eq '') { $Optional.'Recycle Bin Enabled' = $False } else { $Optional.'Recycle Bin Enabled' = $True } } if ($Feature.Name -eq 'Privileged Access Management Feature') { if ("$($Feature.EnabledScopes)" -eq '') { $Optional.'Privileged Access Management Feature Enabled' = $False } else { $Optional.'Privileged Access Management Feature Enabled' = $True } } } return $Optional ### Fix optional features } return $Data } $TableHeader = "
| ", " | "
$RawAD = Get-WinADForestInformation
$ForestRawInfo = new-object PSCustomObject -property $RawAD.ForestInformation | convertto-html -Fragment | Select-Object -Skip 1
$ForestToc = ""
$ForestNice = $ForestToc + $TableHeader + ($ForestRawInfo -replace $TableStyling) + $Whitespace
$SiteRawInfo = $RawAD.Sites | Select-Object 'Site Name', Servers, Subnets | ConvertTo-Html -Fragment | Select-Object -Skip 1
$SiteHeader = " AD Forest Physical Structure. " $SiteNice = $SiteHeader + $TableHeader + ($SiteRawInfo -replace $TableStyling) + $Whitespace $DomainsRawInfo = $(Get-WinADForestInformation).Domains | ForEach-Object { Get-ADDomain $_ | Select-Object Name, NetBIOSName, DomainMode } | ConvertTo-Html -Fragment | Select-Object -Skip 1 $DomainsHeader = "AD Forest Logical Structure. " $DomainsNice = $DomainsHeader + $TableHeader + ($DomainsRawInfo -replace $TableStyling) + $Whitespace $OptionalRawFeatures = new-object PSCustomObject -property $RawAD.OptionalFeatures | convertto-html -Fragment | Select-Object -Skip 1 $OptionalFeaturesToc = "" $OptionalNice = $OptionalFeaturesToc + $TableHeader + ($OptionalRawFeatures -replace $TableStyling) + $Whitespace $UPNRawFeatures = $RawAD.UPNSuffixes | convertto-html -Fragment -as list| Select-Object -Skip 1 $UPNToc = "" $UPNNice = $UPNToc + $TableHeader + ($UPNRawFeatures -replace $TableStyling) + $Whitespace $DCRawFeatures = $RawAD.GlobalCatalogs| Sort-Object | ForEach-Object { GetBasicDCInfo $_ } | convertto-html -Fragment | Select-Object -Skip 1 $DCToc = "" $DCNice = $DCToc + $TableHeader + ($DCRawFeatures -replace $TableStyling) + $Whitespace $DCRawNTPconfig = $RawAD.GlobalCatalogs | Sort-Object | ForEach-Object { (GetTimeServerRegistryKeys $_) } | convertto-html -Fragment | Select-Object -Skip 1 $NTPToc = "" $DCNTPconfigNice = $NTPToc + $TableHeader + ($DCRawNTPconfig -replace $TableStyling) + $Whitespace $FSMORawFeatures = $RawAD.FSMO | convertto-html -Fragment | Select-Object -Skip 1 $FSMOToc = "" $FSMONice = $FSMOToc + $TableHeader + ($FSMORawFeatures -replace $TableStyling) + $Whitespace #$ForestFunctionalLevel = $RawAD.RootDSE.forestFunctionality #$DomainFunctionalLevel = $RawAD.RootDSE.domainFunctionality #$domaincontrollerMaxLevel = $RawAD.RootDSE.domainControllerFunctionality $passwordpolicyraw = Get-ADDefaultDomainPasswordPolicy | Select-Object ComplexityEnabled, PasswordHistoryCount, LockoutDuration, LockoutThreshold, MaxPasswordAge, MinPasswordAge | convertto-html -Fragment -As List | Select-Object -skip 1 $passwordpolicyheader = " |
|---|---|
| Policy | Setting |