Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
0
#This File is in Unicode format. Do not edit in an ASCII editor.
<#
.SYNOPSIS
Perform an Active Directory Health Check.
.DESCRIPTION
Perform an Active Directory Health Check based on LDAP queries.
These are originally based on Jeff Wouters personal best practices.
No rights can be claimed by this report!
You will see a lot of redirection to streams in this script. i.e. 3>$Null,
4>$Null and
possibly *>$Null
This is explained here:
https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-
streams-redirection-and-write-host-in-powershell/
.PARAMETER CompanyAddress
Company Address to use for the Cover Page, if the Cover Page has the Address
field.
This parameter is only valid with the MSWORD and PDF output parameters.
This parameter has an alias of CA.
.PARAMETER CompanyEmail
Company Email to use for the Cover Page, if the Cover Page has the Email
field.
The following Cover Pages have an Email field:
Facet (Word 2013/2016)
This parameter is only valid with the MSWORD and PDF output parameters.
This parameter has an alias of CE.
.PARAMETER CompanyFax
Company Fax to use for the Cover Page, if the Cover Page has the Fax field.
This parameter is only valid with the MSWORD and PDF output parameters.
This parameter has an alias of CF.
.PARAMETER CompanyName
Company Name to use for the Cover Page.
Default value is contained in
HKCU:\Software\Microsoft\Office\Common\UserInfo\CompanyName or
HKCU:\Software\Microsoft\Office\Common\UserInfo\Company, whichever is
populated
on the computer running the script.
This parameter has an alias of CN.
If either registry key does not exist and this parameter is not specified,
the report
will not contain a Company Name on the cover page.
This parameter is only valid with the MSWORD and PDF output parameters.
.PARAMETER CompanyPhone
Company Phone to use for the Cover Page if the Cover Page has the Phone
field.
This parameter is only valid with the MSWORD and PDF output parameters.
This parameter has an alias of CPh.
.PARAMETER CoverPage
What Microsoft Word Cover Page to use.
Only Word 2010, 2013 and 2016 are supported.
(default cover pages in Word en-US)
This is used when the script developer requests more troubleshooting data.
Text file is placed in the same folder from where the script is run.
This will generate a DOCX document with all the checks included.
For each check, a separate CSV file will be created with the results.
A log file is created for the purpose of troubleshooting.
All files are created at the location of the script that is executed.
.EXAMPLE
PS C:\PSScript > .\ADHealthCheck_V2.ps1 -MSWord -Sites -Users -Groups
This will generate a DOCX document with the checks for Sites, Users and
Groups.
.EXAMPLE
PS C:\PSScript > .\ADHealthCheck_V2.ps1 -Folder \\FileServer\ShareName
This will generate a DOCX document with all the checks included.
Output file will be saved in the path \\FileServer\ShareName
.EXAMPLE
PS C:\PSScript > .\ADHealthCheck_V2.ps1 -SmtpServer mail.domain.tld
-From ADAdmin@domain.tld -To ITGroup@domain.tld
Script will use the email server mail.domain.tld, sending from
ADAdmin@domain.tld, sending to ITGroup@domain.tld.
If the current user's credentials are not valid to send email, the user will
be prompted
to enter valid credentials.
.EXAMPLE
PS C:\PSScript > .\ADHealthCheck_V2.ps1 -SmtpServer smtp.office365.com
-SmtpPort 587
-UseSSL -From Webster@CarlWebster.com -To ITGroup@CarlWebster.com
Script will use the email server smtp.office365.com on port 587 using SSL,
sending from
webster@carlwebster.com, sending to ITGroup@carlwebster.com.
If the current user's credentials are not valid to send email, the user will
be prompted
to enter valid credentials.
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
No objects are output from this script. This script creates a Word or PDF
document.
.NOTES
NAME : AD Health Check.ps1
AUTHOR : Jeff Wouters [MVP Windows PowerShell], Carl Webster and
Michael B. Smith
VERSION : 2.04
LAST EDIT : 6-Apr-2018
The Word file generation part of the script is based upon the work done by:
Set-StrictMode -Version 2
#force -verbose on
$PSDefaultParameterValues = @{"*:Verbose"=$True}
$SaveEAPreference = $ErrorActionPreference
$ErrorActionPreference = 'SilentlyContinue'
##$Script:ThisScriptPath = $(Split-Path ((Get-PSCallStack)[0]).ScriptName) -- this
is crap after v1
$Script:ThisScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
If($PSBoundParameters.ContainsKey('Log'))
{
$Script:LogPath = "$Script:ThisScriptPath\ADHealthCheckTranscript.txt"
If((Test-Path $Script:LogPath) -eq $true)
{
Write-Verbose "$(Get-Date): Transcript/Log $Script:LogPath already exists"
$Script:StartLog = $false
}
Else
{
try
{
Start-Transcript -Path $Script:LogPath -Force -Verbose:$false | Out-
Null
Write-Verbose "$(Get-Date): Transcript/log started at $Script:LogPath"
$Script:StartLog = $true
}
catch
{
Write-Verbose "$(Get-Date): Transcript/log failed at $Script:LogPath"
$Script:StartLog = $false
}
}
}
If($MSWord)
{
Write-Verbose "$(Get-Date): MSWord is set"
}
ElseIf($PDF)
{
Write-Verbose "$(Get-Date): PDF is set"
}
Else
{
$ErrorActionPreference = $SaveEAPreference
Write-Verbose "$(Get-Date): Unable to determine output parameter"
If($Null -eq $MSWord)
{
Write-Verbose "$(Get-Date): MSWord is Null"
}
ElseIf($Null -eq $PDF)
{
Write-Verbose "$(Get-Date): PDF is Null"
}
Else
{
Write-Verbose "$(Get-Date): MSWord is " $MSWord
Write-Verbose "$(Get-Date): PDF is " $PDF
}
Write-Error "Unable to determine output parameter. Script cannot continue"
Exit
}
If($Dev)
{
$Error.Clear()
$pwdPath = $Folder
If($pwdPath -eq "")
{
$pwdpath = $pwd.Path
}
If($ScriptInfo)
{
$pwdPath = $Folder
If($pwdPath -eq "")
{
$pwdpath = $pwd.Path
}
[int]$PointsPerTabStop = 36
[int]$Indent0TabStops = 0 * $PointsPerTabStop
[int]$Indent1TabStops = 1 * $PointsPerTabStop
[int]$Indent2TabStops = 2 * $PointsPerTabStop
[int]$Indent3TabStops = 3 * $PointsPerTabStop
[int]$Indent4TabStops = 4 * $PointsPerTabStop
# http://www.thedoctools.com/index.php?
show=wt_style_names_english_danish_german_french
[int]$wdStyleHeading1 = -2
[int]$wdStyleHeading2 = -3
[int]$wdStyleHeading3 = -4
[int]$wdStyleHeading4 = -5
[int]$wdStyleNoSpacing = -158
[int]$wdTableGrid = -155
#http://groovy.codehaus.org/modules/scriptom/1.6.0/scriptom-office-2K3-
tlb/apidocs/org/codehaus/groovy/scriptom/tlb/office/word/WdLineStyle.html
[int]$wdLineStyleNone = 0
[int]$wdLineStyleSingle = 1
[int]$wdHeadingFormatTrue = -1
[int]$wdHeadingFormatFalse = 0
}
Function SetWordHashTable
{
Param([string]$CultureCode)
#ca - Catalan
#da - Danish
#de - German
#en - English
#es - Spanish
#fi - Finnish
#fr - French
#nb - Norwegian
#nl - Dutch
#pt - Portuguese
#sv - Swedish
#zh - Chinese
[string]$toc = $(
Switch ($CultureCode)
{
'ca-' { 'Taula automática 2'; Break }
'da-' { 'Automatisk tabel 2'; Break }
'de-' { 'Automatische Tabelle 2'; Break }
'en-' { 'Automatic Table 2'; Break }
'es-' { 'Tabla automática 2'; Break }
'fi-' { 'Automaattinen taulukko 2'; Break }
'fr-' { 'Table automatique 2'; Break } #changed 13-feb-2017 david
roquier and samuel legrand
'nb-' { 'Automatisk tabell 2'; Break }
'nl-' { 'Automatische inhoudsopgave 2'; Break }
'pt-' { 'Sumário Automático 2'; Break }
'sv-' { 'Automatisk innehållsförteckning2'; Break }
'zh-' { '自动目录 2'; Break }
}
)
$Script:myHash = @{}
$Script:myHash.Word_TableOfContents = $toc
$Script:myHash.Word_NoSpacing = $wdStyleNoSpacing
$Script:myHash.Word_Heading1 = $wdStyleheading1
$Script:myHash.Word_Heading2 = $wdStyleheading2
$Script:myHash.Word_Heading3 = $wdStyleheading3
$Script:myHash.Word_Heading4 = $wdStyleheading4
$Script:myHash.Word_TableGrid = $wdTableGrid
}
Function GetCulture
{
Param([int]$WordValue)
#ca - Catalan
#da - Danish
#de - German
#en - English
#es - Spanish
#fi - Finnish
#fr - French
#nb - Norwegian
#nl - Dutch
#pt - Portuguese
#sv - Swedish
#zh - Chinese
Switch ($WordValue)
{
{$CatalanArray -contains $_} {$CultureCode = "ca-"}
{$ChineseArray -contains $_} {$CultureCode = "zh-"}
{$DanishArray -contains $_} {$CultureCode = "da-"}
{$DutchArray -contains $_} {$CultureCode = "nl-"}
{$EnglishArray -contains $_} {$CultureCode = "en-"}
{$FinnishArray -contains $_} {$CultureCode = "fi-"}
{$FrenchArray -contains $_} {$CultureCode = "fr-"}
{$GermanArray -contains $_} {$CultureCode = "de-"}
{$NorwegianArray -contains $_} {$CultureCode = "nb-"}
{$PortugueseArray -contains $_} {$CultureCode = "pt-"}
{$SpanishArray -contains $_} {$CultureCode = "es-"}
{$SwedishArray -contains $_} {$CultureCode = "sv-"}
Default {$CultureCode = "en-"}
}
Return $CultureCode
}
Function ValidateCoverPage
{
Param([int]$xWordVersion, [string]$xCP, [string]$CultureCode)
$xArray = ""
Switch ($CultureCode)
{
'ca-' {
If($xWordVersion -eq $wdWord2016)
{
$xArray = ("Austin", "En bandes", "Faceta",
"Filigrana",
"Integral", "Ió (clar)", "Ió (fosc)", "Línia
lateral",
"Moviment", "Quadrícula", "Retrospectiu", "Sector
(clar)",
"Sector (fosc)", "Semàfor", "Visualització
principal", "Whisp")
}
ElseIf($xWordVersion -eq $wdWord2013)
{
$xArray = ("Austin", "En bandes", "Faceta",
"Filigrana",
"Integral", "Ió (clar)", "Ió (fosc)", "Línia
lateral",
"Moviment", "Quadrícula", "Retrospectiu", "Sector
(clar)",
"Sector (fosc)", "Semàfor", "Visualització", "Whisp")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alfabet", "Anual", "Austin",
"Conservador",
"Contrast", "Cubicles", "Diplomàtic", "Exposició",
"Línia lateral", "Mod", "Mosiac", "Moviment", "Paper
de diari",
"Perspectiva", "Piles", "Quadrícula", "Sobri",
"Transcendir", "Trencaclosques")
}
}
'da-' {
If($xWordVersion -eq $wdWord2016)
{
$xArray = ("Austin", "BevægElse", "Brusen", "Facet",
"Filigran",
"Gitter", "Integral", "Ion (lys)", "Ion (mørk)",
"Retro", "Semafor", "Sidelinje", "Stribet",
"Udsnit (lys)", "Udsnit (mørk)", "Visningsmaster")
}
ElseIf($xWordVersion -eq $wdWord2013)
{
$xArray = ("BevægElse", "Brusen", "Ion (lys)",
"Filigran",
"Retro", "Semafor", "Visningsmaster", "Integral",
"Facet", "Gitter", "Stribet", "Sidelinje", "Udsnit
(lys)",
"Udsnit (mørk)", "Ion (mørk)", "Austin")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("BevægElse", "Moderat", "Perspektiv",
"Firkanter",
"Overskrid", "Alfabet", "Kontrast", "Stakke",
"Fliser", "Gåde",
"Gitter", "Austin", "Eksponering", "Sidelinje",
"Enkel",
"Nålestribet", "Årlig", "Avispapir", "Tradionel")
}
}
'de-' {
If($xWordVersion -eq $wdWord2016)
{
$xArray = ("Austin", "Bewegung", "Facette",
"Filigran",
"Gebändert", "Integral", "Ion (dunkel)", "Ion
(hell)",
"Pfiff", "Randlinie", "Raster", "Rückblick",
"Segment (dunkel)", "Segment (hell)", "Semaphor",
"ViewMaster")
}
ElseIf($xWordVersion -eq $wdWord2013)
{
$xArray = ("Semaphor", "Segment (hell)", "Ion
(hell)",
"Raster", "Ion (dunkel)", "Filigran", "Rückblick",
"Pfiff",
"ViewMaster", "Segment (dunkel)", "Verbunden",
"Bewegung",
"Randlinie", "Austin", "Integral", "Facette")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alphabet", "Austin", "Bewegung",
"Durchscheinend",
"Herausgestellt", "Jährlich", "Kacheln", "Kontrast",
"Kubistisch",
"Modern", "Nadelstreifen", "Perspektive", "Puzzle",
"Randlinie",
"Raster", "Schlicht", "Stapel", "Traditionell",
"Zeitungspapier")
}
}
'en-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("Austin", "Banded", "Facet", "Filigree",
"Grid",
"Integral", "Ion (Dark)", "Ion (Light)", "Motion",
"Retrospect",
"Semaphore", "Sideline", "Slice (Dark)", "Slice
(Light)", "ViewMaster",
"Whisp")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alphabet", "Annual", "Austere", "Austin",
"Conservative",
"Contrast", "Cubicles", "Exposure", "Grid", "Mod",
"Motion", "Newsprint",
"Perspective", "Pinstripes", "Puzzle", "Sideline",
"Stacks", "Tiles", "Transcend")
}
}
'es-' {
If($xWordVersion -eq $wdWord2016)
{
$xArray = ("Austin", "Con bandas", "Cortar (oscuro)",
"Cuadrícula",
"Whisp", "Faceta", "Filigrana", "Integral", "Ion
(claro)",
"Ion (oscuro)", "Línea lateral", "Movimiento",
"Retrospectiva",
"Semáforo", "Slice (luz)", "Vista principal",
"Whisp")
}
ElseIf($xWordVersion -eq $wdWord2013)
{
$xArray = ("Whisp", "Vista principal", "Filigrana",
"Austin",
"Slice (luz)", "Faceta", "Semáforo", "Retrospectiva",
"Cuadrícula",
"Movimiento", "Cortar (oscuro)", "Línea lateral",
"Ion (oscuro)",
"Ion (claro)", "Integral", "Con bandas")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alfabeto", "Anual", "Austero", "Austin",
"Conservador",
"Contraste", "Cuadrícula", "Cubículos", "Exposición",
"Línea lateral",
"Moderno", "Mosaicos", "Movimiento", "Papel
periódico",
"Perspectiva", "Pilas", "Puzzle", "Rayas",
"Sobrepasar")
}
}
'fi-' {
If($xWordVersion -eq $wdWord2016)
{
$xArray = ("Filigraani", "Integraali", "Ioni
(tumma)",
"Ioni (vaalea)", "Opastin", "Pinta", "Retro",
"Sektori (tumma)",
"Sektori (vaalea)", "Vaihtuvavärinen", "ViewMaster",
"Austin",
"Kuiskaus", "Liike", "Ruudukko", "Sivussa")
}
ElseIf($xWordVersion -eq $wdWord2013)
{
$xArray = ("Filigraani", "Integraali", "Ioni
(tumma)",
"Ioni (vaalea)", "Opastin", "Pinta", "Retro",
"Sektori (tumma)",
"Sektori (vaalea)", "Vaihtuvavärinen", "ViewMaster",
"Austin",
"Kiehkura", "Liike", "Ruudukko", "Sivussa")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Aakkoset", "Askeettinen", "Austin",
"Kontrasti",
"Laatikot", "Liike", "Liituraita", "Mod", "Osittain
peitossa",
"Palapeli", "Perinteinen", "Perspektiivi", "Pinot",
"Ruudukko",
"Ruudut", "Sanomalehtipaperi", "Sivussa",
"Vuotuinen", "Ylitys")
}
}
'fr-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("À bandes", "Austin", "Facette",
"Filigrane",
"Guide", "Intégrale", "Ion (clair)", "Ion (foncé)",
"Lignes latérales", "Quadrillage", "Rétrospective",
"Secteur (clair)",
"Secteur (foncé)", "Sémaphore", "ViewMaster",
"Whisp")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alphabet", "Annuel", "Austère", "Austin",
'nb-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("Austin", "BevegElse", "Dempet", "Fasett",
"Filigran",
"Integral", "Ion (lys)", "Ion (mørk)", "Retrospekt",
"Rutenett",
"Sektor (lys)", "Sektor (mørk)", "Semafor",
"Sidelinje", "Stripet",
"ViewMaster")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alfabet", "Årlig", "Avistrykk", "Austin",
"Avlukker",
"BevegElse", "Engasjement", "Enkel", "Fliser",
"Konservativ",
"Kontrast", "Mod", "Perspektiv", "Puslespill",
"Rutenett", "Sidelinje",
"Smale striper", "Stabler", "Transcenderende")
}
}
'nl-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("Austin", "Beweging", "Facet",
"Filigraan", "Gestreept",
"Integraal", "Ion (donker)", "Ion (licht)", "Raster",
"Segment (Light)", "Semafoor", "Slice (donker)",
"Spriet",
"Terugblik", "Terzijde", "ViewMaster")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Aantrekkelijk", "Alfabet", "Austin",
"Bescheiden",
"Beweging", "Blikvanger", "Contrast", "Eenvoudig",
"Jaarlijks",
"Krantenpapier", "Krijtstreep", "Kubussen", "Mod",
"Perspectief",
"Puzzel", "Raster", "Stapels",
"Tegels", "Terzijde")
}
}
'pt-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("Animação", "Austin", "Em Tiras",
"Exibição Mestra",
"Faceta", "Fatia (Clara)", "Fatia (Escura)",
"Filete", "Filigrana",
"Grade", "Integral", "Íon (Claro)", "Íon (Escuro)",
"Linha Lateral",
"Retrospectiva", "Semáforo")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alfabeto", "Animação", "Anual",
"Austero", "Austin", "Baias",
"Conservador", "Contraste", "Exposição", "Grade",
"Ladrilhos",
"Linha Lateral", "Listras", "Mod", "Papel Jornal",
"Perspectiva", "Pilhas",
"Quebra-cabeça", "Transcend")
}
}
'sv-' {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion -eq
$wdWord2016)
{
$xArray = ("Austin", "Band", "Fasett", "Filigran",
"Integrerad", "Jon (ljust)",
"Jon (mörkt)", "Knippe", "Rutnät", "RörElse", "Sektor
(ljus)", "Sektor (mörk)",
"Semafor", "Sidlinje", "VisaHuvudsida", "Återblick")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alfabetmönster", "Austin", "Enkelt",
"Exponering", "Konservativt",
"Kontrast", "Kritstreck", "Kuber", "Perspektiv",
"Plattor", "Pussel", "Rutnät",
"RörElse", "Sidlinje", "Sobert", "Staplat",
"Tidningspapper", "Årligt",
"Övergående")
}
}
'zh-' {
If($xWordVersion -eq $wdWord2010 -or $xWordVersion -eq
$wdWord2013 -or $xWordVersion -eq $wdWord2016)
{
$xArray = ('奥斯汀', '边线型', '花丝', '怀旧', '积分',
'离子(浅色)', '离子(深色)', '母版型', '平面', '切片(浅色)',
'切片(深色)', '丝状', '网格', '镶边', '信号灯',
'运动型')
}
}
Default {
If($xWordVersion -eq $wdWord2013 -or $xWordVersion
-eq $wdWord2016)
{
$xArray = ("Austin", "Banded", "Facet",
"Filigree", "Grid",
"Integral", "Ion (Dark)", "Ion (Light)",
"Motion", "Retrospect",
"Semaphore", "Sideline", "Slice (Dark)", "Slice
(Light)", "ViewMaster",
"Whisp")
}
ElseIf($xWordVersion -eq $wdWord2010)
{
$xArray = ("Alphabet", "Annual", "Austere",
"Austin", "Conservative",
"Contrast", "Cubicles", "Exposure", "Grid",
"Mod", "Motion", "Newsprint",
"Perspective", "Pinstripes", "Puzzle",
"Sideline", "Stacks", "Tiles", "Transcend")
}
}
}
Function Stop-WinWord
{
Write-Debug "***Enter Stop-WinWord"
$SessionID = $proc.SessionId
If( $null -eq $SessionID )
{
Write-Debug "Stop-WinWord: SessionId on $PID is null"
throw "Can't find a session for pid $PID"
}
If( 0 -eq $SessionID )
{
Write-Debug "Stop-WinWord: SessionId is 0 -- that is a bug"
throw "SessionId is zero for pid $PID"
}
If( !$wordproc )
{
Write-Debug "***Exit Stop-WinWord: no WinWord tasks are running #2"
Return ## WinWord is not running in ANY session
}
Function CheckWordPrereq
{
If((Test-Path REGISTRY::HKEY_CLASSES_ROOT\Word.Application) -eq $False)
{
$ErrorActionPreference = $SaveEAPreference
Write-Host "`n`n`t`tThis script directly outputs to Microsoft Word,
please install Microsoft Word`n`n"
Exit
}
Function ValidateCompanyName
{
[bool]$xResult = Test-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "CompanyName"
If($xResult)
{
Return Get-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "CompanyName"
}
Else
{
$xResult = Test-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "Company"
If($xResult)
{
Return Get-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "Company"
}
Else
{
Return ""
}
}
}
#http://stackoverflow.com/questions/5648931/test-if-registry-value-exists
# This Function just gets $True or $False
Function Test-RegistryValue($path, $name)
{
$key = Get-Item -LiteralPath $path -EA 0
$key -and $Null -ne $key.GetValue($name, $Null)
}
Function WriteWordLine
#Function created by Ryan Revord
#@rsrevord on Twitter
#Function created to make output to Word easy in this script
#updated 27-Mar-2014 to include font name, font size, italics and bold options
#update 5-May-2016 by Michael B. Smith
{
Param(
[int] $style = 0,
[int] $tabs = 0,
[string] $name = '',
[string] $value = '',
[string] $fontName = $null,
[int] $fontSize = 0,
[bool] $italics = $false,
[bool] $boldface = $false,
[Switch] $nonewline
)
#build # of tabs
While($tabs -gt 0)
{
$output += "`t"
$tabs--
}
If(![String]::IsNullOrEmpty($fontName))
{
$Script:Selection.Font.name = $fontName
}
If($fontSize -ne 0)
{
$Script:Selection.Font.size = $fontSize
}
Function Set-DocumentProperty {
<#
.SYNOPSIS
Function to set the Title Page document properties in MS Word
.DESCRIPTION
Long description
.PARAMETER Document
Current Document Object
.PARAMETER DocProperty
Parameter description
.PARAMETER Value
Parameter description
.EXAMPLE
Set-DocumentProperty -Document $Script:Doc -DocProperty Title -Value
'MyTitle'
.EXAMPLE
Set-DocumentProperty -Document $Script:Doc -DocProperty Company -Value
'MyCompany'
.EXAMPLE
Set-DocumentProperty -Document $Script:Doc -DocProperty Author -Value 'Jim
Moyle'
.EXAMPLE
Set-DocumentProperty -Document $Script:Doc -DocProperty Subject -Value
'MySubjectTitle'
.NOTES
Function Created by Jim Moyle June 2017
Twitter : @JimMoyle
#>
param (
[object]$Document,
[String]$DocProperty,
[string]$Value
)
try {
$binding = "System.Reflection.BindingFlags" -as [type]
$builtInProperties = $Document.BuiltInDocumentProperties
$property = [System.__ComObject].invokemember("item",
$binding::GetProperty, $null, $BuiltinProperties, $DocProperty)
[System.__ComObject].invokemember("value", $binding::SetProperty, $null,
$property, $Value)
}
catch {
Write-Warning "Failed to set $DocProperty to $Value"
}
}
Function AbortScript
{
$Word.Quit()
Write-Verbose "$(Get-Date): System Cleanup"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject( $Word ) | Out-
Null
If( Get-Variable -Name Word -Scope Global )
{
Remove-Variable -Name word -Scope Global
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
Write-Verbose "$(Get-Date): Script has been aborted"
$ErrorActionPreference = $SaveEAPreference
Exit
}
Function FindWordDocumentEnd
{
#Return focus to main document
$Script:Doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument
#move to the end of the current document
$Script:Selection.EndKey($wdStory,$wdMove) | Out-Null
}
<#
.Synopsis
Add a table to a Microsoft Word document
.DESCRIPTION
This Function adds a table to a Microsoft Word document from either an array
of
Hashtables or an array of PSCustomObjects.
Using this Function is quicker than setting each table cell individually but
can
only utilise the built-in MS Word table autoformats. Individual tables cells
can
be altered after the table has been appended to the document (a table
reference
is Returned).
.EXAMPLE
AddWordTable -Hashtable $HashtableArray
This example adds table to the MS Word document, utilising all key/value
pairs in
the array of hashtables. Column headers will display the key names as
defined.
Note: the columns might not be displayed in the order that they were defined.
To
ensure columns are displayed in the required order utilise the -Columns
parameter.
.EXAMPLE
AddWordTable -Hashtable $HashtableArray -List
This example adds table to the MS Word document, utilising all key/value
pairs in
the array of hashtables. No column headers will be added, in a ListView
format.
Note: the columns might not be displayed in the order that they were defined.
To
ensure columns are displayed in the required order utilise the -Columns
parameter.
.EXAMPLE
AddWordTable -CustomObject $PSCustomObjectArray
This example adds table to the MS Word document, utilising all note property
names
the array of PSCustomObjects. Column headers will display the note property
names.
Note: the columns might not be displayed in the order that they were defined.
To
ensure columns are displayed in the required order utilise the -Columns
parameter.
.EXAMPLE
AddWordTable -Hashtable $HashtableArray -Columns
FirstName,LastName,EmailAddress
This example adds a table to the MS Word document, but only using the
specified
key names: FirstName, LastName and EmailAddress. If other keys are present in
the
array of Hashtables they will be ignored.
.EXAMPLE
AddWordTable -CustomObject $PSCustomObjectArray -Columns
FirstName,LastName,EmailAddress -Headers "First Name","Last Name","Email Address"
This example adds a table to the MS Word document, but only using the
specified
PSCustomObject note properties: FirstName, LastName and EmailAddress. If
other note
properties are present in the array of PSCustomObjects they will be ignored.
The
display names for each specified column header has been overridden to display
a
custom header. Note: the order of the header names must match the specified
columns.
#>
Function AddWordTable
{
[CmdletBinding()]
Param
(
# Array of Hashtable (including table headers)
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true,
ParameterSetName='Hashtable', Position=0)]
[ValidateNotNullOrEmpty()] [System.Collections.Hashtable[]] $Hashtable,
# Array of PSCustomObjects
[Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true,
ParameterSetName='CustomObject', Position=0)]
[ValidateNotNullOrEmpty()] [PSCustomObject[]] $CustomObject,
# Array of Hashtable key names or PSCustomObject property names to
include, in display order.
# If not supplied then all Hashtable keys or all PSCustomObject
properties will be displayed.
[Parameter(ValueFromPipelineByPropertyName=$true)] [AllowNull()]
[string[]] $Columns = $null,
# Array of custom table header strings in display order.
[Parameter(ValueFromPipelineByPropertyName=$true)] [AllowNull()]
[string[]] $Headers = $null,
# AutoFit table behavior.
[Parameter(ValueFromPipelineByPropertyName=$true)] [AllowNull()] [int]
$AutoFit = -1,
# List view (no headers)
[Switch] $List,
# Grid lines
[Switch] $NoGridLines=$false,
# Built-in Word table formatting style constant
# Would recommend only $wdTableFormatContempory for normal usage
(possibly $wdTableFormatList5 for List view)
[Parameter(ValueFromPipelineByPropertyName=$true)] [int] $Format = '-
231'
)
Begin
{
Write-Debug ("Using parameter set '{0}'" -f
$PSCmdlet.ParameterSetName);
## Check if -Columns wasn't specified but -Headers were (saves some
additional parameter sets!)
If(($Columns -eq $null) -and ($Headers -ne $null))
{
Write-Warning "No columns specified and therefore, specified
headers will be ignored.";
$Columns = $null;
}
ElseIf(($Columns -ne $null) -and ($Headers -ne $null))
{
## Check if number of specified -Columns matches number of
specified -Headers
If($Columns.Length -ne $Headers.Length)
{
Write-Error "The specified number of columns does not match
the specified number of headers.";
}
} ## end ElseIf
} ## end Begin
Process
{
## Build the Word table data string to be converted to a range and then
a table later.
[System.Text.StringBuilder] $WordRangeString = New-Object
System.Text.StringBuilder;
Switch ($PSCmdlet.ParameterSetName)
{
'CustomObject'
{
If($Columns -eq $null)
{
## Build the available columns from all availble
PSCustomObject note properties
[string[]] $Columns = @();
## Add each NoteProperty name to the array
ForEach($Property in ($CustomObject | Get-Member
-MemberType NoteProperty))
{
$Columns += $Property.Name;
}
}
Default
{ ## Hashtable
If($Columns -eq $null)
{
## Build the available columns from all available
hashtable keys. Hopefully
## all Hashtables have the same keys (they should for
a table).
$Columns = $Hashtable[0].Keys;
}
#the next line causes the heading row to flow across page breaks
$WordTable.Rows.First.Headingformat = $wdHeadingFormatTrue;
If(!$NoGridLines)
{
$WordTable.Borders.InsideLineStyle = $wdLineStyleSingle;
$WordTable.Borders.OutsideLineStyle = $wdLineStyleSingle;
}
Return $WordTable;
} ## end Process
}
<#
.Synopsis
Sets the format of one or more Word table cells
.DESCRIPTION
This Function sets the format of one or more table cells, either from a
collection
of Word COM object cell references, an individual Word COM object cell
reference or
a hashtable containing Row and Column information.
The font name, font size, bold, italic , underline and shading values can be
used.
.EXAMPLE
SetWordCellFormat -Hashtable $Coordinates -Table $TableReference -Bold
This example sets all text to bold that is contained within the
$TableReference
Word table, using an array of hashtables. Each hashtable contain a pair of
co-
ordinates that is used to select the required cells. Note: the hashtable must
contain the .Row and .Column key names. For example:
@ { Row = 7; Column = 3 } to set the cell at row 7 and column 3 to bold.
.EXAMPLE
$RowCollection = $Table.Rows.First.Cells
SetWordCellFormat -Collection $RowCollection -Bold -Size 10
This example sets all text to size 8 and bold for all cells that are
contained
within the first row of the table.
Note: the $Table.Rows.First.Cells Returns a collection of Word COM cells
objects
that are in the first table row.
.EXAMPLE
$ColumnCollection = $Table.Columns.Item(2).Cells
SetWordCellFormat -Collection $ColumnCollection -BackgroundColor 255
This example sets the background (shading) of all cells in the table's second
column to red.
Note: the $Table.Columns.Item(2).Cells Returns a collection of Word COM cells
objects
that are in the table's second column.
.EXAMPLE
SetWordCellFormat -Cell $Table.Cell(17,3) -Font "Tahoma" -Color 16711680
This example sets the font to Tahoma and the text color to blue for the cell
located
in the table's 17th row and 3rd column.
Note: the $Table.Cell(17,3) Returns a single Word COM cells object.
#>
Function SetWordCellFormat
{
[CmdletBinding(DefaultParameterSetName='Collection')]
Param (
# Word COM object cell collection reference
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
ParameterSetName='Collection', Position=0)] [ValidateNotNullOrEmpty()] $Collection,
# Word COM object individual cell reference
[Parameter(Mandatory=$true, ParameterSetName='Cell', Position=0)]
[ValidateNotNullOrEmpty()] $Cell,
# Hashtable of cell co-ordinates
[Parameter(Mandatory=$true, ParameterSetName='Hashtable', Position=0)]
[ValidateNotNullOrEmpty()] [System.Collections.Hashtable[]] $Coordinates,
# Word COM object table reference
[Parameter(Mandatory=$true, ParameterSetName='Hashtable', Position=1)]
[ValidateNotNullOrEmpty()] $Table,
# Font name
[Parameter()] [AllowNull()] [string] $Font = $null,
# Font color
[Parameter()] [AllowNull()] $Color = $null,
# Font size
[Parameter()] [ValidateNotNullOrEmpty()] [int] $Size = 0,
# Cell background color
[Parameter()] [AllowNull()] $BackgroundColor = $null,
# Force solid background color
[Switch] $Solid,
[Switch] $Bold,
[Switch] $Italic,
[Switch] $Underline
)
Begin
{
Write-Debug ("Using parameter set '{0}'." -f
$PSCmdlet.ParameterSetName);
}
Process
{
Switch ($PSCmdlet.ParameterSetName)
{
'Collection'
{
ForEach($Cell in $Collection)
{
If($BackgroundColor -ne $null)
{ $Cell.Shading.BackgroundPatternColor = $BackgroundColor; }
If($Bold) { $Cell.Range.Font.Bold = $true; }
If($Italic) { $Cell.Range.Font.Italic = $true; }
If($Underline) { $Cell.Range.Font.Underline = 1; }
If($Font -ne $null) { $Cell.Range.Font.Name =
$Font; }
If($Color -ne $null) { $Cell.Range.Font.Color =
$Color; }
If($Size -ne 0) { $Cell.Range.Font.Size = $Size; }
If($Solid) { $Cell.Shading.Texture = 0; } ##
wdTextureNone
} # end ForEach
} # end Collection
'Cell'
{
If($Bold) { $Cell.Range.Font.Bold = $true; }
If($Italic) { $Cell.Range.Font.Italic = $true; }
If($Underline) { $Cell.Range.Font.Underline = 1; }
If($Font -ne $null) { $Cell.Range.Font.Name = $Font; }
If($Color -ne $null) { $Cell.Range.Font.Color = $Color; }
If($Size -ne 0) { $Cell.Range.Font.Size = $Size; }
If($BackgroundColor -ne $null)
{ $Cell.Shading.BackgroundPatternColor = $BackgroundColor; }
If($Solid) { $Cell.Shading.Texture = 0; } ## wdTextureNone
} # end Cell
'Hashtable'
{
ForEach($Coordinate in $Coordinates)
{
$Cell = $Table.Cell($Coordinate.Row,
$Coordinate.Column);
If($Bold) { $Cell.Range.Font.Bold = $true; }
If($Italic) { $Cell.Range.Font.Italic = $true; }
If($Underline) { $Cell.Range.Font.Underline = 1; }
If($Font -ne $null) { $Cell.Range.Font.Name =
$Font; }
If($Color -ne $null) { $Cell.Range.Font.Color =
$Color; }
If($Size -ne 0) { $Cell.Range.Font.Size = $Size; }
If($BackgroundColor -ne $null)
{ $Cell.Shading.BackgroundPatternColor = $BackgroundColor; }
If($Solid) { $Cell.Shading.Texture = 0; } ##
wdTextureNone
}
} # end Hashtable
} # end Switch
} # end process
}
<#
.Synopsis
Sets alternate row colors in a Word table
.DESCRIPTION
This Function sets the format of alternate rows within a Word table using the
specified $BackgroundColor. This Function is expensive (in performance terms)
as
it recursively sets the format on alternate rows. It would be better to pick
one
of the predefined table formats (if one exists)? Obviously the more rows, the
longer it takes :'(
This example sets every-other table (starting with the first) row and sets
the
background color to red (wdColorRed).
.EXAMPLE
SetWordTableAlternateRowColor -Table $TableReference -BackgroundColor 39423
-Seed Second
This example sets every other table (starting with the second) row and sets
the
background color to light orange (weColorLightOrange).
#>
Function SetWordTableAlternateRowColor
{
[CmdletBinding()]
Param (
# Word COM object table reference
[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
[ValidateNotNullOrEmpty()] $Table,
# Alternate row background color
[Parameter(Mandatory=$true, Position=1)] [ValidateNotNull()] [int]
$BackgroundColor,
# Alternate row starting seed
[Parameter(ValueFromPipelineByPropertyName=$true, Position=2)]
[ValidateSet('First','Second')] [string] $Seed = 'First'
)
Process
{
$StartDateTime = Get-Date;
Write-Debug ("{0}: `t`tSetting alternate table row colors.." -f
$StartDateTime);
## Determine the row seed (only really need to check for 'Second' and
default to 'First' otherwise
If($Seed.ToLower() -eq 'second')
{
$StartRowIndex = 2;
}
Else
{
$StartRowIndex = 1;
}
For($AlternateRowIndex = $StartRowIndex; $AlternateRowIndex -lt
$Table.Rows.Count; $AlternateRowIndex += 2)
{
$Table.Rows.Item($AlternateRowIndex).Shading.BackgroundPatternColor =
$BackgroundColor;
}
## I've put verbose calls in here we can see how expensive this
Functionality actually is.
$EndDateTime = Get-Date;
$ExecutionTime = New-TimeSpan -Start $StartDateTime -End $EndDateTime;
Write-Debug ("{0}: `t`tDone setting alternate row style color in '{1}'
seconds" -f $EndDateTime, $ExecutionTime.TotalSeconds);
}
}
Function ShowScriptOptions
{
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): Add DateTime : $($AddDateTime)"
Write-Verbose "$(Get-Date): All : $($All)"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): Company Name : $($Script:CoName)"
}
Write-Verbose "$(Get-Date): Computers : $($Computers)"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): Company Address : $CompanyAddress"
Write-Verbose "$(Get-Date): Company Email : $CompanyEmail"
Write-Verbose "$(Get-Date): Company Fax : $CompanyFax"
Write-Verbose "$(Get-Date): Company Name : $Script:CoName"
Write-Verbose "$(Get-Date): Company Phone : $CompanyPhone"
Write-Verbose "$(Get-Date): Cover Page : $CoverPage"
}
Write-Verbose "$(Get-Date): Dev : $($Dev)"
If($Dev)
{
Write-Verbose "$(Get-Date): DevErrorFile : $
($Script:DevErrorFile)"
}
Write-Verbose "$(Get-Date): Filename1 : $($Script:FileName1)"
If($PDF)
{
Write-Verbose "$(Get-Date): Filename2 : $($Script:FileName2)"
}
Write-Verbose "$(Get-Date): Folder : $($Folder)"
Write-Verbose "$(Get-Date): From : $($From)"
Write-Verbose "$(Get-Date): Groups : $($Groups)"
Write-Verbose "$(Get-Date): Log : $($Log)"
Write-Verbose "$(Get-Date): Mgmt : $($Mgmt)"
Write-Verbose "$(Get-Date): Organisational Unit: $($OrganisationalUnit)"
Write-Verbose "$(Get-Date): Save As PDF : $($PDF)"
Write-Verbose "$(Get-Date): Save As WORD : $($MSWORD)"
Write-Verbose "$(Get-Date): Script Info : $($ScriptInfo)"
Write-Verbose "$(Get-Date): Sites : $($Sites)"
Write-Verbose "$(Get-Date): Smtp Port : $($SmtpPort)"
Write-Verbose "$(Get-Date): Smtp Server : $($SmtpServer)"
Write-Verbose "$(Get-Date): To : $($To)"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): User Name : $($UserName)"
}
Write-Verbose "$(Get-Date): Users : $($Users)"
Write-Verbose "$(Get-Date): Use SSL : $($UseSSL)"
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): OS Detected : $($Script:RunningOS)"
Write-Verbose "$(Get-Date): PoSH version : $($Host.Version)"
Write-Verbose "$(Get-Date): PSCulture : $($PSCulture)"
Write-Verbose "$(Get-Date): PSUICulture : $($PSUICulture)"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): Word language : $
($Script:WordLanguageValue)"
Write-Verbose "$(Get-Date): Word version : $
($Script:WordProduct)"
}
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): Script start : $($Script:StartTime)"
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): "
}
Function validStateProp
{
Param(
[object] $object,
[string] $topLevel,
[string] $secondLevel
)
Function SetupWord
{
Write-Verbose "$(Get-Date): Setting up Word"
SetWordHashTable $Script:WordCultureCode
[int]$Script:WordVersion = [int]$Script:Word.Version
If($Script:WordVersion -eq $wdWord2016)
{
$Script:WordProduct = "Word 2016"
}
ElseIf($Script:WordVersion -eq $wdWord2013)
{
$Script:WordProduct = "Word 2013"
}
ElseIf($Script:WordVersion -eq $wdWord2010)
{
$Script:WordProduct = "Word 2010"
}
ElseIf($Script:WordVersion -eq $wdWord2007)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tMicrosoft Word 2007 is no longer
supported.`n`n`t`tScript will end.`n`n"
AbortScript
}
Else
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tYou are running an untested or unsupported version
of Microsoft Word.`n`n`t`tScript will end.`n`n`t`tPlease send info on your version
of Word to webster@carlwebster.com`n`n"
AbortScript
}
If([String]::IsNullOrEmpty($TmpName))
{
Write-Warning "`n`n`t`tCompany Name is blank so Cover Page will
not show a Company Name."
Write-Warning "`n`t`tCheck
HKCU:\Software\Microsoft\Office\Common\UserInfo for Company or CompanyName value."
Write-Warning "`n`t`tYou may want to use the -CompanyName
parameter if you need a Company Name on the cover page.`n`n"
}
Else
{
$Script:CoName = $TmpName
Write-Verbose "$(Get-Date): Updated company name to $
($Script:CoName)"
}
}
'da-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Sidelinje"
$CPChanged = $True
}
}
'de-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Randlinie"
$CPChanged = $True
}
}
'es-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Línea lateral"
$CPChanged = $True
}
}
'fi-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Sivussa"
$CPChanged = $True
}
}
'fr-' {
If($CoverPage -eq "Sideline")
{
If($Script:WordVersion -eq $wdWord2013 -or
$Script:WordVersion -eq $wdWord2016)
{
$CoverPage = "Lignes latérales"
$CPChanged = $True
}
Else
{
$CoverPage = "Ligne latérale"
$CPChanged = $True
}
}
}
'nb-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Sidelinje"
$CPChanged = $True
}
}
'nl-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Terzijde"
$CPChanged = $True
}
}
'pt-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Linha Lateral"
$CPChanged = $True
}
}
'sv-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Sidlinje"
$CPChanged = $True
}
}
'zh-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "边线型"
$CPChanged = $True
}
}
}
If($CPChanged)
{
Write-Verbose "$(Get-Date): Changed Default Cover Page from
Sideline to $($CoverPage)"
}
}
If(!$ValidCP)
{
$ErrorActionPreference = $SaveEAPreference
Write-Verbose "$(Get-Date): Word language value $
($Script:WordLanguageValue)"
Write-Verbose "$(Get-Date): Culture code $($Script:WordCultureCode)"
Write-Error "`n`n`t`tFor $($Script:WordProduct), $($CoverPage) is not a
valid Cover Page option.`n`n`t`tScript cannot continue.`n`n"
AbortScript
}
ShowScriptOptions
$Script:Word.Visible = $False
#http://jdhitsolutions.com/blog/2012/05/san-diego-2012-powershell-deep-dive-
slides-and-demos/
#using Jeff's Demo-WordReport.ps1 file for examples
Write-Verbose "$(Get-Date): Load Word Templates"
[bool]$Script:CoverPagesExist = $False
[bool]$BuildingBlocksExist = $False
$Script:Word.Templates.LoadBuildingBlocks()
#word 2010/2013/2016
$BuildingBlocksCollection = $Script:Word.Templates | Where-Object {$_.name
-eq "Built-In Building Blocks.dotx"}
Try
{
$part = $BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}
Catch
{
$part = $Null
}
If(!$Script:CoverPagesExist)
{
Write-Verbose "$(Get-Date): Cover Pages are not installed or the Cover
Page $($CoverPage) does not exist."
Write-Warning "Cover Pages are not installed or the Cover Page $
($CoverPage) does not exist."
Write-Warning "This report will not have a Cover Page."
}
$Script:Selection = $Script:Word.Selection
If($Null -eq $Script:Selection)
{
Write-Verbose "$(Get-Date): "
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tAn unknown error happened selecting the entire
Word document for default formatting options.`n`n`t`tScript cannot continue.`n`n"
AbortScript
}
#set Default tab stops to 1/2 inch (this line is not from Jeff Hicks)
#36 = .50"
$Script:Word.ActiveDocument.DefaultTabStop = 36
#Disable Spell and Grammar Check to resolve issue and improve performance
(from Pat Coughlin)
Write-Verbose "$(Get-Date): Disable grammar and spell checking"
#bug reported 1-Apr-2014 by Tim Mangan
#save current options first before turning them off
$Script:CurrentGrammarOption = $Script:Word.Options.CheckGrammarAsYouType
$Script:CurrentSpellingOption = $Script:Word.Options.CheckSpellingAsYouType
$Script:Word.Options.CheckGrammarAsYouType = $False
$Script:Word.Options.CheckSpellingAsYouType = $False
If($BuildingBlocksExist)
{
#insert new page, getting ready for table of contents
Write-Verbose "$(Get-Date): Insert new page, getting ready for table of
contents"
$part.Insert($Script:Selection.Range,$True) | Out-Null
$Script:Selection.InsertNewPage()
#table of contents
Write-Verbose "$(Get-Date): Table of Contents - $
($Script:MyHash.Word_TableOfContents)"
$toc =
$BuildingBlocks.BuildingBlockEntries.Item($Script:MyHash.Word_TableOfContents)
If($Null -eq $toc)
{
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): Table of Content - $
($Script:MyHash.Word_TableOfContents) could not be retrieved."
Write-Warning "This report will not have a Table of Contents."
}
Else
{
$toc.insert($Script:Selection.Range,$True) | Out-Null
}
}
Else
{
Write-Verbose "$(Get-Date): Table of Contents are not installed."
Write-Warning "Table of Contents are not installed so this report will
not have a Table of Contents."
}
FindWordDocumentEnd
Write-Verbose "$(Get-Date):"
#end of Jeff Hicks
}
Function UpdateDocumentProperties
{
Param([string]$AbstractTitle, [string]$SubjectTitle)
#updated 8-Jun-2017 with additional cover page fields
#Update document properties
If($MSWORD -or $PDF)
{
If($Script:CoverPagesExist)
{
Write-Verbose "$(Get-Date): Set Cover Page Properties"
#8-Jun-2017 put these 4 items in alpha order
Set-DocumentProperty -Document $Script:Doc -DocProperty Author -Value
$UserName
Set-DocumentProperty -Document $Script:Doc -DocProperty Company -Value
$Script:CoName
Set-DocumentProperty -Document $Script:Doc -DocProperty Subject -Value
$SubjectTitle
Set-DocumentProperty -Document $Script:Doc -DocProperty Title -Value
$Script:title
#added 8-Jun-2017
$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename
-eq "CompanyAddress"}
#set the text
[string]$abstract = $CompanyAddress
$ab.Text = $abstract
#added 8-Jun-2017
$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename
-eq "CompanyEmail"}
#set the text
[string]$abstract = $CompanyEmail
$ab.Text = $abstract
#added 8-Jun-2017
$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename
-eq "CompanyFax"}
#set the text
[string]$abstract = $CompanyFax
$ab.Text = $abstract
#added 8-Jun-2017
$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename
-eq "CompanyPhone"}
#set the text
[string]$abstract = $CompanyPhone
$ab.Text = $abstract
Function SaveandCloseDocumentandShutdownWord
{
#bug fix 1-Apr-2014
#reset Grammar and Spelling options back to their original settings
$Script:Word.Options.CheckGrammarAsYouType = $Script:CurrentGrammarOption
$Script:Word.Options.CheckSpellingAsYouType = $Script:CurrentSpellingOption
Stop-WinWord
}
Function SetFileName1andFileName2
{
Param(
[string] $OutputFileName
)
$pwdPath = $Folder
If($pwdPath -eq "")
{
$pwdpath = $pwd.Path
}
If(!$AddDateTime)
{
[string] $Script:FileName1 = ( Join-Path $pwdPath $OutputFileName
) + '.docx'
If($PDF)
{
[string] $Script:FileName2 = ( Join-Path $pwdPath
$OutputFileName ) + '.pdf'
}
}
SetupWord
}
}
$emailAttachment = $Attachments
$emailSubject = $Script:Title
$emailBody = @"
Hello, <br />
<br />
$Script:Title is attached.
"@
If($Dev)
{
Out-File -FilePath $Script:DevErrorFile -InputObject $error 4>$Null
}
$error.Clear()
If($UseSSL)
{
Write-Verbose "$(Get-Date): Trying to send email using current user's
credentials with SSL"
Send-MailMessage -Attachments $emailAttachment -Body $emailBody
-BodyAsHtml -From $From `
-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To
`
-UseSSL *>$Null
}
Else
{
Write-Verbose "$(Get-Date): Trying to send email using current user's
credentials without SSL"
Send-MailMessage -Attachments $emailAttachment -Body $emailBody
-BodyAsHtml -From $From `
-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To
*>$Null
}
$e = $error[0]
If($e.Exception.ToString().Contains("5.7.57"))
{
#The server response was: 5.7.57 SMTP; Client was not authenticated to
send anonymous mail during MAIL FROM
Write-Verbose "$(Get-Date): Current user's credentials failed. Ask for
usable credentials."
If($Dev)
{
Out-File -FilePath $Script:DevErrorFile -InputObject $error
-Append 4>$Null
}
$error.Clear()
If($UseSSL)
{
Send-MailMessage -Attachments $emailAttachment -Body $emailBody
-BodyAsHtml -From $From `
-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject
-To $To `
-UseSSL -credential $emailCredentials *>$Null
}
Else
{
Send-MailMessage -Attachments $emailAttachment -Body $emailBody
-BodyAsHtml -From $From `
-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject
-To $To `
-credential $emailCredentials *>$Null
}
$e = $error[0]
#Script begins
$script:startTime = Get-Date
Function Split-IntoGroups
{
# Written by 'The Masked Avenger with the Cheetos'
[CmdletBinding()]
param (
[parameter(mandatory=$true,position=0,valuefrompipeline=$true)][Object[]]
$InputObject,
[parameter(mandatory=$false,position=1)][ValidateRange(1,
([int]::MaxValue))][int]$Number=10000
)
begin
{
$currentGroup = New-Object System.Collections.ArrayList($Number)
}
process
{
ForEach($object in $InputObject) {
$index = $currentGroup.Add($object)
If($index -ge $Number - 1) {
,$currentGroup.ToArray()
$currentGroup.Clear()
}
}
}
end
{
If($currentGroup.Count -gt 0) {
,$currentGroup.ToArray()
}
}
}
Function Add-CheckListResults
{
[CmdletBinding()]
Param(
[Parameter()]
$Name,
[Parameter()]
$Count
)
$global:someCallers = 0
Function Write-ToCSV
{
[CmdletBinding()]
Param(
[Parameter( Mandatory = $true, Position = 0, ValuefromPipeline = $true
)]
$Content,
$global:someCallers++
If( $null -eq $Content )
{
Write-Debug "***Write-ToCSV: Content is empty, for call count $
($global:someCallers)"
Return
}
## This code makes some assumptions (which were true at the time that
## the code was written):
## 1. All entries in $Content are the same type of PSObject.
## 2. Each entry in $Content is a PSObject.
## 3. $Content contains at least one entry.
## 4. PowerShell version 3 or higher.
## 5. Each PSObject property value is represented in the PSObject
## by a string or integer.
## 6. No property values contain a double quote ('"').
## MBS - 3-May-16
$sample = $null
$count = 0
If( $Content -is [Array] )
{
$sample = $Content[ 0 ]
$count = $Content.Count
}
Else
{
$sample = $Content
$count = 1
}
$output = @()
$headers = ''
$properties = $sample.PSObject.Properties
ForEach( $property in $properties )
{
$headers += '"' + $property.Name + '"' + ','
}
Function Write-ToWord
{
[CmdletBinding()]
Param(
[Parameter( Mandatory = $true, Position = 0)]
$TableContent,
Function ConvertTo-FQDN
{
Param (
[Parameter( Mandatory = $true )]
[string] $DomainFQDN
)
Function Get-Domains
{
( [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() ).Domains
}
Function Get-ADDomains
{
$Domains = Get-Domains
ForEach($Domain in $Domains)
{
$DomainName = $Domain.Name
$DomainFQDN = ConvertTo-FQDN $DomainName
$ADObject = [ADSI]"LDAP://$DomainName"
$sidObject = New-Object
System.Security.Principal.SecurityIdentifier( $ADObject.objectSid[ 0 ], 0 )
Function Get-PrivilegedGroupsMemberCount
{
Param (
[Parameter( Mandatory = $true, ValueFromPipeline = $true )]
$Domains
)
## Jeff W. said this was original code, but until I got ahold of it and
## rewrote it, it looked only slightly changed from:
## https://gallery.technet.microsoft.com/scriptcenter/List-Membership-In-
bff89703
## So I give them both credit. :-)
$Groups = $source.FindAll()
ForEach( $Group in $Groups )
{
$DistinguishedName =
$Group.Properties.Item( 'distinguishedName' )
$groupName = $Group.Properties.Item( 'Name' )
$Source.Filter =
"(memberOf:1.2.840.113556.1.4.1941:=$DistinguishedName)"
$Users = $null
## CHECK: I don't think a try/catch is necessary here - MBS
try
{
$Users = $Source.FindAll()
}
catch
{
# nothing
}
If( $null -eq $users )
{
## Obsolete: F-I-X-M-E: we should probably Return a
PSObject with a count of zero
## Write-ToCSV and Write-ToWord understand empty
Return results.
Write-Debug "***Get-PrivilegedGroupsMemberCount: no
members found in $groupName"
}
Else
{
Function GetProperValue
{
Param(
[Object] $object
)
Return 1
}
Write-Debug "***Get-PrivilegedGroupsMemberCount:
'$groupName' user count before first filter $MemberCount"
Write-Debug "***Get-PrivilegedGroupsMemberCount:
'$groupName' user count after first filter $MemberCount"
Write-Debug "***Get-PrivilegedGroupsMemberCount:
'$groupName' has $MemberCount members"
Function Get-AllADDomainControllers
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, ValueFromPipeline = $true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$adsiSearcher = New-Object
DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$adsiSearcher.Filter = '(&(objectCategory=computer)
(userAccountControl:1.2.840.113556.1.4.803:=8192))'
$Servers = $adsiSearcher.FindAll()
Function Get-AllADMemberServers
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, ValueFromPipeline = $true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$adsiSearcher = New-Object
DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$adsiSearcher.Filter = '(&(objectCategory=computer)(operatingSystem=*server*)
(!(userAccountControl:1.2.840.113556.1.4.803:=8192)))"'
$Servers = $adsiSearcher.FindAll()
Function Get-AllADMemberServerObjects
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Parametersetname =
'PasswordNeverExpires' )]
[Switch]$PasswordNeverExpires,
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$localParamset = $PSCmdlet.ParameterSetName
$source = New-Object
System.DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$source.SearchScope = 'Subtree'
$source.PageSize = 1000
Switch ( $localParamset )
{
'PasswordNeverExpires'
{
$source.Filter = "(&(objectCategory=computer)
(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=8192))
(userAccountControl:1.2.840.113556.1.4.803:=65536))"
}
'PasswordExpiration'
{
$source.Filter = "(&(objectCategory=computer)
(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=8192)))"
}
'AccountNeverExpires'
{
$source.Filter = "(&(objectCategory=computer)
(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=8192))(|
(accountExpires=0)(accountExpires=9223372036854775807)))"
}
'Disabled'
{
#$source.Filter = "(&(objectCategory=computer)
(operatingSystem=*server*)(!(userAccountControl:1.2.840.113556.1.4.803:=8194)))"
$source.Filter = "(&(&(objectCategory=computer)
(objectClass=computer)(operatingSystem=*server*)
(useraccountcontrol:1.2.840.113556.1.4.803:=2)))"
}
}
Write-Debug "***Get-AllADMemberServerObjects,
paramset='$localParamset', found server='$serverName'"
Write-Debug "***Get-AllADMemberServerObjects,
paramset='$localParamset', found server='$serverName'"
Function Get-ADUserObjects
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Parametersetname =
'PasswordNeverExpires')]
[Switch]$PasswordNeverExpires,
[Parameter( Mandatory = $true, Parametersetname =
'PasswordNotRequired')]
[Switch]$PasswordNotRequired,
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$localParamset = $PSCmdlet.ParameterSetName
$source = New-Object
System.Directoryservices.Directorysearcher( "LDAP://$DomainName" )
$source.SearchScope = 'Subtree'
$source.PageSize = 1000
Switch ( $localParamset )
{
'PasswordNeverExpires'
{
$source.filter = "(&(sAMAccountType=805306368)
(userAccountControl:1.2.840.113556.1.4.803:=65536))"
}
'PasswordNotRequired'
{
$source.filter = "(&(sAMAccountType=805306368)
(userAccountControl:1.2.840.113556.1.4.803:=32))"
}
'PasswordChangeAtNextLogon'
{
$source.filter = "(&(sAMAccountType=805306368)(pwdLastSet=0))"
}
'PasswordExpiration'
{
$source.filter = "(&(sAMAccountType=805306368)(pwdLastSet>=0))"
}
'NotRequireKerbereosAuthentication'
{
$source.filter = "(&(sAMAccountType=805306368)
(userAccountControl:1.2.840.113556.1.4.803:=4194304))"
}
'AccountNoExpire'
{
$source.filter = "(&(sAMAccountType=805306368)(|
(accountExpires=0)(accountExpires=9223372036854775807)))"
}
'Disabled'
{
$source.filter = "(&(sAMAccountType=805306368)
(userAccountControl:1.2.840.113556.1.4.803:=2))"
}
}
Write-Debug "***Get-ADUserObjects:
domain='$DomainFQDN', paramset='$localParamset', username='$userName'"
Function Get-OUGPInheritanceBlocked
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$source = New-Object
System.DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$source.SearchScope = 'Subtree'
$source.PageSize = 1000
$source.filter = '(&(objectclass=OrganizationalUnit)(gpoptions=1))'
try
{
$source.FindAll() | ForEach-Object {
$ouName = $_.Properties.Item( 'Name' )
Function Get-ADSites
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$searchRoot = "LDAP://CN=Sites,CN=Configuration,$DomainName"
try
{
$source.FindAll() | ForEach-Object {
$siteName = $_.Properties.Item( 'Name' )
$desc = $_.Properties.Item( 'Description' )
$subnets = @()
$siteBL = $_.Properties.Item( 'siteObjectBL' )
ForEach( $item in $siteBL )
{
$temp = $item.SubString( 0, $item.IndexOf( ',' ) ) ## up
to first ","
$temp = $temp.SubString( 3 ) ## drop
CN=
Write-Debug "***Get-AdSites: sitename='$sitename',
subnet='$temp'"
$subnets += $temp
}
If( $subnets.Count -eq 0 )
{
$subnets = $null
}
Function Get-ADSiteServer
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true)]
$Domain,
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$searchRoot = "LDAP://CN=Servers,CN=$Site,CN=Sites,CN=Configuration,
$DomainName"
try
{
$SiteServers = $source.FindAll()
If( $null -ne $SiteServers )
{
ForEach( $SiteServer in $SiteServers )
{
$serverName = $SiteServer.Properties.Item( 'Name' )
Function Get-ADSiteConnection
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain,
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$searchRoot = "LDAP://CN=$Site,CN=Sites,CN=Configuration,$DomainName"
try
{
$SiteConnections = $source.FindAll()
If( $null -ne $SiteConnections )
{
ForEach( $SiteConnection in $SiteConnections )
{
$connectName = $SiteConnection.Properties.Item( 'Name' )
$connectServer =
$SiteConnection.Properties.Item( 'FromServer' )
Write-Debug "***Get-ADSiteConnection
DomainFQDN='$DomainFQDN', site='$Site', connectionName='$connectName'"
Function Get-ADSiteLink
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$searchRoot = "LDAP://CN=Sites,CN=Configuration,$DomainName"
try
{
$SiteLinks = $source.FindAll()
ForEach( $SiteLink in $SiteLinks )
{
$siteLinkName = $SiteLink.Properties.Item( 'Name' )
$siteLinkDesc = $SiteLink.Properties.Item( 'Description' )
$siteLinkRepl = $SiteLink.Properties.Item( 'replinterval' )
$siteLinkSite = $SiteLink.Properties.Item( 'Sitelist' )
$siteLinkCt = 0
If( $siteLinkSite )
{
$siteLinkCt = $siteLinkSite.Count
}
$sites = @()
ForEach( $item in $siteLinkSite )
{
$temp = $item.SubString( 0, $item.IndexOf( ',' ) )
$temp = $temp.SubString( 3 )
$sites += $temp
}
If( $sites.Count -eq 0 )
{
$sites = $null
$siteLinkCt = 0
}
Function Get-ADSiteSubnet
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$searchRoot = "LDAP://CN=Subnets,CN=Sites,CN=Configuration,$DomainName"
try
{
$source.FindAll() | ForEach-Object {
$subnetSite = ($_.Properties.Item( 'SiteObject' ) -split ','
-replace 'CN=','')[0]
$subnetName = $_.Properties.Item( 'Name' )
$subnetDesc = $_.Properties.Item( 'Description' )
Function Get-ADEmptyGroups
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
## $exclude includes (punny, aren't I?) the list of groups commonly used as a
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$source = New-Object
DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$source.SearchScope = 'Subtree'
$source.PageSize = 1000
$source.Filter = '(&(objectCategory=Group)(!member=*))'
try
{
$groups = $source.FindAll()
$groups = (($groups | Where-Object { $exclude -notcontains
$_.Properties[ 'Name' ].Item( 0 ) } ) | ForEach-Object
{ $_.Properties[ 'Name' ].Item( 0 ) }) | Sort-Object
ForEach( $group in $groups )
{
Write-Debug "***Get-AdEmptyGroups: DomainFQDN='$DomainFQDN',
empty groupname='$group'"
Function Get-ADDomainLocalGroups
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$search = New-Object
System.DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$search.SearchScope = 'Subtree'
$search.PageSize = 1000
$search.Filter = '(&(groupType:1.2.840.113556.1.4.803:=4)(!
(groupType:1.2.840.113556.1.4.803:=1)))'
try
{
$search.FindAll() | ForEach-Object {
$groupName = $_.Properties.Item( 'Name' )
$groupDN = $_.Properties.Item( 'Distinguishedname' )
Function Get-ADUsersInDomainLocalGroups
{
[CmdletBinding()]
Param (
[Parameter( Mandatory = $true, Position = 0, ValueFromPipeline =
$true )]
$Domain
)
$DomainName = $Domain.Name
$DomainFQDN = $Domain.FQDN
$search = New-Object
DirectoryServices.DirectorySearcher( "LDAP://$DomainName" )
$search.SearchScope = 'Subtree'
$search.PageSize = 1000
$search.Filter = '(&(groupType:1.2.840.113556.1.4.803:=4)(!
(groupType:1.2.840.113556.1.4.803:=1)))'
try
{
## $search was being used twice.
$results = $search.FindAll()
$results | ForEach-Object {
$groupName = $_.Properties.Item( 'Name' )
$DistinguishedName = $_.Properties.Item( 'DistinguishedName' )
$search.Filter = "(&(memberOf=$DistinguishedName)
(objectclass=User))"
$search.FindAll() | ForEach-Object {
$userName = $_.Properties.Item( 'Name' )
Write-Debug "***Get-AdUsersInDomainLocalGroups
name='$groupName', user='$userName'"
$GotFile = $False
If($PDF)
{
If(Test-Path "$($Script:FileName2)")
{
Write-Verbose "$(Get-Date): $($Script:FileName2) is ready for
use"
Write-Verbose "$(Get-Date): "
$GotFile = $True
}
Else
{
Write-Warning "$(Get-Date): Unable to save the output file, $
($Script:FileName2)"
Write-Error "Unable to save the output file, $
($Script:FileName2)"
}
}
Else
{
If(Test-Path "$($Script:FileName1)")
{
Write-Verbose "$(Get-Date): $($Script:FileName1) is ready for
use"
Write-Verbose "$(Get-Date): "
$GotFile = $True
}
Else
{
Write-Warning "$(Get-Date): Unable to save the output file, $
($Script:FileName1)"
Write-Error "Unable to save the output file, $
($Script:FileName1)"
}
}
#email output file if requested
If($GotFile -and ![System.String]::IsNullOrEmpty( $SmtpServer ))
{
If($PDF)
{
$emailAttachment = $Script:FileName2
}
Else
{
$emailAttachment = $Script:FileName1
}
SendEmail $emailAttachment
}
If($Dev)
{
If($SmtpServer -eq "")
{
Out-File -FilePath $Script:DevErrorFile -InputObject $error
4>$Null
}
Else
{
Out-File -FilePath $Script:DevErrorFile -InputObject $error
-Append 4>$Null
}
}
If($ScriptInfo)
{
Out-File -FilePath $Script:SIFile -InputObject "" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Add DateTime
: $($AddDateTime)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "All
: $($All)" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $SIFile -Append -InputObject "Company Address
: $CompanyAddress" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Company Email
: $CompanyEmail" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Company Fax
: $CompanyFax" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Company Name
: $Script:CoName" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Company Phone
: $CompanyPhone" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Cover Page
: $CoverPage" 4>$Null
}
Out-File -FilePath $Script:SIFile -Append -InputObject "Computers
: $($computers)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Dev
: $($Dev)" 4>$Null
If($Dev)
{
Out-File -FilePath $Script:SIFile -Append -InputObject
"DevErrorFile : $($Script:DevErrorFile)" 4>$Null
}
Out-File -FilePath $Script:SIFile -Append -InputObject "Filename1
: $($Script:FileName1)" 4>$Null
If($PDF)
{
Out-File -FilePath $Script:SIFile -Append -InputObject "Filename2
: $($Script:FileName2)" 4>$Null
}
Out-File -FilePath $Script:SIFile -Append -InputObject "Folder
: $($Folder)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "From
: $($From)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Groups
: $($groups)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Log
: $($Log)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Mgmt
: $($mgmt)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Organisational
Unit: $($OrganisationalUnit)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Save As PDF
: $($PDF)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Save As WORD
: $($MSWORD)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Script Info
: $($ScriptInfo)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Sites
: $($Sites)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Smtp Port
: $($SmtpPort)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Smtp Server
: $($SmtpServer)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "To
: $($To)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Use SSL
: $($UseSSL)" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $Script:SIFile -Append -InputObject "User Name
: $($UserName)" 4>$Null
}
Out-File -FilePath $Script:SIFile -Append -InputObject "Users
: $($users)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "OS Detected
: $($Script:RunningOS)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "PoSH version
: $($Host.Version)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "PSUICulture
: $($PSUICulture)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "PSCulture
: $($PSCulture)" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $Script:SIFile -Append -InputObject "Word
language : $($Script:WordLanguageValue)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Word
version : $($Script:WordProduct)" 4>$Null
}
Out-File -FilePath $Script:SIFile -Append -InputObject "" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Script start
: $($Script:StartTime)" 4>$Null
Out-File -FilePath $Script:SIFile -Append -InputObject "Elapsed time
: $($Str)" 4>$Null
}
$runtime = $Null
$Str = $Null
$ErrorActionPreference = $SaveEAPreference
}
#endregion
#region Content
$Script:MgmtPage = @()
Function Add-TableContent
{
[CmdletBinding()]
Param(
$content,
$hashParam,
$title
)
$count = 0
If( $null -eq $content )
{
## do not early-Return, because the MgmtPage needs to be updated
Write-Debug "***Add-TableContent: empty for title='$title'"
}
Else
{
$count = 1
If( $content -is [Array] )
{
$count = $content.Count
}
Function IsInDomain
{
$computerSystem = Get-CimInstance Win32_ComputerSystem -ErrorAction
SilentlyContinue -verbose:$False
If( !$? -or $null -eq $computerSystem )
{
$computerSystem = Get-WmiObject Win32_ComputerSystem -ErrorAction
SilentlyContinue
If( !$? -or $null -eq $computerSystem )
{
Write-Error 'IsInDomain: fatal error: cannot obtain
Win32_ComputerSystem from CIM or WMI.'
AbortScript
}
}
Return $computerSystem.PartOfDomain
}
FindWordDocumentEnd
$Script:Selection.InsertNewPage()
Write-Verbose "$(Get-Date): Get domains"
$Domains = Get-ADDomains
If( $null -eq $Domains )
{
Write-Error 'ADHealthCheck cannot obtain a list of domains in the forest.'
AbortScript
}
$parameters = $PSBoundParameters
$paramset = $PSCmdlet.ParameterSetName
#Sites - No subnet
$CheckTitle = 'Sites - Without one or more subnet(s)'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = $TableContentTemp | Where-Object {$_.Subnets -eq
$null}
Add-TableContent $TableContent $PSBoundParameters $CheckTitle
#Sites - No server
$CheckTitle = 'Sites - No server(s)'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = $TableContentTemp | ForEach-Object { Get-
ADSiteServer -Site $_.Site -Domain $Domain } | Where-Object {$_.Name -eq $null}
Add-TableContent $TableContent $PSBoundParameters $CheckTitle
#Sites - No connection
$CheckTitle = 'Sites - Without a connection'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = $TableContentTemp | ForEach-Object { Get-
ADSiteConnection -Site $_.site -Domain $Domain } | Where-Object {$_.Name -eq $null}
WriteWordLine -Style 3 -Tabs 0 -Name $CheckTitle
FindWordDocumentEnd
Add-TableContent $TableContent $PSBoundParameters $CheckTitle
WriteWordLine -Style 0 -Tabs 0 -Name ''
FindWordDocumentEnd
}
#Sites - No sitelink
$CheckTitle = 'Sites - No sitelink(s)'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = $allSiteLinks | Where-Object {$_.'Site Count' -eq '0'}
Add-TableContent $TableContent $PSBoundParameters $CheckTitle
#Sitelinks - No description
$CheckTitle = 'SiteLinks - Without a description'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = $allSiteLinks | Where-Object {$_.Description -eq $null}
Add-TableContent $TableContent $PSBoundParameters $CheckTitle
#Member Servers
Write-Verbose "$(Get-Date): Member Servers"
WriteWordLine -Style 2 -Tabs 0 -Name 'Member Servers'
FindWordDocumentEnd
#Users - Disabled
$CheckTitle = 'Users - Disabled'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = Get-ADUserObjects -Domain $Domain -Disabled | Sort-
Object -Property Name
Add-TableContent $TableContent $parameters $CheckTitle
}
#Groups - Empty
$CheckTitle = 'Groups - Primary - Empty (no members)'
Write-Verbose "$(Get-Date): $CheckTitle"
$TableContent = Get-ADEmptyGroups -Domain $Domain | Sort-Object
-Property Name
Add-TableContent $TableContent $parameters $CheckTitle
}
$CheckTitle = 'Management'
Write-Verbose "$(Get-Date): $CheckTitle"
If($parameters.ContainsKey('Mgmt'))
{
If($parameters.ContainsKey('CSV'))
{
Write-ToCSV -Name $CheckTitle -Content $MgmtPage
}
$Script:Selection.InsertNewPage()
FindWordDocumentEnd
WriteWordLine -Style 2 -Tabs 0 -Name $CheckTitle
FindWordDocumentEnd
Write-ToWord -Name 'Management Table' -TableContent $MgmtPage
}
}
#endregion Content
ProcessScriptEnd
If($parameters.ContainsKey('Log'))
{
If($Script:StartLog -eq $true)
{
try
{
Stop-Transcript | Out-Null
Write-Verbose "$(Get-Date): $Script:LogPath is ready for use"
}
catch
{
Write-Verbose "$(Get-Date): Transcript/log stop failed"
}
}
}