‘Sup PSHomies,
Last blog I talked about how to create a AD configuration snapshot. I saved the AD Configuration using the Export-Clixml cmdlet. As promised here’s the follow up: How to create a report from the saved snapshot.
I’m a fan of HTML for reporting purposes. In the past I’ve dabbled in creating reports using XML in combination with CSS. The challenge was creating a well-defined XML file. If you’ve ever had the idea of using Export-Clixml to combine with CSS then you’re in for a disappointment! ConvertTo-HTML is a better fit for reporting. Having said that, creating a well-defined XML file can also be a challenge. As luck would have it there’s a mini-series on the subject, check it out if you want to go down that route.
My favorite way of creating HTML reports these days is using PScribo, brought to us by Iain Brighton. I saw him demonstrate the module at the PowerShell Conference in Stockholm on youtube. PScribo sure makes creating reports easier! PScribo supports different output formats:
- HTML
- Word
- Text
- XML
You can also edit the style of your document. To get a better impression of all the possibilities have a look at the video. The module has enough examples to help you get started. Before you know it you’ll be hooked!
Here’s the script:
<# | |
Author: I.C.A. Strachan | |
Version: | |
Version History: | |
Purpose: Create PScribo Report of saved AD Configuration | |
#> | |
[CmdletBinding()] | |
Param( | |
$snapshotDate= '29032016' | |
) | |
#PScribo link: https://github.com/iainbrighton/PScribo | |
Import-Module PScribo -Verbose:$false | |
#Get ADSnapshot | |
$SavedADSnapshot = Import-Clixml .\export\dsa\ADReport-$($snapshotDate).xml | |
$reportAD = Document "AD snapshot report - $($snapshotDate)" { | |
GlobalOption -ForceUppercaseSection -EnableSectionNumbering -PageSize A4 -Margin 24 | |
BlankLine -Count 20 | |
Paragraph "Active Directory snapshot report - $($snapshotDate)" -Style Title | |
BlankLine -Count 20 | |
PageBreak | |
TOC -Name 'Table of Contents' | |
PageBreak | |
Section -Style Heading1 'Forest Information' { | |
$ADForest = [Ordered]@{ | |
Name = $($SavedADSnapshot.ForestInformation.Name) | |
RootDomain = $($SavedADSnapshot.ForestInformation.RootDomain) | |
ForestMode = $($SavedADSnapshot.ForestInformation.ForestMode.ToString()) | |
Domains = $($SavedADSnapshot.ForestInformation.Domains) | |
} | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 -Hashtable $ADForest | |
} | |
Section -Style Heading1 'Domain Information' { | |
$ADDomain = [Ordered]@{ | |
NetBIOSName = $($SavedADSnapshot.DomainInformation.NetBIOSName) | |
DomainMode = $($SavedADSnapshot.DomainInformation.DomainMode.ToString()) | |
DistinguishedName = $($SavedADSnapshot.DomainInformation.DistinguishedName) | |
DomainSID = $($SavedADSnapshot.DomainInformation.DomainSID) | |
} | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 -Hashtable $ADDomain | |
} | |
Section -Style Heading1 'FSMO Roles' { | |
$SavedADSnapshot.ForestInformation | | |
Select-Object DomainNamingMaster,SchemaMaster | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 | |
Blankline | |
$SavedADSnapshot.DomainInformation | | |
Select-Object PDCEmulator,InfrastructureMaster,RIDMaster | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 | |
} | |
Section -Style Heading1 'Global Catalogs' { | |
$GCs = $SavedADSnapshot.ForestInformation | | |
Select-Object -ExpandProperty GlobalCatalogs | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 -InputObject $GCs | |
} | |
Section -Style Heading1 'Domain Controllers' { | |
$SavedADSnapshot.DomainControllers | | |
Select-Object Name,OperatingSystem,IPv4Address,Site | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 | |
} | |
Section -Style Heading1 'Default Domain Password Policy' { | |
$SavedADSnapshot.DefaultPassWordPoLicy | | |
Select-Object ComplexityEnabled,LockoutDuration,LockoutObservationWindow,LockoutThreshold, | |
MaxPasswordAge,MinPasswordAge,MinPasswordLength,PasswordHistoryCount,ReversibleEncryptionEnabled | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 | |
} | |
Section -Style Heading1 'Domain Administrators' { | |
$SavedADSnapshot.DomainAdministrators | | |
Select-Object Name,DistinguishedName | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -Width 0 | |
} | |
Section -Style Heading1 'Organizational Units' { | |
$SavedADSnapshot.OrganizationalUnits | | |
Select-Object Name,DistinguishedName | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -Width 0 | |
} | |
Section -Style Heading1 'Sites & Subnets' { | |
Section -Style Heading2 'Sites' { | |
$SavedADSnapshot.Sites | | |
Select-Object Name,Description,DistinguishedName | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -List -Width 0 | |
} | |
Section -Style Heading2 'Sitelinks' { | |
$SavedADSnapshot.SiteLinks | | |
Select-Object Name,Cost,ReplicationFrequencyInMinutes | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -Width 0 | |
} | |
Section -Style Heading2 'Subnets' { | |
$SavedADSnapshot.Subnets | | |
Select-Object Name,Site | | |
Table -Name 'AutofitWidth-AutofitCell-NoHighlighting' -Width 0 | |
} | |
} | |
} | |
#Render report in HTML format | |
$reportAD | Export-Document -Path .\export\dsa -Format Html |
I’ve recently discovered markdown. If you’re comfortable creating HTML documents, then MD shouldn’t be much of a challenge ;-). BTW if you’re looking for a good MD reader, vscode has you covered. VSCode is gaining momentum in the PowerShell community. I’ll admit to being hooked on ISESteroids, still, Tobias Weltner said there will be a major update pretty soon… So who knows what this might mean?
I decided to give it a try in MD Format as well!
<# | |
Author: I.C.A. Strachan | |
Version: | |
Version History: | |
Purpose: Create MD Report of saved AD Configuration | |
#> | |
[CmdletBinding()] | |
Param( | |
$snapshotDate= '29032016' | |
) | |
#Get ADSnapshot | |
$SavedADSnapshot = Import-Clixml .\export\dsa\ADReport-$($snapshotDate).xml | |
#MDReport Title | |
$MDReport = "# Active Directory snapshot report - $($snapshotDate)`n" | |
#region Get Forest Information | |
$ADForest = @" | |
# Forest Information | |
| | | | |
|---|---| | |
|Name | $($SavedADSnapshot.ForestInformation.Name) | | |
|RootDomain | $($SavedADSnapshot.ForestInformation.RootDomain) | | |
|ForestMode | $($SavedADSnapshot.ForestInformation.ForestMode.ToString()) | | |
|Domains | $($SavedADSnapshot.ForestInformation.Domains) | | |
"@ | |
$MDReport += $ADForest | |
#endregion | |
#region Domain Information | |
$ADDomain = @" | |
# Domain Information | |
| | | | |
|---|---| | |
|NetBIOSName | $($SavedADSnapshot.DomainInformation.NetBIOSName) | | |
|DomainMode | $($SavedADSnapshot.DomainInformation.DomainMode.ToString()) | | |
|DistinguishedName | $($SavedADSnapshot.DomainInformation.DistinguishedName) | | |
|DomainSID | $($SavedADSnapshot.DomainInformation.DomainSID) | | |
"@ | |
$MDReport += $ADDomain | |
#endregion | |
#region Default Password Policy | |
$ADPasswordPolicy = @" | |
# Default Password Policy | |
| | | | |
|---|---| | |
|ComplexityEnabled | $($SavedADSnapshot.DefaultPassWordPoLicy.ComplexityEnabled) | | |
|DistinguishedName | $($SavedADSnapshot.DefaultPassWordPoLicy.DistinguishedName) | | |
|LockoutDuration | $($SavedADSnapshot.DefaultPassWordPoLicy.LockoutDuration) | | |
|LockoutObservationWindow | $($SavedADSnapshot.DefaultPassWordPoLicy.LockoutObservationWindow) | | |
|LockoutThreshold | $($SavedADSnapshot.DefaultPassWordPoLicy.LockoutThreshold) | | |
|MaxPasswordAge | $($SavedADSnapshot.DefaultPassWordPoLicy.MaxPasswordAge) | | |
|MinPasswordAge | $($SavedADSnapshot.DefaultPassWordPoLicy.MinPasswordAge) | | |
|MinPasswordLength | $($SavedADSnapshot.DefaultPassWordPoLicy.MinPasswordLength) | | |
|PasswordHistoryCount | $($SavedADSnapshot.DefaultPassWordPoLicy.PasswordHistoryCount) | | |
|ReversibleEncryptionEnabled | $($SavedADSnapshot.DefaultPassWordPoLicy.ReversibleEncryptionEnabled) | | |
"@ | |
$MDReport += $ADPasswordPolicy | |
#endregion | |
#region FSMO Roles | |
$ADFSMORoles = @" | |
# FSMO Roles | |
| | | | |
|---|---| | |
|DomainNamingMaster| $($SavedADSnapshot.ForestInformation.DomainNamingMaster) | | |
|SchemaMaster | $($SavedADSnapshot.ForestInformation.SchemaMaster) | | |
|PDCEmulator | $($SavedADSnapshot.DomainInformation.PDCEmulator) | | |
|RIDMaster | $($SavedADSnapshot.DomainInformation.RIDMaster) | | |
|InfrastructureMaster | $($SavedADSnapshot.DomainInformation.InfrastructureMaster) | | |
"@ | |
$MDReport += $ADFSMORoles | |
#endregion | |
#region Global Catalogs | |
$ADGCs = @" | |
# Global Catalogs | |
|Global Catalogs | | |
|---| | |
$( | |
$SavedADSnapshot.ForestInformation.GlobalCatalogs | | |
ForEach-Object { | |
"|$_|`n" | |
} | |
) | |
"@ | |
$MDReport += $ADGCs | |
#endregion | |
#region Domain Controllers | |
$ADDCs = @" | |
# Domain Controllers | |
|Name|OperatingSystem|IPv4Address|Site| | |
|---|---|---|---| | |
$( | |
$SavedADSnapshot.DomainControllers | | |
ForEach-Object { | |
"|$($_.Name)|$($_.OperatingSystem)|$($_.IPv4Address)|$($_.Site)|`n" | |
} | |
) | |
"@ | |
$MDReport += $ADDCs | |
#endregion | |
#region Domain Administrators | |
$ADDomainAdmins = @" | |
# Domain Administrators | |
|Name|DistinguishedName| | |
|---|---| | |
$( | |
$SavedADSnapshot.DomainAdministrators | | |
ForEach-Object { | |
"|$($_.Name)|$($_.DistinguishedName)|`n" | |
} | |
) | |
"@ | |
$MDReport += $ADDomainAdmins | |
#endregion | |
#region Sites & Subnets | |
$ADSitesSubnets = @" | |
# Sites & Subnets | |
## Sites | |
|Name|Description|DistinguishedName| | |
|---|---|---| | |
$( | |
$SavedADSnapshot.Sites | | |
ForEach-Object { | |
"|$($_.Name)|$($_.Description)|$($_.DistinguishedName)|`n" | |
} | |
) | |
## Sitelinks | |
|Name|Cost|ReplicationFrequencyInMinutes | | |
|---|---|---| | |
$( | |
$SavedADSnapshot.SiteLinks | | |
ForEach-Object { | |
"|$($_.Name)|$($_.Cost)|$($_.ReplicationFrequencyInMinutes)|`n" | |
} | |
) | |
## Subnets | |
|Name|Site| | |
|---|---| | |
$( | |
$SavedADSnapshot.Subnets | | |
ForEach-Object { | |
"|$($_.Name)|$($_.Site)|`n" | |
} | |
) | |
"@ | |
$MDReport += $ADSitesSubnets | |
#endregion | |
#region Organizational Units | |
$ADOUs = @" | |
# Organizational Units | |
|Name|DistinguishedName| | |
|---|---| | |
$( | |
$SavedADSnapshot.OrganizationalUnits | | |
ForEach-Object { | |
"|$($_.Name)|$($_.DistinguishedName)|`n" | |
} | |
) | |
"@ | |
#$MDReport += $ADOUs | |
#endregion | |
#Save MDReport | |
$MDReport | Out-File ".\export\dsa\AD snapshot report - $($snapshotDate).md" -Encoding utf8 -Force |
Not bad…
MD format has a small footprint which could be interesting. MD files can be converted to other formats. Doug Finke has an excellent vscode extension to render md files in pdf, word or html, using PanDoc.
PScribo is definitely worth a try! MD is also an option. Generating reports in Powershell just got easier thanks to Iain & Doug! Great addition guys! Keep up the good work!
Hope it’s worth something to you
Ttyl,
Urv
Pingback: Active Directory Operations Test | pshirwin
Pingback: Powershell Reporting using Pscribo Part 1 -Getting the XML | Wei-Yen Tan’s IT Journal
Pingback: AD Operation Validation class | pshirwin