Public/Export-CMHealthCheck.ps1

#requires -version 3
function Export-CMHealthCheck {
    <#
    .SYNOPSIS
        Convert extracted ConfigMgr site information to Word Document
    .DESCRIPTION
        Converts the data output from Get-CMHealthCheck to generate a
        report document using Microsoft Word (2010, 2013, 2016). Intended
        to be invoked on a desktop computer which has Office installed.
    .PARAMETER ReportFolder
        Path to output data folder (e.g. ".\2017-11-17\cm01.contoso.com")
    .PARAMETER Detailed
        Collect more granular data for final reporting
    .PARAMETER CoverPage
        Word theme cover page (default = "Slice (Light)")
    .PARAMETER Template
        Word document file to use as a template. Should have a cover page already in place.
        If Template is specified, CoverPage and Copyright are ignored.
    .PARAMETER CustomerName
        Name of customer (default = "Customer Name")
    .PARAMETER AuthorName
        Report Author name (default = "Your Name")
    .PARAMETER CopyrightName
        Text to use for copyright footer string (default = "Your Company Name")
    .PARAMETER Overwrite
        Overwrite existing report file if found
    .PARAMETER Healthcheckfilename
        Healthcheck configuration XML file name (default = ".\assets\cmhealthcheck.xml")
        The file can be local, UNC or URI sourced as well
    .PARAMETER MessagesFilename
        Status and error message lookup table (default = ".\assets\messages.xml")
        The file can be local, UNC or URI sourced as well
    .PARAMETER Healthcheckdebug
        Enable verbose output (or use -Verbose)
    .EXAMPLE
        Export-CMHealthCheck -ReportFolder "2017-11-17\cm01.contoso.com" -Detailed -CustomerName "Contoso" -AuthorName "David Stein" -CopyrightName "ACME Consulting" -Overwrite -Verbose
    .EXAMPLE
        Export-CMHealthCheck -ReportFolder "2017-11-17\cm01.contoso.com" -Detailed -Template ".\contoso.docx" -CustomerName "Contoso" -AuthorName "David Stein" -CopyrightName "ACME Consulting" -Overwrite -Verbose
    .NOTES
        * 1.0.4 - 12/04/2017 - David Stein
        * Thanks to Rafael Perez for inventing this - http://www.rflsystems.co.uk
        * Thanks to Carl Webster for the basis of Word functions - http://www.carlwebster.com
        * Thanks to David O'Brien for additional Word function - http://www.david-obrien.net/2013/06/20/huge-powershell-inventory-script-for-configmgr-2012/
        * Thanks to Starbucks for empowering me to survive hours of clicking through the Office Word API reference
    #>

    [CmdletBinding()]
    param (
        [Parameter (Mandatory = $True, HelpMessage = "Collected data folder")] 
            [ValidateNotNullOrEmpty()]
            [string] $ReportFolder,
        [parameter(Mandatory=$False, HelpMessage="Log folder path")]
            [ValidateNotNullOrEmpty()]
            [string] $OutputFolder = "$($env:USERPROFILE)\Documents",
        [Parameter (Mandatory = $False, HelpMessage = "Export full data, not only summary")] 
            [switch] $Detailed,
        [parameter (Mandatory = $False, HelpMessage = "Word Template cover page name")] 
            [string] $CoverPage = "Slice (Light)",
        [parameter (Mandatory = $False, HelpMessage = "Word document source file")]
            [string] $Template = "", 
        [parameter (Mandatory = $False, HelpMessage = "Customer company name")] 
            [string] $CustomerName = "Customer Name",
        [parameter (Mandatory = $False, HelpMessage = "Author's full name")] 
            [string] $AuthorName = "Your Name",
        [parameter (Mandatory = $False, HelpMessage = "Footer text")]
            [string] $CopyrightName  = "Your Company Name",
        [Parameter (Mandatory = $False, HelpMessage = "HealthCheck query file name")] 
            [string] $Healthcheckfilename = "", 
        [Parameter (Mandatory = $False, HelpMessage = "HealthCheck messages file name")]
            [string] $MessagesFilename = "", 
        [Parameter (Mandatory = $False, HelpMessage = "Debug more?")] 
            $Healthcheckdebug = $False,
        [parameter (Mandatory = $False, HelpMessage = "Overwrite existing report file")]
            [switch] $Overwrite
    )
    $time1 = Get-Date -Format "hh:mm:ss"
    $ModuleData = Get-Module CMHealthCheck
    $ModuleVer  = $ModuleData.Version -join '.'
    $ModulePath = $ModuleData.Path -replace 'CMHealthCheck.psm1', ''
    $tsLog      = Join-Path -Path $OutputFolder -ChildPath "Export-CMHealthCheck-Transcript.log"
    $logfile    = Join-Path -Path $OutputFolder -ChildPath "Export-CMHealthCheck.log"
    try {
        Stop-Transcript -ErrorAction SilentlyContinue
    }
    catch {}
    finally {
        Start-Transcript -Path $tsLog -Append -ErrorAction SilentlyContinue
    }
    
    $TempFilename      = "cmhealthreport.docx"
    $DefaultTableStyle = "Grid Table 4 - Accent 1"
    $TableStyle        = "Grid Table 4 - Accent 1"
    $TableSimpleStyle  = "Grid Table 4 - Accent 1"
    $ReviewTableStyle  = "Grid Table 4 - Accent 6"
    $RecTableStyle     = "Grid Table 4 - Accent 3"
    $ReviewTableCols   = ("No.", "Severity", "Comment")
    $bLogValidation    = $False
    $bAutoProps        = $True
    $NormalFontSize    = 10
    $poshversion       = $PSVersionTable.PSVersion.Major
    $osversion         = (Get-WmiObject -Class Win32_OperatingSystem).Caption
    #$FormatEnumerationLimit = -1
    
    if ($Healthcheckfilename -eq "") {
        $Healthcheckfilename = Join-Path -Path $ModulePath -ChildPath "assets\cmhealthcheck.xml"
    }
    if ($MessagesFilename -eq "") {
        $MessagesFilename = Join-Path -Path $ModulePath -ChildPath "assets\messages.xml"
    }

    if ($healthcheckdebug -eq $true) { 
        $PSDefaultParameterValues = @{"*:Verbose"=$True}
    }
    $logFolder = Join-Path -Path $PWD.Path -ChildPath "_Logs\"
    if (-not (Test-Path $logFolder)) {
        mkdir $logFolder -Force | Out-Null
    }
    if ($reportFolder.Substring($reportFolder.length-1) -ne '\') { $reportFolder+= '\' }
    
    $Error.Clear()

    $poshversion = $PSVersionTable.PSVersion.Major
    Show-CMHCInfo
    
    [xml]$HealthCheckXML = Get-CmHealthCheckFile -XmlSource $HealthcheckFilename
    [xml]$MessagesXML    = Get-CmHealthCheckFile -XmlSource $MessagesFilename
     
    Write-Log -Message "Connecting to Microsoft Word..." -LogFile $logfile
    try {
        $Word = New-Object -ComObject "Word.Application" -ErrorAction Stop
    }
    catch {
        Invoke-Error -Message "Microsoft Word could not be opened!"
        break
    }

    if ($HealthCheckXML -and $MessagesXML) {
        $bLogValidation = $true
        Write-Log -Message "Provisioning config table" -LogFile $logfile
        $ConfigTable = New-Object System.Data.DataTable 'ConfigTable'
        $ConfigTable = Get-CmXMLFile -Path $reportFolder -FileName "config.xml"
        if ($ConfigTable -eq "") {
            Invoke-Error -Message "File $configfile does not exist, no futher action taken"; break
        }
        Write-Log -Message "Provisioning report table" -LogFile $logfile
        $ReportTable = New-Object System.Data.DataTable 'ReportTable'
        $ReportTable = Get-CmXMLFile -Path $reportFolder -FileName "report.xml"
        if ($ReportTable -eq "") {
            Invoke-Error -Message "File $repfile does not exist, no futher action taken"; break
        }
        Write-Log -Message "Assigning number of days from config data..." -LogFile $logfile
        if ($poshversion -eq 3) { 
            $NumberOfDays = $ConfigTable.Rows[0].NumberOfDays
        }
        else { 
            $NumberOfDays = $ConfigTable.NumberOfDays
        }

        if (!(Test-Powershell64bit)) { Invoke-Error -Message "Powershell is not 64bit, no futher action taken"; break }
        
        $wordVersion = $Word.Version
        Write-Log -Message "Microsoft Word version: $WordVersion" -LogFile $logfile
        if ($wordVersion -lt '15.0') { Invoke-Error -Message "This module requires Word 2013 or newer"; break }
        if ($Template -ne "") {
            $newFile = Get-WordTempSource -SourceFile $Template
            Write-Log -Message "Opening temp file [$newFile]..." -LogFile $logfile
            try {
                $Doc = $Word.Documents.Open($newFile)
            }
            catch {
                Invoke-Error -Message "Failed to open temp document file: $newFile"
                break
            }
        }
        else {
            Write-Log -Message "Creating new (blank) document..." -LogFile $logfile
            $Doc = $Word.Documents.Add()
        }
        if ($doc -eq $null) { Invoke-Error -Message "Failed to obtain handle to Word document"; break }
        $Word.Visible = $True
        $Selection = $Word.Selection

        Set-WordOptions
        Set-DocProperties

        Write-Log -Message "Loading default building blocks " -LogFile $logfile
        $Word.Templates.LoadBuildingBlocks() | Out-Null    
        $BuildingBlocks = $Word.Templates | Where-Object {$_.name -eq "Built-In Building Blocks.dotx"}

        if ($Template -eq "") {
            Write-Log -Message "Inserting cover page: $CoverPage" -LogFile $logfile
            $part = $BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
            $part.Insert($selection.Range,$True) | Out-Null
        }
        else {
            Write-Log -Message "Cover page option ignored when using custom template" -LogFile $logfile
            $Selection.EndKey(6, 0) | Out-Null            
        }

        $selection.InsertNewPage()
        Set-WordTOC

        $selection.InsertNewPage()
        $currentview = $doc.ActiveWindow.ActivePane.view.SeekView
        $doc.ActiveWindow.ActivePane.view.SeekView = 4
        Set-WordFooter

        $doc.ActiveWindow.ActivePane.view.SeekView = $currentview
        $selection.EndKey(6,0) | Out-Null
        Set-WordAbstract

        Write-WordTableGrid -Caption "Revision History" -Rows 4 -ColumnHeadings ("Version","Date","Description","Author")

        $selection.InsertNewPage()
        Write-WordTableGrid -Caption "Summary of Findings" -Rows 4 -ColumnHeadings ("Item", "Severity", "Explanation") -StyleName $ReviewTableStyle
        Write-WordTableGrid -Caption "Summary of Recommendations" -Rows 4 -ColumnHeadings ("Item", "Severity", "Explanation") -StyleName $RecTableStyle

        $selection.InsertNewPage()
        Write-DocReportSections

        $selection.InsertNewPage()
        Set-DocAppendix
    }
    else {
        Write-Log -Message "Unable to load Healthcheck or Messages XML data" -Severity 3 -LogFile $logfile -ShowMsg
        $error.Clear()
    }

    if ($toc -ne $null) {
        $Doc.TablesOfContents.Item(1).Update()
        if ($bLogValidation -eq $False) {
            Write-Host "Finishing up healthcheck report"
        }
        else {
            Write-Log -Message "Finishing up HealthCheck Export" -LogFile $logfile
        }
    }
    $Difference = Get-TimeOffset -StartTime $time1
    Write-Log -Message "Completed in: $Difference (hh:mm:ss)" -LogFile $logfile -ShowMsg
    Stop-Transcript
}
Export-ModuleMember -Function Export-CMHealthcheck