Public/Export-CMHealthCheck.ps1
#requires -version 3 <# .SYNOPSIS Export-CMHealthcheck.ps1 reads the output from Get-CM-Inventory.ps1 to generate a final report using Microsoft Word (2010, 2013, 2016) .DESCRIPTION Export-CMHealthcheck.ps1 reads the output from Get-CM-Inventory.ps1 to generate a final report using Microsoft Word (2010, 2013, 2016) .PARAMETER ReportFolder [string] [required] Path to output data folder .PARAMETER Detailed [switch] [optional] Collect more granular data for final reporting .PARAMETER Healthcheckfilename [string] [optional] healthcheck configuration file name default = "https://raw.githubusercontent.com/Skatterbrainz/CM_HealthCheck/master/cmhealthcheck.xml" alternate = ".\cmhealthcheck.xml" .PARAMETER Healthcheckdebug [boolean] [optional] Enable verbose output (or use -Verbose) .PARAMETER CoverPage [string] [optional] default = "Slice (Light)" .PARAMETER CustomerName [string] [optional] Name of customer default = "Company" .PARAMETER AuthorName [string] [optional] Name of report author default = "Author" .PARAMETER Overwrite [switch] [optional] Overwrite existing report file if found .NOTES Thanks to: Base script (the hardest part) created by Rafael Perez (www.rflsystems.co.uk) Word functions copied from Carl Webster (www.carlwebster.com) Word functions copied from David O'Brien (www.david-obrien.net/2013/06/20/huge-powershell-inventory-script-for-configmgr-2012/) .EXAMPLE Option 1: powershell.exe -ExecutionPolicy Bypass .\Export-CMHealthcheck [Parameters] Option 2: Open Powershell and execute .\Export-CMHealthcheck [Parameters] Option 3: .\Export-CMHealthCheck -ReportFolder "2017-05-17\cm1.contoso.com" -Detailed -CustomerName "ACME" -AuthorName "David Stein" -Overwrite -Verbose #> function Export-CMHealthCheck { [CmdletBinding()] param ( [Parameter (Mandatory = $True, HelpMessage = "Collected data folder")] [ValidateNotNullOrEmpty()] [string] $ReportFolder, [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 = "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 = 'https://raw.githubusercontent.com/Skatterbrainz/CM_HealthCheck/master/cmhealthcheck.xml', [Parameter (Mandatory = $False, HelpMessage = "HealthCheck messages file name")] [string] $MessagesFilename = 'https://raw.githubusercontent.com/Skatterbrainz/CM_HealthCheck/master/Messages.xml', [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" Start-Transcript -Path ".\_logs\export-reportfile.log" -Append $ScriptVersion = "1710.01" $bLogValidation = $False $bAutoProps = $True $currentFolder = $PWD.Path $NormalFontSize = 10 $poshversion = $PSVersionTable.PSVersion.Major $osversion = (Get-WmiObject -Class Win32_OperatingSystem).Caption $FormatEnumerationLimit = -1 if ($healthcheckdebug -eq $true) { $PSDefaultParameterValues = @{"*:Verbose"=$True}; $currentFolder = "C:\Temp\CMHealthCheck\" } $logFolder = $currentFolder + "\_Logs\" if (-not (Test-Path $logFolder)) { Write-Error "$logFolder was not found!" break } if ($reportFolder.substring($reportFolder.length-1) -ne '\') { $reportFolder+= '\' } $component = ($MyInvocation.MyCommand.Name -replace '.ps1', '') $logfile = $logFolder + $component + ".log" $Error.Clear() Write-Log -Message "==========" -LogFile $logfile -ShowMsg $false Write-Log -Message "Script Version: $ScriptVersion" -LogFile $logfile Write-Log -Message "Windows Version: $osversion" -LogFile $logfile Write-Log -Message "Running Powershell version: $poshversion" -LogFile $logfile Write-Log -Message "Running Powershell 64 bits" -LogFile $logfile Write-Log -Message "Report Folder: $reportFolder" -LogFile $logfile Write-Log -Message "Detailed Report: $detailed" -LogFile $logfile Write-Verbose "Export-CMHealthCheck $ScriptVersion" Write-Verbose "Current Folder: $currentFolder" Write-Verbose "Log Folder: $logFolder" Write-Verbose "Log File: $logfile" Write-Verbose "Healthcheck Data File: $Healthcheckfilename" Write-Output "script version: $ScriptVersion" $poshversion = $PSVersionTable.PSVersion.Major Write-Verbose "info: connecting to Microsoft Word..." try { $Word = New-Object -ComObject "Word.Application" -ErrorAction Stop } catch { Write-Warning "Error: This script requires Microsoft Word" break } if ($Healthcheckfilename.StartsWith('http')) { Write-Verbose "importing xml from remote URI: $healthcheckfilename" try { [xml]$HealthCheckXML = Invoke-RestMethod -Uri $Healthcheckfilename } catch { Write-Error "Failed to import data from Uri: $HealthcheckFilename" Write-Warning "If no Internet access is allowed, use -HealthcheckFilename '.\cmhealthcheck.xml'" break } Write-Verbose "configuration XML data loaded successfully" } else { Write-Verbose "importing Configuration xml from local file: $healthcheckfilename" if (!(Test-Path -Path $healthcheckfilename)) { Write-Warning "File $healthcheckfilename does not exist, no futher action taken" break } else { try { [xml]$HealthCheckXML = Get-Content ($healthcheckfilename) } catch { Write-Error "Failed to import data from local file: $HealthcheckFilename" break } Write-Verbose "configuration XML data loaded successfully" } } if ($MessagesFilename.StartsWith('http')) { Write-Verbose "importing Messages xml from remote URL: $MessagesFilename" try { [xml]$MessagesXML = Get-XmlUrlContent -Url $MessagesFilename } catch { Write-Error "Failed to import data from Uri: $MessagesFilename" Write-Warning "If no Internet access is allowed, use -MessagesFilename '.\messages.xml'" break } Write-Verbose "Messages XML data loaded successfully" } else { if (!(Test-Path -Path ".\Messages.xml")) { Write-Warning "File Messages.xml does not exist, no futher action taken" break } else { Write-Verbose "reading messages.xml data" try { [xml]$MessagesXML = Get-Content '.\Messages.xml' } catch { Write-Error "Failed to import data from local file: $MessagesFilename" break } } Write-Verbose "Messages XML data loaded successfully" } if ($HealthCheckXML -and $MessagesXML) { if (Test-Folder -Path $logFolder) { try { New-Item ($logFolder + 'Test.log') -Type File -Force | Out-Null Remove-Item ($logFolder + 'Test.log') -Force | Out-Null } catch { Write-Warning "Unable to read/write file on $logFolder folder, no futher action taken" break } } else { Write-Host "Unable to create Log Folder, no futher action taken" -ForegroundColor Red break } $bLogValidation = $true if (Test-Folder -Path $reportFolder -Create $false) { if (!(Test-Path -Path ($reportFolder + "config.xml"))) { Write-Log -Message "File $($reportFolder)config.xml does not exist, no futher action taken" -Severity 3 -LogFile $logfile break } else { Write-Verbose "reading config.xml data" $ConfigTable = Import-CliXml -Path ($reportFolder + "config.xml") } if ($poshversion -ne 3) { $NumberOfDays = $ConfigTable.Rows[0].NumberOfDays } else { $NumberOfDays = $ConfigTable.NumberOfDays } if (!(Test-Path -Path ($reportFolder + "report.xml"))) { Write-Log -Message "File $($reportFolder)report.xml does not exist, no futher action taken" -Severity 3 -LogFile $logfile break } else { $ReportTable = New-Object System.Data.DataTable 'ReportTable' $ReportTable = Import-CliXml -Path ($reportFolder + "report.xml") } } else { Write-Warning "Folder: $reportFolder does not exist, no futher action taken" break } if (!(Test-Powershell64bit)) { Write-Log -Message "Powershell is not 64bit, no futher action taken" -Severity 3 -LogFile $logfile break } $wordVersion = $Word.Version Write-Log -Message "Word Version: $WordVersion" -LogFile $logfile Write-Verbose "info: Microsoft Word version: $WordVersion" if ($WordVersion -ge "16.0") { $TableStyle = "Grid Table 4 - Accent 1" $TableSimpleStyle = "Grid Table 4 - Accent 1" } elseif ($WordVersion -eq "15.0") { $TableStyle = "Grid Table 4 - Accent 1" $TableSimpleStyle = "Grid Table 4 - Accent 1" } elseif ($WordVersion -eq "14.0") { $TableStyle = "Medium Shading 1 - Accent 1" $TableSimpleStyle = "Light Grid - Accent 1" } else { Write-Log -Message "This script requires Word 2010 to 2016 version, no further action taken" -Severity 3 -LogFile $logfile break } $Word.Visible = $True $Doc = $Word.Documents.Add() $Selection = $Word.Selection Write-Verbose "info: disabling real-time spelling and grammar check" $Word.Options.CheckGrammarAsYouType = $False $Word.Options.CheckSpellingAsYouType = $False $Doc.Styles("Normal").Font.Size = $NormalFontSize Write-Verbose "info: loading default building blocks template" $word.Templates.LoadBuildingBlocks() | Out-Null $BuildingBlocks = $word.Templates | Where-Object {$_.name -eq "Built-In Building Blocks.dotx"} $part = $BuildingBlocks.BuildingBlockEntries.Item($CoverPage) if ($doc -eq $null) { Write-Error "Failed to obtain handle to Word document" break } if ($bAutoProps -eq $True) { Write-Verbose "info: setting document properties" $doc.BuiltInDocumentProperties("Title") = "System Center Configuration Manager HealthCheck" $doc.BuiltInDocumentProperties("Subject") = "Prepared for $CustomerName" $doc.BuiltInDocumentProperties("Author") = $AuthorName $doc.BuiltInDocumentProperties("Company") = $CopyrightName $doc.BuiltInDocumentProperties("Category") = "HEALTHCHECK" $doc.BuiltInDocumentProperties("Keywords") = "sccm,healthcheck,systemcenter,configmgr,$CustomerName" } Write-Verbose "info: inserting document parts" $part.Insert($selection.Range,$True) | Out-Null $selection.InsertNewPage() Write-Verbose "info: inserting table of contents" $toc = $BuildingBlocks.BuildingBlockEntries.Item("Automatic Table 2") $toc.Insert($selection.Range,$True) | Out-Null $selection.InsertNewPage() $currentview = $doc.ActiveWindow.ActivePane.view.SeekView $doc.ActiveWindow.ActivePane.view.SeekView = 4 $selection.HeaderFooter.Range.Text= "Copyright $([char]0x00A9) $((Get-Date).Year) - $CopyrightName" $selection.HeaderFooter.PageNumbers.Add(2) | Out-Null $doc.ActiveWindow.ActivePane.view.SeekView = $currentview $selection.EndKey(6,0) | Out-Null $absText = "This document provides a point-in-time inventory and analysis of the " $absText += "System Center Configuration Manager site environment for $CustomerName. " $absText += "For questions, concerns or comments, please consult the $CopyrightName " $absText += "architect or engineer who provided this document." Write-WordText -WordSelection $selection -Text "Abstract" -Style "Heading 1" -NewLine $true Write-WordText -WordSelection $selection -Text $absText -NewLine $true #Write-WordText -WordSelection $selection -Text "Revision History" -Style "Heading 1" -NewLine $true #Write-RevisionTable Write-WordTableGrid -Caption "Revision History" -Rows 4 -ColumnHeadings ("Version","Date","Description","Author") $selection.InsertNewPage() Write-WordTableGrid -Caption "Summary of Findings" -Rows 4 -ColumnHeadings ("Item", "Explanation") Write-WordTableGrid -Caption "Summary of Recommendations" -Rows 4 -ColumnHeadings ("Item", "Severity", "Explanation") $selection.InsertNewPage() Write-WordReportSection -HealthCheckXML $HealthCheckXML -section '1' -Doc $doc -Selection $selection -LogFile $logfile Write-WordReportSection -HealthCheckXML $HealthCheckXML -section '2' -Doc $doc -Selection $selection -LogFile $logfile Write-WordReportSection -HealthCheckXML $HealthCheckXML -section '3' -Doc $doc -Selection $selection -LogFile $logfile Write-WordReportSection -HealthCheckXML $HealthCheckXML -section '4' -Doc $doc -Selection $selection -LogFile $logfile Write-WordReportSection -HealthCheckXML $HealthCheckXML -section '5' -Doc $doc -Selection $selection -LogFile $logfile if ($detailed -eq $true) { Write-WordReportSection -HealthCheckXML $HealthCheckXML -Section '5' -Detailed $true -Doc $doc -Selection $selection -LogFile $logfile } Write-WordReportSection -HealthCheckXML $HealthCheckXML -Section '6' -Doc $doc -Selection $selection -LogFile $logfile } else { Write-Log -Message "unable to load Healthcheck or Messages XML data" -Severity 3 -LogFile $logfile Write-Error "failed to load configuration data from XML files" $error.Clear() } if ($toc -ne $null) { $doc.TablesOfContents.Item(1).Update() if ($bLogValidation -eq $False) { Write-Host "ending healthcheck report" Write-Host "=================" } else { Write-Log -Message "Ending HealthCheck Export" -LogFile $logfile Write-Log -Message "=================" -LogFile $logfile } } $time2 = Get-Date -Format "hh:mm:ss" $RunTime = New-TimeSpan $time1 $time2 $Difference = "{0:g}" -f $RunTime Write-Output "completed in (HH:MM:SS) $Difference" Stop-Transcript } Export-ModuleMember -Function Export-CMHealthcheck |