In this post I describe creating a simplified PowerShell script for creating a customizable subject matter document template using the PScribo module from the PowerShell Gallery.
Goals:
- Reduce the amount of typing required to produce the document outline, cover, common text, etc.
- Provide functionality over and above use of a static Word document or template.
- Provide a method for automating adding data in an automated way to avoid typical cut and paste for information that is pulled from infrastructure.
- Provide a standard set of document topics that you or your team typically uses for the subject matter.
- Provide a simple method for selecting optional sections.
- Include common text for required sections as needed.
Required PowerShell Modules:
- I tested this using latest Windows PowerShell 5.1
- PScribo
Solution:
To provide a simple method of documented all the standard topics and provide a way for selecting among optional topics I created a CSV file with the following column headers:
- Section: Document the ordering and relationships for the topics, 1, 2, 2.1, etc.
- Key: The Section Name, ‘Document Control, Overview, etc.
- Value: The column to set if a section should be included or not. Blank for no, ‘x’ for yes.
- Comments: Any text.
The function to create the section hash table from the csv file:
function get-SectionDict {
$SectionDict = @{ }
if (Test-Path -Path $SectionDictCsv) {
$cdText = Import-Csv $SectionDictCsv
$cdText | ForEach-Object { if ($_.Key -ne '#') { $SectionDict.add("$($_.key)", "$($_.value)") } }
}
else {
Write-Host "$SectionDictCsv not found - Exiting"
exit
}
Return $SectionDict
}
I added the check for key -eq “#” to provide a way of inserting rows that should be ignored. Intended for rows that only comments.
PScribo Sections can be nested. The general approach is to nest a section inside its parent section like so.
Section 1 { Section 1.2 {} } Section 2…. and wrap the sections in If statements. This allows for selections of individual sub sections and entire parent sections as shown in the code snippet below. If the value of the key in the hash table is null then it evaluates to false in the if statement.
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false)]
[System.String[]]$Format = @('Html', 'Word'),
[Parameter(Mandatory = $false)]
[System.String]$Path = "$env:OneDrive\Documentation\",
[Parameter(Mandatory = $false)]
[System.String]$FileName = "TemplateSample",
[Parameter(Mandatory = $false)]
[System.String]$Title = "Document Sample",
[Parameter(Mandatory = $false)]
[System.String]$SectionDictCsv = ".\SectionDict.csv",
[System.Management.Automation.SwitchParameter]$PassThru
)
try {
Import-Module PScribo -Force -Verbose:$false
}
Catch {
Write-Host "This Script Requires PScribo. It can be installed from the PowerShell Gallery."
}
try {
Import-Module ActiveDirectory
$AdObject = Get-ADUser $($env:UserName)
$userEmail = $($AdObject.UserPrincipalName)
$userName = $($AdObject.Name)
}
catch {
$userName = "Your name here"
$userEmail = "Your email here"
}
function get-SectionDict {
$SectionDict = @{ }
if (Test-Path -Path $SectionDictCsv) {
$cdText = Import-Csv $SectionDictCsv
$cdText | ForEach-Object { if ($_.Key -ne '#') { $SectionDict.add("$($_.key)", "$($_.value)") } }
}
else {
Write-Host "$SectionDictCsv not found - Exiting"
exit
}
Return $SectionDict
}
$sections = Get-SectionDict
$currentDate = Get-Date -Format "M/dd/yyyy"
#$cover_image = [System.Convert]::ToBase64String((Get-Content -Path .\cover_image.png -Encoding Byte))
$ThisDocument = Document -Name $filename {
#region Styles
Style -Name TitleLine1 -Align Center -Size 36 -Bold -Color 'MediumVioletRed' -Font Arial
Style -Name TitleDate -Align Center -Size 14 -Font Arial
Style -Name TitleAuthor -Align Center -Size 20 -Bold -Font Arial
Style -Name 'Heading 1' -Size 16 -Align Left -Bold -Font Arial -Color 'CadetBlue'
Style -Name 'Heading 2' -Size 14 -Align Left -Font Arial -Color 'Black' -Bold
Style -Name 'Heading 3' -Size 12 -Align Left -Font Arial -Color 'Black' -Bold
TableStyle -Name Borderless -HeaderStyle Normal -RowStyle Normal
Style -Name RightAligned -Align Right
Style -Name leftAlign -Align Left
Style -Name Normal -Size 12 -Font Arial -Color 'Black'
#endregion Styles
# use [System.Convert]::ToBase64String to convert an image to text.
$CoverImage = Get-Content -Path .\cover_image.txt
DocumentOption -MarginTopAndBottom 36 -MarginLeftAndRight 54
Blankline -Count 2
Section "$Title" -Style TitleLine1 -ScriptBlock {
BlankLine -Count 2
Image -Text "" -Align Center -Base64 $CoverImage
Paragraph "$userName, ESS Architecture Team" -Style TitleAuthor
Paragraph "$userEmail" -Style TitleDate
BlankLine -Count 1
Paragraph "Date:$currentDate" -Style TitleDate
Blankline -Count 8
Paragraph "Trademarks are acknowledged." -Size 10 -Italic
}
PageBreak
TOC -Name "Table of Contents" -
PageBreak
Section "Document Control" -Style 'Heading1' -ScriptBlock {
Paragraph "This document is for ."
}
Pagebreak
if ($sections.'OverView') {
Section -Name "Overview" -Style 'Heading1' -ScriptBlock {
Paragraph "TBD."
if ($sections.'Goals and Objectives') {
Section -Name "Goals and Objectives" -Style 'Heading2' {
Paragraph "TBD"
}
}
if ($sections.'Services Description') {
Section -Name "Services Description" -Style 'Heading2' -ScriptBlock {
Paragraph "TBD"
}
}
if ($sections.'Definitions') {
Section "Definitions" -Style 'Heading2' {
Paragraph "TBD" -Tabs 1
Paragraph "TBD" -tabs 1
Paragraph "..." -tabs 1
}
}
if ($sections.'Scope') {
Section -Name "Scope" -Style 'Heading2' -ScriptBlock {
Paragraph 'This Change applies ....'
}
}
}
}
PageBreak
# Other sections here
Footer -Default -NoSpace {
Paragraph "Page <!# PageNumber #!> of <!# TotalPages #!>" -Style 'LeftAligned'
}
}
$ThisDocument | Export-Document -Path "$Path" -Format $Format -PassThru:$PassThru
With this structure you can easily create a CSV that has the complete set of standard document sections along with the required code stanzas. Then additional CSV files can be created for specific topics with just the appropriate sections selected to be included.