diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index aec620e..f4dedda 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -6,7 +6,7 @@ on: jobs: publish-to-gallery: - runs-on: ubuntu-latest + runs-on: windows-2019 steps: - uses: actions/checkout@v2 - name: Set PSRepository to Trusted for PowerShell Gallery @@ -17,6 +17,14 @@ jobs: shell: pwsh run: | Install-Module -Name AsBuiltReport.Core -Repository PSGallery -Force + - name: Install PSPKI module + shell: pwsh + run: | + Install-Module -Name PSPKI -Repository PSGallery -Force + - name: Test Module Manifest + shell: pwsh + run: | + Test-ModuleManifest .\AsBuiltReport.Microsoft.AD.psd1 - name: Publish module to PowerShell Gallery shell: pwsh run: | diff --git a/AsBuiltReport.Microsoft.AD.Style.ps1 b/AsBuiltReport.Microsoft.AD.Style.ps1 index 40b8ab1..123e44f 100644 --- a/AsBuiltReport.Microsoft.AD.Style.ps1 +++ b/AsBuiltReport.Microsoft.AD.Style.ps1 @@ -8,10 +8,12 @@ Style -Name 'Title' -Size 24 -Color '737373' -Align Center Style -Name 'Title 2' -Size 18 -Color '00A4EF' -Align Center Style -Name 'Title 3' -Size 12 -Color '00A4EF' -Align Left Style -Name 'Heading 1' -Size 16 -Color '00A4EF' -Style -Name 'Heading 2' -Size 14 -Color '00A4EF' -Style -Name 'Heading 3' -Size 12 -Color '00A4EF' -Style -Name 'Heading 4' -Size 11 -Color '00A4EF' -Style -Name 'Heading 5' -Size 10 -Color '00A4EF' +Style -Name 'Heading 2' -Size 15 -Color '00A4EF' +Style -Name 'Heading 3' -Size 14 -Color '00A4EF' +Style -Name 'Heading 4' -Size 13 -Color '00A4EF' +Style -Name 'Heading 5' -Size 12 -Color '00A4EF' +Style -Name 'Heading 6' -Size 11 -Color '00A4EF' +Style -Name 'Heading 7' -Size 10 -Color '00A4EF' Style -Name 'Normal' -Size 10 -Color '565656' -Default Style -Name 'Caption' -Size 10 -Color '565656' -Italic -Align Center Style -Name 'Header' -Size 10 -Color '565656' -Align Center diff --git a/AsBuiltReport.Microsoft.AD.json b/AsBuiltReport.Microsoft.AD.json index 5f6cbe6..089e3f0 100644 --- a/AsBuiltReport.Microsoft.AD.json +++ b/AsBuiltReport.Microsoft.AD.json @@ -33,6 +33,11 @@ }, "DNS": { "Aging": true + }, + "DHCP": { + "Summary": true, + "Credential": true, + "Statistics": true } } } diff --git a/AsBuiltReport.Microsoft.AD.psd1 b/AsBuiltReport.Microsoft.AD.psd1 index be60c21..2259485 100644 --- a/AsBuiltReport.Microsoft.AD.psd1 +++ b/AsBuiltReport.Microsoft.AD.psd1 @@ -12,7 +12,7 @@ RootModule = 'AsBuiltReport.Microsoft.AD.psm1' # Version number of this module. -ModuleVersion = '0.2.0' +ModuleVersion = '0.3.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -56,10 +56,6 @@ RequiredModules = @( ModuleName = 'AsBuiltReport.Core'; ModuleVersion = '1.1.0' }, - @{ - ModuleName = 'ActiveDirectory'; - ModuleVersion = '1.0' - }, @{ ModuleName = 'PSPKI'; ModuleVersion = '3.7.2' @@ -108,7 +104,7 @@ PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'AsBuiltReport', 'Report', 'Microsoft', 'AD', 'Active-Directory', 'Documentation', 'PScribo', 'PSEdition_Desktop', 'PSEdition_Core', 'Windows', 'MacOS', 'Linux' + Tags = 'AsBuiltReport', 'Report', 'Microsoft', 'AD', 'Active-Directory', 'Documentation', 'PScribo', 'PSEdition_Desktop', 'PSEdition_Core', 'Windows' # A URL to the license for this module. LicenseUri = 'https://raw.githubusercontent.com/AsBuiltReport/AsBuiltReport.Microsoft.AD/master/LICENSE' diff --git a/CHANGELOG.md b/CHANGELOG.md index e1c9037..d5ed629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,88 @@ # :arrows_counterclockwise: Microsoft AD As Built Report Changelog +## [0.3.0] - 2021-09-26 + +### Added + +- Added Active Directory DHCP summary information. + - Added DHCP Database information. + - Added DHCP Dynamic DNS information. +- Added per Domain DHCP IPv4 Scope information. + - Added DHCP Scope Failover configuration information. + - Added DHCP Scope Statistics information. + - Added DHCP Scope Interface Binding information. + - Added DHCP Scope Delegation configuration information. +- Added per Domain DHCP IPv6 Scope information. + - Added DHCP Scope Failover configuration information. + - Added DHCP Scope Statistics information. + - Added DHCP Scope Interface Binding information. + - Added DHCP health check. + +### Changed + +- Added more Heading definitions. +- Added funtion to convert from subnetmask to dotted notation. +- Added a function to convert empty culumns to "-" (less switch cases). + +### Fixed + +- Fix for PSSession exhaustion. +- Fix for DNS Zone Delegation IPaddress variable +- Fix for unhandle null values. +- Enhanced error message catching. +- Fix for heading hierarchy. +- Fix Forest schema version code. +- Fix ActiveDirectory RequiredModule error (Fix [#3](https://github.com/AsBuiltReport/AsBuiltReport.Microsoft.AD/issues/3)). + ## [0.2.0] - 2021-09-10 ### Added -- Added Active Directory DNS summary information - - Added DNS Forwarder summary information - - Added Added DNS Recursion configuration information - - Added Added DNS RRL configuration information - - Added Added DNS Zone Reverse Lookup configuration information - - Added Added DNS Zone Scavenging/Aging configuration information - - Added Added DNS Zone Delegation configuration information - - Added more health checks +- Added Active Directory DNS summary information. + - Added DNS Forwarder summary information. + - Added DNS Recursion configuration information. + - Added DNS RRL configuration information. + - Added DNS Zone Reverse Lookup configuration information. + - Added DNS Zone Scavenging/Aging configuration information. + - Added DNS Zone Delegation configuration information. + - Added more health checks. ### Changed -- Improved per Domain configuration information -- Improved per Domain Controller configuration information -- Introduced the ability to use a shared PSsession -- Merged the functions used within the reports into a single file (SharedUtilsFunctions) +- Improved per Domain configuration information. +- Improved per Domain Controller configuration information. +- Introduced the ability to use a shared PSsession. +- Merged the functions used within the reports into a single file (SharedUtilsFunctions). ### Fixed - Enhanced the logic of detecting a unavailable Domain or DC. -- Enhanced verbose/degug logging -- Added more try/catch code to improve error diagnostic +- Enhanced verbose/degug logging. +- Added more try/catch code to improve error diagnostic. ## [0.1.0] - 2021-08-10 ### Added -- Added Active Directory Forest summary information - - Added Forest Optional Features Summary +- Added Active Directory Forest summary information. + - Added Forest Optional Features Summary. + - Added Domain Site summary information. + - Added Domain Site Link summary information. +- Added Active Directory Domain summary Infomation. + - Added Object Count summary Information. + - Added Default Domain Password Policy Summary Information. + - Added Group Managed Service Accounts (GMSA) Summary Information. + - Added Flexible Single Master Operations (FSMO) Information. + - Added Trust Summary information. +- Added Domain Controller Information. + - Added Domain Controller Hardware Summary. + - Added Domain Controller NTDS Summary. + - Added Domain Controller Time Source Summary. + - Added Domain Controller Infrastructure Services Status. + - Added Site Replication Summary. + - Added Site Replication Failure Summary. + - Added Group Policy Objects Summary. + - Added Organizational Unit summary. - Added Domain Site summary information - Added Domain Site Link summary information - Added Active Directory Domain summary Infomation @@ -48,4 +99,4 @@ - Added Site Replication Summary - Added Site Replication Failure Summary - Added Group Policy Objects Summary - - Added Organizational Unit summary + - Added Organizational Unit summary \ No newline at end of file diff --git a/README.md b/README.md index 549273c..0b5b220 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,9 @@ This report does not support Linux or Mac due to the fact that the ActiveDirecto ### :closed_lock_with_key: Required Privileges -A Microsoft AD As Built Report can be generated with Active Directory Enterprise Forest level privileges. Due to the limitations of the WinRM protocol, a domain-joined machine is needed, also it is required to use the FQDN of the DC instead of its IP address. +A Microsoft AD As Built Report can be generated with Active Directory Enterprise Forest level privileges. Since this report relies extensively on the WinRM component, you should make sure that it is enabled and configured. [Reference](https://docs.microsoft.com/en-us/windows/win32/winrm/installation-and-configuration-for-windows-remote-management) + +Due to a limitation of the WinRM component, a domain-joined machine is needed, also it is required to use the FQDN of the DC instead of its IP address. [Reference](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_troubleshooting?view=powershell-7.1#how-to-use-an-ip-address-in-a-remote-command) ## :package: Module Installation diff --git a/Src/Private/Get-AbrADDCDiag.ps1 b/Src/Private/Get-AbrADDCDiag.ps1 index 1d4288c..93534ca 100644 --- a/Src/Private/Get-AbrADDCDiag.ps1 +++ b/Src/Private/Get-AbrADDCDiag.ps1 @@ -5,7 +5,7 @@ function Get-AbrADDCDiag { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux diff --git a/Src/Private/Get-AbrADDHCPInfrastructure.ps1 b/Src/Private/Get-AbrADDHCPInfrastructure.ps1 new file mode 100644 index 0000000..20f3de2 --- /dev/null +++ b/Src/Private/Get-AbrADDHCPInfrastructure.ps1 @@ -0,0 +1,158 @@ +function Get-AbrADDHCPInfrastructure { + <# + .SYNOPSIS + Used by As Built Report to retrieve Microsoft AD DHCP Servers from Domain Controller + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + .EXAMPLE + + .LINK + + #> + [CmdletBinding()] + param ( + [Parameter ( + Position = 0, + Mandatory)] + [string] + $Domain, + $Session, + [PSCredential] + $Cred + ) + + begin { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain.ToString().ToUpper())." + } + + process { + Section -Style Heading5 'DHCP Servers In Active Directory Summary' { + Paragraph "The following section provides a summary of the DHCP servers information on $($Domain.ToString().ToUpper())." + BlankLine + $OutObj = @() + if ($Domain) { + try { + $DHCPinDC = Invoke-Command -Session $Session { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} } + if ($DHCPinDC) {Write-PScriboMessage "Discovered '$(($DHCPinDC | Measure-Object).Count)' DHCP Servers in forest $($Domain)."} + foreach ($DHCPServers in $DHCPinDC) { + Write-PScriboMessage "Collecting DHCP Server Setting information from $($DHCPServers.DnsName.split(".", 2)[0])" + $Setting = Invoke-Command -Session $Session { Get-DhcpServerSetting -ComputerName ($using:DHCPServers).DnsName } + $inObj = [ordered] @{ + 'DC Name' = $DHCPServers.DnsName.Split(".", 2)[0] + 'IP Address' = $DHCPServers.IPAddress + 'Domain Name' = $DHCPServers.DnsName.Split(".", 2)[1] + 'Domain Joined' = ConvertTo-TextYN $Setting.IsDomainJoined + 'Authorized' = ConvertTo-TextYN $Setting.IsAuthorized + 'Conflict Detection Attempts' = ConvertTo-TextYN $Setting.ConflictDetectionAttempts + } + $OutObj += [pscustomobject]$inobj + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving Dhcp Server Setting from $($DHCPServers.DnsName.Split(".", 2)[0])." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + + $TableParams = @{ + Name = "DHCP Servers In Active Directory Information - $($Domain.ToString().ToUpper())" + List = $false + ColumnWidths = 20, 15, 20, 15, 15 ,15 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + Section -Style Heading6 'Service Database Summary' { + Paragraph "The following section provides a summary of the DHCP servers service database information on $($Domain.ToString().ToUpper())." + BlankLine + $OutObj = @() + if ($Domain) { + try { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain)." + $DHCPinDC = Invoke-Command -Session $Session { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} } + if ($DHCPinDC) {Write-PScriboMessage "Discovered '$(($DHCPinDC | Measure-Object).Count)' DHCP Servers in forest $($Domain)."} + foreach ($DHCPServers in $DHCPinDC) { + Write-PScriboMessage "Collecting DHCP Server database information from $($DHCPServers.DnsName.split(".", 2)[0])" + $Setting = Invoke-Command -Session $Session { Get-DhcpServerDatabase -ComputerName ($using:DHCPServers).DnsName } + $inObj = [ordered] @{ + 'DC Name' = $DHCPServers.DnsName.Split(".", 2)[0] + 'File Path' = ConvertTo-EmptyToFiller $Setting.FileName + 'Backup Path' = ConvertTo-EmptyToFiller $Setting.BackupPath + 'Backup Interval' = switch ($Setting.BackupInterval) { + "" {"-"; break} + $NULL {"-"; break} + default {"$($Setting.BackupInterval) min"} + } + 'Logging Enabled' = Switch ($Setting.LoggingEnabled) { + "" {"-"; break} + $Null {"-"; break} + default {ConvertTo-TextYN $Setting.LoggingEnabled} + } + } + $OutObj += [pscustomobject]$inobj + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving Dhcp Servers Database on $($DHCPServers.DnsName)." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + + $TableParams = @{ + Name = "DHCP Servers Database Information - $($Domain.ToString().ToUpper())" + List = $false + ColumnWidths = 20, 28, 28, 12, 12 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + Section -Style Heading6 'Dynamic DNS credentials Summary' { + Paragraph "The following section provides a summary of the DHCP Servers Dynamic DNS registration credentials information on $($Domain.ToString().ToUpper())." + BlankLine + $OutObj = @() + if ($Domain) { + try { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain)." + $DHCPinDC = Invoke-Command -Session $Session { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} } + if ($DHCPinDC) {Write-PScriboMessage "Discovered '$(($DHCPinDC | Measure-Object).Count)' DHCP Servers in forest $($Domain)."} + foreach ($DHCPServers in $DHCPinDC) { + Write-PScriboMessage "Collecting DHCP Server Dynamic DNS Credentials information from $($DHCPServers.DnsName.split(".", 2)[0])" + $Setting = Invoke-Command -Session $Session { Get-DhcpServerDnsCredential -ComputerName ($using:DHCPServers).DnsName } + $inObj = [ordered] @{ + 'DC Name' = $DHCPServers.DnsName.Split(".", 2)[0] + 'User Name' = ConvertTo-EmptyToFiller $Setting.UserName + 'Domain Name' = ConvertTo-EmptyToFiller $Setting.DomainName + } + $OutObj += [pscustomobject]$inobj + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving Dhcp Servers Dynamic DNS credentials on $($DHCPServers.DnsName)." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + + $TableParams = @{ + Name = "DHCP Servers Dynamic DNS Credentials Information - $($Domain.ToString().ToUpper())" + List = $false + ColumnWidths = 30, 30, 40 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + } + + end {} + +} \ No newline at end of file diff --git a/Src/Private/Get-AbrADDHCPv4Scope.ps1 b/Src/Private/Get-AbrADDHCPv4Scope.ps1 new file mode 100644 index 0000000..5d05a61 --- /dev/null +++ b/Src/Private/Get-AbrADDHCPv4Scope.ps1 @@ -0,0 +1,203 @@ +function Get-AbrADDHCPv4Scope { + <# + .SYNOPSIS + Used by As Built Report to retrieve Microsoft AD DHCP Servers Scopes from Domain Controller + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + .EXAMPLE + + .LINK + + #> + [CmdletBinding()] + param ( + [Parameter ( + Position = 0, + Mandatory)] + [string] + $Domain, + $Session, + [string] + $Server + ) + + begin { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain.ToString().ToUpper())." + } + + process { + Section -Style Heading6 "IPv4 Scope Summary on $($Server.ToUpper().split(".", 2)[0])" { + Paragraph "The following section provides a summary of the DHCP servers IPv4 Scope information." + BlankLine + $OutObj = @() + if ($Server -and $Domain) { + try { + $DHCPScopes = Invoke-Command -Session $Session { Get-DhcpServerv4Scope -ComputerName $using:Server} + Write-PScriboMessage "Discovered '$(($DHCPScopes | Measure-Object).Count)' DHCP SCopes in $($Server)." + foreach ($Scope in $DHCPScopes) { + Write-PscriboMessage "Collecting DHCP Server IPv4 $($Scope.ScopeId) Scope from $($Server.split(".", 2)[0])" + $SubnetMask = Convert-IpAddressToMaskLength $Scope.SubnetMask + $inObj = [ordered] @{ + 'Scope Id' = "$($Scope.ScopeId)/$($SubnetMask)" + 'Scope Name' = $Scope.Name + 'Scope Range' = "$($Scope.StartRange) - $($Scope.EndRange)" + 'Lease Duration' = Switch ($Scope.LeaseDuration) { + "10675199.02:48:05.4775807" {"Unlimited"} + default {$Scope.LeaseDuration} + } + 'State' = $Scope.State + } + $OutObj += [pscustomobject]$inobj + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Scopes from $($Server.split(".", 2)[0])." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + + $TableParams = @{ + Name = "IPv4 Scopes Information - $($Server.split(".", 2).ToUpper()[0])" + List = $false + ColumnWidths = 20, 20, 35, 15, 10 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + try { + Section -Style Heading7 "IPv4 Scope Statistics Summary on $($Server.ToUpper().split(".", 2)[0])" { + Paragraph "The following section provides a summary of the DHCP servers IPv4 Scope Statistics information." + BlankLine + $OutObj = @() + if ($Server -and $Domain) { + $DHCPScopes = Invoke-Command -Session $Session { Get-DhcpServerv4ScopeStatistics -ComputerName $using:Server} + Write-PScriboMessage "Discovered '$(($DHCPScopes | Measure-Object).Count)' scopes in $($Server)." + foreach ($Scope in $DHCPScopes) { + Write-PscriboMessage "Collecting DHCP Server IPv4 $($Scope.ScopeId) scope statistics from $($Server.split(".", 2)[0])" + $inObj = [ordered] @{ + 'Scope Id' = $Scope.ScopeId + 'Free IP' = $Scope.Free + 'In Use IP' = $Scope.InUse + 'Percentage In Use' = [math]::Round($Scope.PercentageInUse, 0) + 'Reserved IP' = $Scope.Reserved + } + $OutObj += [pscustomobject]$inobj + } + } + + if ($HealthCheck.DHCP.Statistics) { + $OutObj | Where-Object { $_.'Percentage In Use' -gt '95'} | Set-Style -Style Warning -Property 'Percentage In Use' + } + + $TableParams = @{ + Name = "IPv4 Scope Statistics Information - $($Server.split(".", 2).ToUpper()[0])" + List = $false + ColumnWidths = 20, 20, 20, 20, 20 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Scope Statistics from $($Server.split(".", 2).ToUpper()[0])." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + try { + Section -Style Heading7 "IPv4 Scope Failover Summary on $($Server.ToUpper().split(".", 2)[0])" { + Paragraph "The following section provides a summary of the DHCP servers IPv4 Scope Failover information." + BlankLine + $OutObj = @() + if ($Server -and $Domain) { + $DHCPScopes = Invoke-Command -Session $Session { Get-DhcpServerv4Failover -ComputerName $using:Server} + Write-PScriboMessage "Discovered '$(($DHCPScopes | Measure-Object).Count)' failover setting in $($Server)." + foreach ($Scope in $DHCPScopes) { + Write-PscriboMessage "Collecting DHCP Server IPv4 $($Scope.ScopeId) scope failover setting from $($Server.split(".", 2)[0])" + $inObj = [ordered] @{ + 'DHCP Server' = $Server + 'Partner DHCP Server' = $Scope.PartnerServer + 'Mode' = $Scope.Mode + 'LoadBalance Percent' = ConvertTo-EmptyToFiller ([math]::Round($Scope.LoadBalancePercent, 0)) + 'Server Role' = ConvertTo-EmptyToFiller $Scope.ServerRole + 'Reserve Percent' = ConvertTo-EmptyToFiller ([math]::Round($Scope.ReservePercent, 0)) + 'Max Client Lead Time' = ConvertTo-EmptyToFiller $Scope.MaxClientLeadTime + 'State Switch Interval' = ConvertTo-EmptyToFiller $Scope.StateSwitchInterval + 'Scope Ids' = $Scope.ScopeId + 'State' = $Scope.State + 'Auto State Transition' = ConvertTo-TextYN $Scope.AutoStateTransition + 'Authetication Enable' = ConvertTo-TextYN $Scope.EnableAuth + } + $OutObj += [pscustomobject]$inobj + } + } + + $TableParams = @{ + Name = "IPv4 Scope Failover Cofiguration Information - $($Server.split(".", 2).ToUpper()[0])" + List = $true + ColumnWidths = 40, 60 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Scope Failover Setting from $($Server.split(".", 2).ToUpper()[0])." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + try { + Section -Style Heading7 "IPv4 Network Interface binding Summary on $($Server.ToUpper().split(".", 2)[0])" { + Paragraph "The following section provides a summary of the IPv4 Network Interface binding." + BlankLine + $OutObj = @() + if ($Server -and $Domain) { + $DHCPScopes = Invoke-Command -Session $Session { Get-DhcpServerv4Binding -ComputerName $using:Server} + Write-PScriboMessage "Discovered '$(($DHCPScopes | Measure-Object).Count)' bindings in $($Server)." + foreach ($Scope in $DHCPScopes) { + Write-PscriboMessage "Collecting DHCP Server IPv4 $($Scope.InterfaceAlias) binding from $($Server.split(".", 2)[0])" + $SubnetMask = Convert-IpAddressToMaskLength $Scope.SubnetMask + $inObj = [ordered] @{ + 'Interface Alias' = $Scope.InterfaceAlias + 'IP Address' = $Scope.IPAddress + 'Subnet Mask' = $Scope.SubnetMask + 'State' = Switch ($Scope.BindingState) { + "" {"-"; break} + $Null {"-"} + "True" {"Enabled"} + "False" {"Disabled"} + default {$Scope.BindingState} + } + } + $OutObj += [pscustomobject]$inobj + } + } + + $TableParams = @{ + Name = "IPv4 Network Interface binding Information - $($Server.split(".", 2).ToUpper()[0])" + List = $false + ColumnWidths = 25, 25, 25, 25 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 interface binding from $($Server.split(".", 2).ToUpper()[0])." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + } + + end {} + +} \ No newline at end of file diff --git a/Src/Private/Get-AbrADDHCPv4Statistic.ps1 b/Src/Private/Get-AbrADDHCPv4Statistic.ps1 new file mode 100644 index 0000000..1ff4871 --- /dev/null +++ b/Src/Private/Get-AbrADDHCPv4Statistic.ps1 @@ -0,0 +1,73 @@ +function Get-AbrADDHCPv4Statistic { + <# + .SYNOPSIS + Used by As Built Report to retrieve Microsoft AD DHCP Servers from Domain Controller + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + .EXAMPLE + + .LINK + + #> + [CmdletBinding()] + param ( + [Parameter ( + Position = 0, + Mandatory)] + [string] + $Domain, + $Session + ) + + begin { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain.ToString().ToUpper())." + } + + process { + Section -Style Heading6 'IPv4 Service Statistics Summary' { + Paragraph "The following section provides a summary of the DHCP servers IPv4 Statistics information on $($Domain.ToString().ToUpper())." + BlankLine + $OutObj = @() + if ($Domain) { + $DHCPinDC = Invoke-Command -Session $Session { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} } + if ($DHCPinDC) {Write-PScriboMessage "Discovered '$(($DHCPinDC | Measure-Object).Count)' DHCP Servers in forest $($Domain)."} + foreach ($DHCPServers in $DHCPinDC) { + Write-PScriboMessage "Collecting DHCP Server IPv4 Statistics from $($DHCPServers.DnsName.split(".", 2)[0])" + $Setting = Invoke-Command -Session $Session { Get-DhcpServerv4Statistics -ComputerName ($using:DHCPServers).DnsName } + $inObj = [ordered] @{ + 'DC Name' = $DHCPServers.DnsName.Split(".", 2)[0] + 'Total Scopes' = ConvertTo-EmptyToFiller $Setting.TotalScopes + 'Total Addresses' = ConvertTo-EmptyToFiller $Setting.TotalAddresses + 'Addresses In Use' = ConvertTo-EmptyToFiller $Setting.AddressesInUse + 'Addresses Available' = ConvertTo-EmptyToFiller $Setting.AddressesAvailable + 'Percentage In Use' = ConvertTo-EmptyToFiller ([math]::Round($Setting.PercentageInUse, 0)) + 'Percentage Available' = ConvertTo-EmptyToFiller ([math]::Round($Setting.PercentageAvailable, 0)) + } + $OutObj += [pscustomobject]$inobj + } + } + + if ($HealthCheck.DHCP.Statistics) { + $OutObj | Where-Object { $_.'Percentage In Use' -gt 95} | Set-Style -Style Warning -Property 'Percentage Available','Percentage In Use' + } + + $TableParams = @{ + Name = "DHCP Server IPv4 Statistics Information - $($Domain.ToString().ToUpper())" + List = $false + ColumnWidths = 20, 13, 13, 13, 14 ,13, 14 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + + end {} + +} \ No newline at end of file diff --git a/Src/Private/Get-AbrADDHCPv6Statistic.ps1 b/Src/Private/Get-AbrADDHCPv6Statistic.ps1 new file mode 100644 index 0000000..f2a28ef --- /dev/null +++ b/Src/Private/Get-AbrADDHCPv6Statistic.ps1 @@ -0,0 +1,78 @@ +function Get-AbrADDHCPv6Statistic { + <# + .SYNOPSIS + Used by As Built Report to retrieve Microsoft AD DHCP Servers from Domain Controller + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + .EXAMPLE + + .LINK + + #> + [CmdletBinding()] + param ( + [Parameter ( + Position = 0, + Mandatory)] + [string] + $Domain, + $Session + ) + + begin { + Write-PscriboMessage "Discovering Active Directory DHCP Servers information on $($Domain.ToString().ToUpper())." + } + + process { + Section -Style Heading6 'IPv6 Service Statistics Summary' { + Paragraph "The following section provides a summary of the DHCP servers IPv6 Statistics information on $($Domain.ToString().ToUpper())." + BlankLine + $OutObj = @() + if ($Domain) { + try { + $DHCPinDC = Invoke-Command -Session $Session { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} } + if ($DHCPinDC) {Write-PScriboMessage "Discovered '$(($DHCPinDC | Measure-Object).Count)' DHCP Servers in forest $($Domain)."} + foreach ($DHCPServers in $DHCPinDC) { + Write-PScriboMessage "Collecting DHCP Server IPv6 Statistics from $($DHCPServers.DnsName.split(".", 2)[0])" + $Setting = Invoke-Command -Session $Session { Get-DhcpServerv6Statistics -ComputerName ($using:DHCPServers).DnsName } + $inObj = [ordered] @{ + 'DC Name' = $DHCPServers.DnsName.Split(".", 2)[0] + 'Total Scopes' = ConvertTo-EmptyToFiller $Setting.TotalScopes + 'Total Addresses' = ConvertTo-EmptyToFiller $Setting.TotalAddresses + 'Addresses In Use' = ConvertTo-EmptyToFiller $Setting.AddressesInUse + 'Addresses Available' = ConvertTo-EmptyToFiller $Setting.AddressesAvailable + 'Percentage In Use' = ConvertTo-EmptyToFiller ([math]::Round($Setting.PercentageInUse, 0)) + 'Percentage Available' = ConvertTo-EmptyToFiller ([math]::Round($Setting.PercentageAvailable, 0)) + } + $OutObj += [pscustomobject]$inobj + } + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv6 Statistics from $(($DHCPServers).DnsName)." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + } + + if ($HealthCheck.DHCP.Statistics) { + $OutObj | Where-Object { $_.'Percentage In Use' -gt 95} | Set-Style -Style Warning -Property 'Percentage Available','Percentage In Use' + } + $TableParams = @{ + Name = "DHCP Server IPv6 Statistics Information - $($Domain.ToString().ToUpper())" + List = $false + ColumnWidths = 20, 13, 13, 13, 14 ,13, 14 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + } + + end {} + +} \ No newline at end of file diff --git a/Src/Private/Get-AbrADDNSInfrastructure.ps1 b/Src/Private/Get-AbrADDNSInfrastructure.ps1 index aaf62ce..4e62695 100644 --- a/Src/Private/Get-AbrADDNSInfrastructure.ps1 +++ b/Src/Private/Get-AbrADDNSInfrastructure.ps1 @@ -5,7 +5,7 @@ function Get-AbrADDNSInfrastructure { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -29,7 +29,7 @@ function Get-AbrADDNSInfrastructure { } process { - Section -Style Heading4 "Infrastructure Summary" { + Section -Style Heading5 "Infrastructure Summary" { Paragraph "The following section provides a summary of the Domain Name System Infrastructure configuration." BlankLine $OutObj = @() @@ -68,7 +68,7 @@ function Get-AbrADDNSInfrastructure { } $OutObj | Table @TableParams } - Section -Style Heading5 "Response Rate Limiting (RRL) Summary" { + Section -Style Heading6 "Response Rate Limiting (RRL) Summary" { Paragraph "The following section provides a summary of the Domain Name System Response Rate Limiting configuration." BlankLine $OutObj = @() @@ -110,7 +110,7 @@ function Get-AbrADDNSInfrastructure { $OutObj | Table @TableParams } } - Section -Style Heading5 "Scavenging Summary" { + Section -Style Heading6 "Scavenging Summary" { Paragraph "The following section provides a summary of the Domain Name System Scavenging configuration." BlankLine $OutObj = @() @@ -128,9 +128,9 @@ function Get-AbrADDNSInfrastructure { 'Refresh Interval' = $DNSSetting.RefreshInterval 'Scavenging Interval' = $DNSSetting.ScavengingInterval 'Last Scavenge Time' = Switch ($DNSSetting.LastScavengeTime) { - !$Null {$DNSSetting.LastScavengeTime.ToString("MM/dd/yyyy")} - default {$DNSSetting.LastScavengeTime} - + "" {"-"; break} + $Null {"-"; break} + default {$DNSSetting.LastScavengeTime.ToString("MM/dd/yyyy")} } 'Scavenging State' = Switch ($DNSSetting.ScavengingState) { "True" {"Enabled"} @@ -158,7 +158,7 @@ function Get-AbrADDNSInfrastructure { $OutObj | Table @TableParams } } - Section -Style Heading5 "Forwarder Summary" { + Section -Style Heading6 "Forwarder Summary" { Paragraph "The following section provides a summary of the Domain Name System Forwarder configuration." BlankLine $OutObj = @() @@ -198,7 +198,7 @@ function Get-AbrADDNSInfrastructure { $OutObj | Table @TableParams } } - Section -Style Heading5 "Zone Scope Recursion Summary" { + Section -Style Heading6 "Zone Scope Recursion Summary" { Paragraph "The following section provides a summary of the Domain Name System Zone Scope Recursion configuration." BlankLine $OutObj = @() diff --git a/Src/Private/Get-AbrADDNSZone.ps1 b/Src/Private/Get-AbrADDNSZone.ps1 index e1bddb7..f415731 100644 --- a/Src/Private/Get-AbrADDNSZone.ps1 +++ b/Src/Private/Get-AbrADDNSZone.ps1 @@ -5,7 +5,7 @@ function Get-AbrADDNSZone { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -32,7 +32,7 @@ function Get-AbrADDNSZone { } process { - Section -Style Heading4 "Domain Name System Zone Configuration of $($DC.ToString().ToUpper().Split(".")[0])" { + Section -Style Heading5 "Domain Name System Zone Configuration of $($DC.ToString().ToUpper().Split(".")[0])" { Paragraph "The following section provides a summary of the Domain Name System Zone Configuration information." BlankLine $OutObj = @() @@ -54,6 +54,7 @@ function Get-AbrADDNSZone { } $OutObj += [pscustomobject]$inobj } + Remove-PSSession -Session $DCPssSession } catch { Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation." @@ -75,8 +76,9 @@ function Get-AbrADDNSZone { Write-PscriboMessage "Discovered Actve Directory Domain Controller: $DC. (Domain Name System Zone)" $DNSSetting = Invoke-Command -Session $DCPssSession {Get-DnsServerZone | Where-Object {$_.IsReverseLookupZone -like "False" -and $_.ReplicationScope -eq "Domain"} | Select-Object -ExpandProperty ZoneName } $Zones = Invoke-Command -Session $DCPssSession {Get-DnsServerZoneDelegation -Name $using:DNSSetting} + Remove-PSSession -Session $DCPssSession if ($Zones) { - Section -Style Heading5 "Zone Delegation of $($DC.ToString().ToUpper().Split(".")[0])" { + Section -Style Heading6 "Zone Delegation of $($DC.ToString().ToUpper().Split(".")[0])" { Paragraph "The following section provides a summary of the Domain Name System Zone Delegation information." BlankLine $OutObj = @() @@ -89,7 +91,7 @@ function Get-AbrADDNSZone { 'Zone Name' = $Delegations.ZoneName 'Child Zone' = $Delegations.ChildZoneName 'Name Server' = $Delegations.NameServer.RecordData.NameServer - 'IP Address' = $Delegations.IPaddress.RecordData.IPv4Address.IPAddressToString + 'IP Address' = $Delegations.IPaddress.RecordData.IPv4Address.ToString() } $OutObj += [pscustomobject]$inobj } @@ -113,7 +115,7 @@ function Get-AbrADDNSZone { Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation." Write-PscriboMessage -IsDebug $_.Exception.Message } - Section -Style Heading5 "Reverse Lookup Zone Configuration of $($DC.ToString().ToUpper().Split(".")[0])" { + Section -Style Heading6 "Reverse Lookup Zone Configuration of $($DC.ToString().ToUpper().Split(".")[0])" { Paragraph "The following section provides a summary of the Domain Name System Reverse Lookup Zone Configuration information." BlankLine $OutObj = @() @@ -135,6 +137,7 @@ function Get-AbrADDNSZone { } $OutObj += [pscustomobject]$inobj } + Remove-PSSession -Session $DCPssSession } catch { Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation." @@ -152,7 +155,7 @@ function Get-AbrADDNSZone { $OutObj | Table @TableParams } } - Section -Style Heading5 "Zone Scope Aging properties of $($DC.ToString().ToUpper().Split(".")[0])" { + Section -Style Heading6 "Zone Scope Aging properties of $($DC.ToString().ToUpper().Split(".")[0])" { Paragraph "The following section provides a summary of the Domain Name System Zone Aging properties information." BlankLine $OutObj = @() @@ -168,10 +171,15 @@ function Get-AbrADDNSZone { 'Aging Enabled' = ConvertTo-TextYN $Settings.AgingEnabled 'Refresh Interval' = $Settings.RefreshInterval 'NoRefresh Interval' = $Settings.NoRefreshInterval - 'Available For Scavenge' = if ($Settings.AvailForScavengeTime) {($Settings.AvailForScavengeTime).ToUniversalTime().toString("r")} + 'Available For Scavenge' = Switch ($Settings.AvailForScavengeTime) { + "" {"-"} + $Null {"-"} + default {($Settings.AvailForScavengeTime).ToUniversalTime().toString("r")} + } } $OutObj += [pscustomobject]$inobj } + Remove-PSSession -Session $DCPssSession } catch { Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation." diff --git a/Src/Private/Get-AbrADDomain.ps1 b/Src/Private/Get-AbrADDomain.ps1 index fcf0286..687c275 100644 --- a/Src/Private/Get-AbrADDomain.ps1 +++ b/Src/Private/Get-AbrADDomain.ps1 @@ -5,7 +5,7 @@ function Get-AbrADDomain { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -42,17 +42,17 @@ function Get-AbrADDomain { 'NetBIOS Name' = $DomainInfo.NetBIOSName 'Domain SID' = $DomainInfo.DomainSID 'Domain Functional Level' = $DomainInfo.DomainMode - 'Domains' = $DomainInfo.Domains + 'Domains' = ConvertTo-EmptyToFiller $DomainInfo.Domains 'Forest' = $DomainInfo.Forest - 'Parent Domain' = $DomainInfo.ParentDomain + 'Parent Domain' = ConvertTo-EmptyToFiller $DomainInfo.ParentDomain 'Replica Directory Servers' = $DomainInfo.ReplicaDirectoryServers - 'Child Domains' = $DomainInfo.ChildDomains + 'Child Domains' = ConvertTo-EmptyToFiller $DomainInfo.ChildDomains 'Computers Container' = $DomainInfo.ComputersContainer 'Distinguished Name' = $DomainInfo.DistinguishedName 'Domain Controllers Container' = $DomainInfo.DomainControllersContainer 'Systems Container' = $DomainInfo.SystemsContainer 'Users Container' = $DomainInfo.UsersContainer - 'ReadOnly Replica Directory Servers' = $DomainInfo.ReadOnlyReplicaDirectoryServers + 'ReadOnly Replica Directory Servers' = ConvertTo-EmptyToFiller $DomainInfo.ReadOnlyReplicaDirectoryServers } $OutObj += [pscustomobject]$inobj } @@ -172,7 +172,7 @@ function Get-AbrADDomain { 'Created' = $Account.Created 'Enabled' = ConvertTo-TextYN $Account.Enabled 'DNS Host Name' = $Account.DNSHostName - 'Host Computers' = $Account.HostComputers + 'Host Computers' = ConvertTo-EmptyToFiller $Account.HostComputers 'Retrieve Managed Password' = $Account.PrincipalsAllowedToRetrieveManagedPassword 'Primary Group' = $Account.PrimaryGroup 'Last Logon Date' = $Account.LastLogonDate diff --git a/Src/Private/Get-AbrADDomainController.ps1 b/Src/Private/Get-AbrADDomainController.ps1 index 01cbdb2..6ec8556 100644 --- a/Src/Private/Get-AbrADDomainController.ps1 +++ b/Src/Private/Get-AbrADDomainController.ps1 @@ -5,7 +5,7 @@ function Get-AbrADDomainController { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -42,6 +42,7 @@ function Get-AbrADDomainController { Write-PscriboMessage "Collecting AD Domain Controller Summary information of $DC." $DCPssSession = New-PSSession $DC -Credential $Cred -Authentication Default $DCInfo = Invoke-Command -Session $DCPssSession {Get-ADDomainController -Identity $using:DC} + Remove-PSSession -Session $DCPssSession $inObj = [ordered] @{ 'DC Name' = ($DCInfo.Name).ToString().ToUpper() 'Domain Name' = $DCInfo.Domain @@ -70,7 +71,7 @@ function Get-AbrADDomainController { $OutObj | Table @TableParams } Write-PscriboMessage "Collecting AD Domain Controller Hardware information for domain $Domain" - Section -Style Heading5 'Domain Controller Hardware Summary' { + Section -Style Heading6 'Domain Controller Hardware Summary' { Paragraph "The following section provides a summary of the Domain Controller Hardware for $($Domain.ToString().ToUpper())." BlankLine $OutObj = @() @@ -84,6 +85,7 @@ function Get-AbrADDomainController { Write-PscriboMessage "Collecting AD Domain Controller Hardware information for $DC." $DCPssSession = New-PSSession $DC -Credential $Cred -Authentication Default $HW = Invoke-Command -Session $DCPssSession -ScriptBlock { Get-ComputerInfo } + Remove-PSSession -Session $DCPssSession if ($HW) { $inObj = [ordered] @{ 'Name' = $HW.CsDNSHostName @@ -117,7 +119,7 @@ function Get-AbrADDomainController { } } Write-PscriboMessage "Collecting AD Domain Controller NTDS information." - Section -Style Heading5 'Domain Controller NTDS Summary' { + Section -Style Heading6 'Domain Controller NTDS Summary' { Paragraph "The following section provides a summary of the Domain Controller NTDS file size on $($Domain.ToString().ToUpper())." BlankLine $OutObj = @() @@ -132,6 +134,7 @@ function Get-AbrADDomainController { $DCPssSession = New-PSSession $DC -Credential $Cred -Authentication Default $NTDS = Invoke-Command -Session $DCPssSession -ScriptBlock {Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Services\NTDS\Parameters | Select-Object -ExpandProperty 'DSA Database File'} $size = Invoke-Command -Session $DCPssSession -ScriptBlock {(Get-ItemProperty -Path $using:NTDS).Length} + Remove-PSSession -Session $DCPssSession if ( $NTDS -and $size ) { $inObj = [ordered] @{ 'Name' = $DC @@ -159,7 +162,7 @@ function Get-AbrADDomainController { $OutObj | Table @TableParams } Write-PscriboMessage "Collecting AD Domain Controller Time Source information." - Section -Style Heading5 'Domain Controller Time Source Summary' { + Section -Style Heading6 'Domain Controller Time Source Summary' { Paragraph "The following section provides a summary of the Domain Controller Time Source configuration on $($Domain.ToString().ToUpper())." BlankLine $OutObj = @() @@ -174,7 +177,7 @@ function Get-AbrADDomainController { $DCPssSession = New-PSSession $DC -Credential $Cred -Authentication Default $NtpServer = Invoke-Command -Session $DCPssSession -ScriptBlock {Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Services\W32Time\Parameters | Select-Object -ExpandProperty 'NtpServer'} $SourceType = Invoke-Command -Session $DCPssSession -ScriptBlock {Get-ItemProperty -Path HKLM:\System\CurrentControlSet\Services\W32Time\Parameters | Select-Object -ExpandProperty 'Type'} - + Remove-PSSession -Session $DCPssSession if ( $NtpServer -and $SourceType ) { $inObj = [ordered] @{ 'Name' = $DC diff --git a/Src/Private/Get-AbrADFSMO.ps1 b/Src/Private/Get-AbrADFSMO.ps1 index 2a9aa2b..df59c42 100644 --- a/Src/Private/Get-AbrADFSMO.ps1 +++ b/Src/Private/Get-AbrADFSMO.ps1 @@ -5,7 +5,7 @@ function Get-AbrADFSMO { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -29,7 +29,7 @@ function Get-AbrADFSMO { } process { - Section -Style Heading4 'Flexible Single Master Operations (FSMO) Information' { + Section -Style Heading5 'Flexible Single Master Operations (FSMO) Information' { Paragraph "The following section provides a summary of the Active Directory FSMO for Domain $($Domain.ToString().ToUpper())." BlankLine $OutObj = @() @@ -48,7 +48,7 @@ function Get-AbrADFSMO { $OutObj += [pscustomobject]$inobj } catch { - Write-PscriboMessage -IsWarning "Error: Could not connect to domain $Domain" + Write-PscriboMessage -IsWarning "Error: Could not get Flexible Single Master Operations (FSMO) Information from domain $Domain" Write-PscriboMessage -IsDebug $_.Exception.Message } diff --git a/Src/Private/Get-AbrADForest.ps1 b/Src/Private/Get-AbrADForest.ps1 index 1f8b257..2b7cc60 100644 --- a/Src/Private/Get-AbrADForest.ps1 +++ b/Src/Private/Get-AbrADForest.ps1 @@ -5,7 +5,7 @@ function Get-AbrADForest { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -30,16 +30,15 @@ function Get-AbrADForest { $Data = Invoke-Command -Session $Session {Get-ADForest} $ForestInfo = $Data.RootDomain.toUpper() Write-PscriboMessage "Discovered Active Directory information of forest $ForestInfo." - $ADVersion = Invoke-Command -Session $Session {Get-ADObject (Get-ADRootDSE).schemaNamingContext -property objectVersion | Select-Object objectVersion} - $ADnumber = $ADVersion -replace "@{objectVersion=","" -replace "}","" - If ($ADnumber -eq '88') {$server = 'Windows Server 2019'} - ElseIf ($ADnumber -eq '87') {$server = 'Windows Server 2016'} - ElseIf ($ADnumber -eq '69') {$server = 'Windows Server 2012 R2'} - ElseIf ($ADnumber -eq '56') {$server = 'Windows Server 2012'} - ElseIf ($ADnumber -eq '47') {$server = 'Windows Server 2008 R2'} - ElseIf ($ADnumber -eq '44') {$server = 'Windows Server 2008'} - ElseIf ($ADnumber -eq '31') {$server = 'Windows Server 2003 R2'} - ElseIf ($ADnumber -eq '30') {$server = 'Windows Server 2003'} + $ADVersion = Invoke-Command -Session $Session {Get-ADObject (Get-ADRootDSE).schemaNamingContext -property objectVersion | Select-Object -ExpandProperty objectVersion} + If ($ADVersion -eq '88') {$server = 'Windows Server 2019'} + ElseIf ($ADVersion -eq '87') {$server = 'Windows Server 2016'} + ElseIf ($ADVersion -eq '69') {$server = 'Windows Server 2012 R2'} + ElseIf ($ADVersion -eq '56') {$server = 'Windows Server 2012'} + ElseIf ($ADVersion -eq '47') {$server = 'Windows Server 2008 R2'} + ElseIf ($ADVersion -eq '44') {$server = 'Windows Server 2008'} + ElseIf ($ADVersion -eq '31') {$server = 'Windows Server 2003 R2'} + ElseIf ($ADVersion -eq '30') {$server = 'Windows Server 2003'} $OutObj = @() if ($Data) { Write-PscriboMessage "Collecting Active Directory information of forest $ForestInfo." @@ -47,13 +46,13 @@ function Get-AbrADForest { $inObj = [ordered] @{ 'Forest Name' = $Item.RootDomain 'Forest Functional Level' = $Item.ForestMode - 'Schema Version' = "ObjectVersion $ADnumber, Correspond to $server" + 'Schema Version' = "ObjectVersion $ADVersion, Correspond to $server" 'Domains' = $Item.Domains -join '; ' 'Global Catalogs' = $Item.GlobalCatalogs -join '; ' 'Application Partitions' = $Item.ApplicationPartitions 'PartitionsContainer' = [string]$Item.PartitionsContainer - 'SPN Suffixes' = $Item.SPNSuffixes - 'UPN Suffixes' = $Item.UPNSuffixes + 'SPN Suffixes' = ConvertTo-EmptyToFiller $Item.SPNSuffixes + 'UPN Suffixes' = ConvertTo-EmptyToFiller $Item.UPNSuffixes } $OutObj += [pscustomobject]$inobj } diff --git a/Src/Private/Get-AbrADGPO.ps1 b/Src/Private/Get-AbrADGPO.ps1 index 4ee1db4..744518b 100644 --- a/Src/Private/Get-AbrADGPO.ps1 +++ b/Src/Private/Get-AbrADGPO.ps1 @@ -5,7 +5,7 @@ function Get-AbrADGPO { .DESCRIPTION .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -41,7 +41,7 @@ function Get-AbrADGPO { Write-PscriboMessage "Collecting Active Directory Group Policy Objects '$($GPO.DisplayName)'. (Group Policy Objects)" $inObj = [ordered] @{ 'Display Name' = $GPO.DisplayName - 'GpoStatus' = ($GPO.GpoStatus -creplace '([A-Z\W_]|\d+)(?> function Format-FileSize - function Invoke-DcDiag { + +function Invoke-DcDiag { + <# + .SYNOPSIS + Used by As Built Report to get the dcdiag tests for a Domain Controller. + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Adam Bertram + + .EXAMPLE + + .LINK + + #> + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DomainController + ) + $result = Invoke-Command -Session $TempPssSession {dcdiag /s:$using:DomainController} + $result | select-string -pattern '\. (.*) \b(passed|failed)\b test (.*)' | ForEach-Object { + $obj = @{ + TestName = $_.Matches.Groups[3].Value + TestResult = $_.Matches.Groups[2].Value + Entity = $_.Matches.Groups[1].Value + } + [pscustomobject]$obj + } +}# end + +function ConvertTo-EmptyToFiller { <# .SYNOPSIS - Used by As Built Report to get the dcdiag tests for a Domain Controller. + Used by As Built Report to convert empty culumns to "-". .DESCRIPTION .NOTES - Version: 0.2.0 - Author: Adam Bertram + Version: 0.3.0 + Author: Jonathan Colon .EXAMPLE .LINK #> - param( - [Parameter(Mandatory)] - [ValidateNotNullOrEmpty()] - [string]$DomainController - ) - $result = Invoke-Command -Session $TempPssSession {dcdiag /s:$using:DomainController} - $result | select-string -pattern '\. (.*) \b(passed|failed)\b test (.*)' | ForEach-Object { - $obj = @{ - TestName = $_.Matches.Groups[3].Value - TestResult = $_.Matches.Groups[2].Value - Entity = $_.Matches.Groups[1].Value + [CmdletBinding()] + [OutputType([String])] + Param + ( + [Parameter ( + Position = 0, + Mandatory)] + [AllowEmptyString()] + [string] + $TEXT + ) + + switch ($TEXT) { + "" {"-"; break} + $Null {"-"; break} + "True" {"Yes"; break} + "False" {"No"; break} + default {$TEXT} } - [pscustomobject]$obj + } # end + +function Convert-IpAddressToMaskLength { + <# + .SYNOPSIS + Used by As Built Report to convert subnet mask to dotted notation. + .DESCRIPTION + + .NOTES + Version: 0.3.0 + Author: Ronald Rink + + .EXAMPLE + + .LINK + + #> + [CmdletBinding()] + [OutputType([String])] + Param + ( + [Parameter ( + Position = 0, + Mandatory)] + [string] + $SubnetMask + ) + + [IPAddress] $MASK = $SubnetMask + $octets = $MASK.IPAddressToString.Split('.') + foreach ($octet in $octets) { + while (0 -ne $octet) { + $octet = ($octet -shl 1) -band [byte]::MaxValue + $result++; } - } \ No newline at end of file + } + return $result; +} \ No newline at end of file diff --git a/Src/Public/Invoke-AsBuiltReport.Microsoft.AD.ps1 b/Src/Public/Invoke-AsBuiltReport.Microsoft.AD.ps1 index e871503..a6cde97 100644 --- a/Src/Public/Invoke-AsBuiltReport.Microsoft.AD.ps1 +++ b/Src/Public/Invoke-AsBuiltReport.Microsoft.AD.ps1 @@ -5,7 +5,7 @@ function Invoke-AsBuiltReport.Microsoft.AD { .DESCRIPTION Documents the configuration of Microsoft AD in Word/HTML/Text formats using PScribo. .NOTES - Version: 0.2.0 + Version: 0.3.0 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -55,13 +55,15 @@ function Invoke-AsBuiltReport.Microsoft.AD { if ($InfoLevel.Forest -gt 0) { try { Section -Style Heading2 "Forest Information." { + Paragraph "The Active Directory framework that holds the objects can be viewed at a number of levels. The forest, tree, and domain are the logical divisions in an Active Directory network. At the top of the structure is the forest. A forest is a collection of trees that share a common global catalog, directory schema, logical structure, and directory configuration. The forest represents the security boundary within which users, computers, groups, and other objects are accessible." + BlankLine Get-AbrADForest -Session $TempPssSession Get-AbrADSite -Session $TempPssSession } } catch { Write-PscriboMessage -IsWarning "Error: Unable to retreive Forest: $ForestInfo information." - Write-PScriboMessage -IsDebug $_.Exception.Message + Write-PscriboMessage -IsWarning $_.Exception.Message continue } } @@ -69,58 +71,61 @@ function Invoke-AsBuiltReport.Microsoft.AD { # Domain Section # #---------------------------------------------------------------------------------------------# if ($InfoLevel.Domain -gt 0) { - foreach ($Domain in (Invoke-Command -Session $TempPssSession {Get-ADForest | Select-Object -ExpandProperty Domains | Sort-Object -Descending})) { - try { - if (Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain}) { - Section -Style Heading3 "Active Directory Information for domain $($Domain.ToString().ToUpper())" { - Paragraph "The following section provides a summary of the AD Domain Information." - BlankLine - Get-AbrADDomain -Domain $Domain -Session $TempPssSession - Get-AbrADFSMO -Domain $Domain -Session $TempPssSession - Get-AbrADTrust -Domain $Domain -Session $TempPssSession -Cred $Credential - Section -Style Heading4 'Domain Controller Information' { - Paragraph "The following section provides a summary of the Active Directory Domain Controller." + Section -Style Heading3 "Active Directory Domain summary for forest $($ForestInfo.toUpper())" { + Paragraph "An Active Directory domain is a collection of objects within a Microsoft Active Directory network. An object can be a single user or a group or it can be a hardware component, such as a computer or printer.Each domain holds a database containing object identity information. Active Directory domains can be identified using a DNS name, which can be the same as an organization's public domain name, a sub-domain or an alternate version (which may end in .local)." + BlankLine + foreach ($Domain in (Invoke-Command -Session $TempPssSession {Get-ADForest | Select-Object -ExpandProperty Domains | Sort-Object -Descending})) { + try { + if (Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain}) { + Section -Style Heading4 "Active Directory Information for domain $($Domain.ToString().ToUpper())" { + Paragraph "The following section provides a summary of the AD Domain Information." BlankLine - Get-AbrADDomainController -Domain $Domain -Session $TempPssSession -Cred $Credential - if ($HealthCheck.DomainController.Diagnostic) { - try { - Section -Style Heading4 'DCDiag Information' { - Paragraph "The following section provides a summary of the Active Directory DC Diagnostic." - BlankLine - $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} - foreach ($DC in $DCs){ - Get-AbrADDCDiag -Domain $Domain -Session $TempPssSession -DC $DC + Get-AbrADDomain -Domain $Domain -Session $TempPssSession + Get-AbrADFSMO -Domain $Domain -Session $TempPssSession + Get-AbrADTrust -Domain $Domain -Session $TempPssSession -Cred $Credential + Section -Style Heading5 'Domain Controller Information' { + Paragraph "The following section provides a summary of the Active Directory Domain Controller." + BlankLine + Get-AbrADDomainController -Domain $Domain -Session $TempPssSession -Cred $Credential + if ($HealthCheck.DomainController.Diagnostic) { + try { + Section -Style Heading6 'DCDiag Information' { + Paragraph "The following section provides a summary of the Active Directory DC Diagnostic." + BlankLine + $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} + foreach ($DC in $DCs){ + Get-AbrADDCDiag -Domain $Domain -Session $TempPssSession -DC $DC + } } } + catch { + Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation. ('DCDiag Information)" + Write-PscriboMessage -IsWarning $_.Exception.Message + continue + } + } + try { + $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} + foreach ($DC in $DCs){ + Get-AbrADInfrastructureService -DC $DC -Cred $Credential + } } catch { - Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation. ('DCDiag Information)" - Write-PScriboMessage -IsDebug $_.Exception.Message + Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation. (ADInfrastructureService)" + Write-PscriboMessage -IsWarning $_.Exception.Message continue } + Get-AbrADSiteReplication -Domain $Domain -Session $TempPssSession + Get-AbrADGPO -Domain $Domain -Session $TempPssSession + Get-AbrADOU -Domain $Domain -Session $TempPssSession } - try { - $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain -Identity $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} - foreach ($DC in $DCs){ - Get-AbrADInfrastructureService -DC $DC -Cred $Credential - } - } - catch { - Write-PscriboMessage -IsWarning "Error: Connecting to remote server $DC failed: WinRM cannot complete the operation. (ADInfrastructureService)" - Write-PScriboMessage -IsDebug $_.Exception.Message - continue - } - Get-AbrADSiteReplication -Domain $Domain -Session $TempPssSession - Get-AbrADGPO -Domain $Domain -Session $TempPssSession - Get-AbrADOU -Domain $Domain -Session $TempPssSession } } } - } - catch { - Write-PscriboMessage -IsWarning "WARNING: Could not connect to domain $Domain" - Write-PscriboMessage -IsDebug $_.Exception.Message - continue + catch { + Write-PscriboMessage -IsWarning $_.Exception.Message + continue + } } } } @@ -128,27 +133,95 @@ function Invoke-AsBuiltReport.Microsoft.AD { # DNS Section # #---------------------------------------------------------------------------------------------# if ($InfoLevel.DNS -gt 0) { - foreach ($Domain in ( Invoke-Command -Session $TempPssSession {Get-ADForest | Select-Object -ExpandProperty Domains | Sort-Object -Descending})) { - try { - if (Invoke-Command -Session $TempPssSession {Get-ADDomain $using:Domain -ErrorAction Stop}) { - Section -Style Heading3 "Domain Name System Information for domain $($Domain.ToString().ToUpper())" { - Paragraph "The following section provides a summary of the Domain Name System Information." - BlankLine - Get-AbrADDNSInfrastructure -Domain $Domain -Session $TempPssSession + Section -Style Heading3 "Domain Name System summary for forest $($ForestInfo.toUpper())" { + Paragraph "The Domain Name System (DNS) is a hierarchical and decentralized naming system for computers, services, or other resources connected to the Internet or a private network. It associates various information with domain names assigned to each of the participating entities. Most prominently, it translates more readily memorized domain names to the numerical IP addresses needed for locating and identifying computer services and devices with the underlying network protocols." + BlankLine + foreach ($Domain in ( Invoke-Command -Session $TempPssSession {Get-ADForest | Select-Object -ExpandProperty Domains | Sort-Object -Descending})) { + try { + if (Invoke-Command -Session $TempPssSession {Get-ADDomain $using:Domain -ErrorAction Stop}) { + Section -Style Heading4 "Domain Name System Information for domain $($Domain.ToString().ToUpper())" { + Paragraph "The following section provides a configuration summary of the Domain Name System." + BlankLine + Get-AbrADDNSInfrastructure -Domain $Domain -Session $TempPssSession + $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} + foreach ($DC in $DCs){ + Get-AbrADDNSZone -Domain $Domain -DC $DC -Cred $Credential + } + } } } - $DCs = Invoke-Command -Session $TempPssSession {Get-ADDomain $using:Domain | Select-Object -ExpandProperty ReplicaDirectoryServers} - foreach ($DC in $DCs){ - Get-AbrADDNSZone -Domain $Domain -DC $DC -Cred $Credential + catch { + Write-PscriboMessage -IsWarning $_.Exception.Message + continue } } - catch { - Write-PscriboMessage -IsWarning "WARNING: Could not connect to domain $Domain" - Write-PscriboMessage -IsDebug $_.Exception.Message - continue + } + } + #---------------------------------------------------------------------------------------------# + # DHCP Section # + #---------------------------------------------------------------------------------------------# + if ($InfoLevel.DHCP -gt 0) { + Section -Style Heading3 "Dynamic Host Configuration Protocol summary for forest $($ForestInfo.toUpper())" { + Paragraph "The Dynamic Host Configuration Protocol (DHCP) is a network management protocol used on Internet Protocol (IP) networks for automatically assigning IP addresses and other communication parameters to devices connected to the network using a client/server architecture." + BlankLine + foreach ($Domain in ( Invoke-Command -Session $TempPssSession {Get-ADForest | Select-Object -ExpandProperty Domains | Sort-Object -Descending})) { + Section -Style Heading4 "Dynamic Host Configuration Protocol information for domain $($Domain.ToString().ToUpper())" { + Paragraph "The following section provides a summary of the Dynamic Host Configuration Protocol." + BlankLine + Get-AbrADDHCPInfrastructure -Domain $Domain -Session $TempPssSession + Section -Style Heading5 "IPv4 Scope Information for Domain $($Domain.ToString().ToUpper())" { + Paragraph "The following section provides a IPv4 configuration summary of the Dynamic Host Configuration Protocol." + BlankLine + try { + Get-AbrADDHCPv4Statistic -Domain $Domain -Session $TempPssSession + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Statistics from $($Domain.ToString().ToUpper())." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + try { + Get-AbrADDHCPv4Failover -Domain $Domain -Session $TempPssSession + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Failover configuration from $($Domain.ToString().ToUpper())." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + $DomainDHCPs = Invoke-Command -Session $TempPssSession { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} | Select-Object -ExpandProperty DnsName} + foreach ($DHCPServer in $DomainDHCPs){ + try { + Get-AbrADDHCPv4Scope -Domain $Domain -Server $DHCPServer -Session $TempPssSession + } + catch { + Write-PScriboMessage -IsWarning $_.Exception.Message + } + } + } + Section -Style Heading5 "IPv6 Scope Information for Domain $($Domain.ToString().ToUpper())" { + Paragraph "The following section provides a IPv6 configuration summary of the Dynamic Host Configuration Protocol." + BlankLine + try { + Get-AbrADDHCPv6Statistic -Domain $Domain -Session $TempPssSession + } + catch { + Write-PScriboMessage -IsWarning "Error: Retreiving DHCP Server IPv4 Statistics from $($Domain.ToString().ToUpper())." + Write-PScriboMessage -IsDebug $_.Exception.Message + } + $DomainDHCPs = Invoke-Command -Session $TempPssSession { Get-DhcpServerInDC | Where-Object {$_.DnsName.split(".", 2)[1] -eq $using:Domain} | Select-Object -ExpandProperty DnsName} + foreach ($DHCPServer in $DomainDHCPs){ + try { + #Get-AbrADDHCPv4Scope -Domain $Domain -Server $DHCPServer -Session $TempPssSession + } + catch { + Write-PscriboMessage -IsWarning $_.Exception.Message + } + } + } + } } } } }#endregion AD Section + Write-PscriboMessage "Clearing PowerShell Session $($TempPssSession.Id)" + Remove-PSSession -Session $TempPssSession }#endregion foreach loop } \ No newline at end of file