0% found this document useful (0 votes)
387 views319 pages

ADDS Inventory V2

This PowerShell script creates a complete inventory report of an Active Directory forest or domain in Word, PDF, plain text or HTML format. It collects information on users, groups, computers, sites, services, and other AD objects. The report includes a cover page, table of contents and footer. It requires PowerShell 3.0 or higher and can be run remotely with RSAT tools on Windows 7 or higher.

Uploaded by

Sri Kanth
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
387 views319 pages

ADDS Inventory V2

This PowerShell script creates a complete inventory report of an Active Directory forest or domain in Word, PDF, plain text or HTML format. It collects information on users, groups, computers, sites, services, and other AD objects. The report includes a cover page, table of contents and footer. It requires PowerShell 3.0 or higher and can be run remotely with RSAT tools on Windows 7 or higher.

Uploaded by

Sri Kanth
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 319

#Requires -Version 3.

0
#requires -Module ActiveDirectory
#requires -Module GroupPolicy
#This File is in Unicode format. Do not edit in an ASCII editor. Notepad++ UTF-8-
BOM

<#
.SYNOPSIS
Creates a complete inventory of a Microsoft Active Directory Forest.
.DESCRIPTION
Creates a complete inventory of a Microsoft Active Directory Forest using
Microsoft
PowerShell, Word, plain text or HTML.

Creates a Word or PDF document, text or HTML file named after the Active
Directory Forest.

Word and PDF document includes a Cover Page, Table of Contents and Footer.
Includes support for the following language versions of Microsoft Word:
Catalan
Chinese
Danish
Dutch
English
Finnish
French
German
Norwegian
Portuguese
Spanish
Swedish

The script requires at least PowerShell version 3 but runs best in version 5.

Word is NOT needed to run the script. This script will output in Text and
HTML.

You do NOT have to run this script on a domain controller. This script was
developed
and run from a Windows 10 VM.

While most of the script can be run with a non-admin account, there are some
features
that will not or may not work without domain admin or enterprise admin
rights.
The Hardware and Services parameters require domain admin privileges.

Version 2.0 of the script adds gathering information on Time Server and AD
database,
log file, and SYSVOL locations. Those require access to the registry on each
domain
controller, which means the script should now always be run from an elevated
PowerShell
session with an account with a minimum of domain admin rights.

Running the script in a forest with multiple domains requires Enterprise


Admin rights.

The count of all users may not be accurate if the user running the script
doesn't have
the necessary permissions on all user objects. In that case, there may be
user accounts
classified as "unknown".

To run the script from a workstation, RSAT is required.

Remote Server Administration Tools for Windows 7 with Service Pack 1 (SP1)
http://www.microsoft.com/en-us/download/details.aspx?id=7887

Remote Server Administration Tools for Windows 8


http://www.microsoft.com/en-us/download/details.aspx?id=28972

Remote Server Administration Tools for Windows 8.1


http://www.microsoft.com/en-us/download/details.aspx?id=39296

Remote Server Administration Tools for Windows 10


http://www.microsoft.com/en-us/download/details.aspx?id=45520

.PARAMETER CompanyAddress
Company Address to use for the Cover Page, if the Cover Page has the Address
field.

The following Cover Pages have an Address field:


Banded (Word 2013/2016)
Contrast (Word 2010)
Exposure (Word 2010)
Filigree (Word 2013/2016)
Ion (Dark) (Word 2013/2016)
Retrospect (Word 2013/2016)
Semaphore (Word 2013/2016)
Tiles (Word 2010)
ViewMaster (Word 2013/2016)

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.

The following Cover Pages have a Fax field:


Contrast (Word 2010)
Exposure (Word 2010)

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.

The following Cover Pages have a Phone field:


Contrast (Word 2010)
Exposure (Word 2010)

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)

Valid input is:


Alphabet (Word 2010. Works)
Annual (Word 2010. Doesn't work well for this report)
Austere (Word 2010. Works)
Austin (Word 2010/2013/2016. Doesn't work in 2013 or 2016, mostly
works in 2010 but Subtitle/Subject & Author fields need to be moved
after title box is moved up)
Banded (Word 2013/2016. Works)
Conservative (Word 2010. Works)
Contrast (Word 2010. Works)
Cubicles (Word 2010. Works)
Exposure (Word 2010. Works if you like looking sideways)
Facet (Word 2013/2016. Works)
Filigree (Word 2013/2016. Works)
Grid (Word 2010/2013/2016. Works in 2010)
Integral (Word 2013/2016. Works)
Ion (Dark) (Word 2013/2016. Top date doesn't fit; box needs to be
manually resized or font changed to 8 point)
Ion (Light) (Word 2013/2016. Top date doesn't fit; box needs to be
manually resized or font changed to 8 point)
Mod (Word 2010. Works)
Motion (Word 2010/2013/2016. Works if top date is manually changed to
36 point)
Newsprint (Word 2010. Works but date is not populated)
Perspective (Word 2010. Works)
Pinstripes (Word 2010. Works)
Puzzle (Word 2010. Top date doesn't fit; box needs to be manually
resized or font changed to 14 point)
Retrospect (Word 2013/2016. Works)
Semaphore (Word 2013/2016. Works)
Sideline (Word 2010/2013/2016. Doesn't work in 2013 or 2016, works in
2010)
Slice (Dark) (Word 2013/2016. Doesn't work)
Slice (Light) (Word 2013/2016. Doesn't work)
Stacks (Word 2010. Works)
Tiles (Word 2010. Date doesn't fit unless changed to 26 point)
Transcend (Word 2010. Works)
ViewMaster (Word 2013/2016. Works)
Whisp (Word 2013/2016. Works)

The default value is Sideline.


This parameter has an alias of CP.
This parameter is only valid with the MSWORD and PDF output parameters.
.PARAMETER UserName
Username to use for the Cover Page and Footer.
Default value is contained in $env:username
This parameter has an alias of UN.
This parameter is only valid with the MSWORD and PDF output parameters.
.PARAMETER HTML
Creates an HTML file with an .html extension.
This parameter is disabled by default.
.PARAMETER MSWord
SaveAs DOCX file
This parameter is set True if no other output format is selected.
.PARAMETER PDF
SaveAs PDF file instead of DOCX file.
This parameter is disabled by default.
The PDF file is roughly 5X to 10X larger than the DOCX file.
This parameter requires Microsoft Word to be installed.
This parameter uses the Word SaveAs PDF capability.
.PARAMETER Text
Creates a formatted text file with a .txt extension.
This parameter is disabled by default.
.PARAMETER AddDateTime
Adds a date time stamp to the end of the file name.
The timestamp is in the format of yyyy-MM-dd_HHmm.
June 1, 2019 at 6PM is 2019-06-01_1800.
Output filename will be ReportName_2019-06-01_1800.docx (or .pdf).
This parameter is disabled by default.
.PARAMETER ADForest
Specifies an Active Directory forest object by providing one of the following

attribute values.
The identifier in parentheses is the LDAP display name for the attribute.

Fully qualified domain name


Example: labaddomain.com
GUID (objectGUID)
Example: 599c3d2e-e61e-4d20-7b77-030d99495e19
DNS host name
Example: labaddomain.com
NetBIOS name
Example: labaddomain

Default value is $Env:USERDNSDOMAIN

If both ADForest and ADDomain are specified, ADDomain takes precedence.


.PARAMETER ADDomain
Specifies an Active Directory domain object by providing one of the following

property values. The identifier in parentheses is the LDAP display name for
the
attribute. All values are for the domainDNS object that represents the
domain.

Distinguished Name
Example: DC=tullahoma,DC=corp,DC=labaddomain,DC=com

GUID (objectGUID)

Example: b9fa5fbd-4334-4a98-85f1-3a3a44069fc6

Security Identifier (objectSid)

Example: S-1-5-21-3643273344-1505409314-3732760578

DNS domain name

Example: tullahoma.corp.labaddomain.com

NetBIOS domain name

Example: Tullahoma

If both ADForest and ADDomain are specified, ADDomain takes precedence.


.PARAMETER ComputerName
Specifies which domain controller to use to run the script against.
If ADForest is a trusted forest, then ComputerName is required to detect the
existence of ADForest.
ComputerName can be entered as the NetBIOS name, FQDN, localhost or IP
Address.
If entered as localhost, the actual computer name is determined and used.
If entered as an IP address, an attempt is made to determine and use the
actual
computer name.

This parameter has an alias of ServerName.


Default value is $Env:USERDNSDOMAIN
.PARAMETER DCDNSInfo
Use WMI to gather, for each domain controller, the IP Address, and each DNS
server
configured.
This parameter requires the script be run from an elevated PowerShell session

using an account with permission to retrieve hardware information (i.e.


Domain
Admin).
Selecting this parameter will add an extra section to the report.
This parameter is disabled by default.
.PARAMETER Folder
Specifies the optional output folder to save the output report.
.PARAMETER GPOInheritance
In the Group Policies by OU section, adds Inherited GPOs in addition to the
GPOs
directly linked.
Adds a second column to the table GPO Type.
The text file is placed in the same folder from where the script is run.

This parameter is disabled by default.


This parameter has an alias of GPO.
.PARAMETER Hardware
Use WMI to gather hardware information on Computer System, Disks, Processor,
and
Network Interface Cards
This parameter requires the script be run from an elevated PowerShell session

using an account with permission to retrieve hardware information (i.e.


Domain
Admin).
Selecting this parameter will add to both the time it takes to run the script
and
size of the report.
This parameter is disabled by default.
.PARAMETER IncludeUserInfo
For the User Miscellaneous Data section outputs a table with the
SamAccountName
and DistinguishedName of the users in the All Users counts:

Disabled users
Unknown users
Locked out users
All users with password expired
All users whose password never expires
All users with password not required
All users who cannot change password
All users with SID History
All users with Homedrive set in ADUC
All users whose Primary Group is not Domain Users
All users with RDS HomeDrive set in ADUC

The Text output option is limited to the first 25 characters of the


SamAccountName
and the first 116 characters of the DistinguishedName.

This parameter is disabled by default.


This parameter has an alias of IU.
.PARAMETER MaxDetails
Adds maximum detail to the report.

This is the same as using the following parameters:


DCDNSInfo
GPOInheritance
Hardware
IncludeUserInfo
Services

WARNING: Using this parameter can create an extremely large report and
can take a very long time to run.

This parameter has an alias of MAX.


.PARAMETER Services
Gather information on all services running on domain controllers.
Servers that are configured to automatically start but are not running will
be
colored in red.
Used on Domain Controllers only.
This parameter requires the script be run from an elevated PowerShell session
using an account with permission to retrieve service information (i.e. Domain

Admin).
Selecting this parameter will add to both the time it takes to run the script
and
size of the report.
This parameter is disabled by default.
.PARAMETER Section
Processes one or more sections of the report.
Valid options are:
Forest
Sites
Domains (includes Domain Controllers and optional Hardware, Services
and
DCDNSInfo)
OUs (Organizational Units)
Groups
GPOs
Misc (Miscellaneous data)
All

This parameter defaults to All sections.

Multiple sections are separated by a comma. -Section forest, domains

.PARAMETER SmtpServer
Specifies the optional email server to send the output report.
.PARAMETER SmtpPort
Specifies the SMTP port.
The default is 25.
.PARAMETER UseSSL
Specifies whether to use SSL for the SmtpServer.
The default is False.
.PARAMETER From
Specifies the username for the From email address.
If SmtpServer is used, this is a required parameter.
.PARAMETER To
Specifies the username for the To email address.
If SmtpServer is used, this is a required parameter.
.PARAMETER Dev
Clears errors at the beginning of the script.
Outputs all errors to a text file at the end of the script.

This is used when the script developer requests more troubleshooting data.
The text file is placed in the same folder from where the script is run.

This parameter is disabled by default.


.PARAMETER ScriptInfo
Outputs information about the script to a text file.
The text file is placed in the same folder from where the script is run.

This parameter is disabled by default.


This parameter has an alias of SI.
.PARAMETER Log
Generates a log file for troubleshooting.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator
Carl Webster for the Company Name.
Sideline for the Cover Page format.
Administrator for the User Name.

ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest company.tld

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
company.tld for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADDomain child.company.tld

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
child.company.tld for the AD Domain.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest parent.company.tld
-ADDomain child.company.tld

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.

Because both ADForest and ADDomain are specified, ADDomain wins and
child.company.tld
is used for AD Domain.
ADForest is set to the value of ADDomain.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest company.tld -ComputerName
DC01

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
company.tld for the AD Forest
The script will be run remotely on the DC01 domain controller.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -PDF -ADForest corp.carlwebster.com

Will use all default values and save the document as a PDF file.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -Text -ADForest corp.carlwebster.com

Will use all default values and save the document as a formatted text file.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -HTML -ADForest corp.carlwebster.com

Will use all default values and save the document as an HTML file.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -hardware

Will use all default values and add additional information for each domain
controller
about its hardware.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -services

Will use all default values and add additional information for the services
running
on each domain controller.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.

ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -DCDNSInfo

Will use all default values and add additional information for each domain
controller
about its DNS IP configuration.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
An extra section will be added to the end of the report.

ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript .\ADDS_Inventory_V2.ps1 -CompanyName "Carl Webster Consulting"
-CoverPage "Mod" -UserName "Carl Webster" -ComputerName ADDC01

Will use:
Carl Webster Consulting for the Company Name.
Mod for the Cover Page format.
Carl Webster for the User Name.
ADForest defaults to the value of $Env:USERDNSDOMAIN.
Domain Controller named ADDC01 for the ComputerName.
.EXAMPLE
PS C:\PSScript .\ADDS_Inventory_V2.ps1 -CN "Carl Webster Consulting" -CP
"Mod"
-UN "Carl Webster"

Will use:
Carl Webster Consulting for the Company Name (alias CN).
Mod for the Cover Page format (alias CP).
Carl Webster for the User Name (alias UN).

ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript .\ADDS_Inventory_V2.ps1 -CompanyName "Sherlock Holmes
Consulting"
-CoverPage Exposure -UserName "Dr. Watson"
-CompanyAddress "221B Baker Street, London, England"
-CompanyFax "+44 1753 276600"
-CompanyPhone "+44 1753 276200"

Will use:
Sherlock Holmes Consulting for the Company Name.
Exposure for the Cover Page format.
Dr. Watson for the User Name.
221B Baker Street, London, England for the Company Address.
+44 1753 276600 for the Company Fax.
+44 1753 276200 for the Company Phone.

ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript .\ADDS_Inventory_V2.ps1 -CompanyName "Sherlock Holmes
Consulting"
-CoverPage Facet -UserName "Dr. Watson"
-CompanyEmail [email protected]

Will use:
Sherlock Holmes Consulting for the Company Name.
Facet for the Cover Page format.
Dr. Watson for the User Name.
[email protected] for the Company Email.
ADForest defaults to the value of $Env:USERDNSDOMAIN.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest company.tld -AddDateTime

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
company.tld for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.

Adds a date time stamp to the end of the file name.


The timestamp is in the format of yyyy-MM-dd_HHmm.
June 1, 2019 at 6PM is 2019-06-01_1800.
Output filename will be company.tld_2019-06-01_1800.docx.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -PDF -ADForest corp.carlwebster.com
-AddDateTime

Will use all default values and save the document as a PDF file.
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.

Adds a date time stamp to the end of the file name.


The timestamp is in the format of yyyy-MM-dd_HHmm.
June 1, 2019 at 6PM is 2019-06-01_1800.
Output filename will be corp.carlwebster.com_2019-06-01_1800.PDF
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest corp.carlwebster.com
-Folder \\FileServer\ShareName

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.

The output file will be saved in the path \\FileServer\ShareName.


.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest corp.carlwebster.com
-SmtpServer mail.domain.tld
-From [email protected]
-To [email protected]
-ComputerName Server01

Will use all Default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
corp.carlwebster.com for the AD Forest.

The script will be run remotely against server Server01.

The script will use the email server mail.domain.tld, sending from
[email protected],
sending to [email protected].
The script will use the default SMPTP port 25 and will not use SSL.
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 > .\ADDS_Inventory_V2.ps1 -ADForest corp.carlwebster.com
-SmtpServer smtp.office365.com
-SmtpPort 587
-UseSSL
-From [email protected]
-To [email protected]

Will use all Default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator
corp.carlwebster.com for the AD Forest

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.

The script will use the email server smtp.office365.com on port 587 using
SSL, sending
from [email protected], sending to [email protected].
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 > .\ADDS_Inventory_V2.ps1 -Section Forest

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.

ADForest defaults to the value of $Env:USERDNSDOMAIN

ComputerName defaults to the value of $Env:USERDNSDOMAIN, then the script


queries for
a domain controller that is also a global catalog server and will use that as
the
value for ComputerName.

The report will include only the Forest section.


.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -Section groups, misc
-ADForest WebstersLab.com -ServerName PrimaryDC.websterslab.com

Will use all default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl
Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.
WebstersLab.com for ADForest.
PrimaryDC.websterslab.com for ComputerName.

The report will include only the Groups and Miscellaneous sections.
.EXAMPLE
PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -MaxDetails

Will use all Default values.


HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\CompanyName="Carl

Webster" or
HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl
Webster"
$env:username = Administrator

Carl Webster for the Company Name.


Sideline for the Cover Page format.
Administrator for the User Name.

Set the following parameter values:


DCDNSInfo = True
GPOInheritance = True
Hardware = True
IncludeUserInfo = True
Services = True

Section = "All"
.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: ADDS_Inventory_V2.ps1
VERSION: 2.22
AUTHOR: Carl Webster, Sr. Solutions Architect, Choice Solutions, LLC and
Michael B. Smith
LASTEDIT: February 13, 2019
#>

#thanks to @jeffwouters and Michael B. Smith for helping me with these parameters
[CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = "None",
DefaultParameterSetName = "Word") ]

Param(
[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CA")]
[ValidateNotNullOrEmpty()]
[string]$CompanyAddress="",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CE")]
[ValidateNotNullOrEmpty()]
[string]$CompanyEmail="",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CF")]
[ValidateNotNullOrEmpty()]
[string]$CompanyFax="",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CN")]
[ValidateNotNullOrEmpty()]
[string]$CompanyName="",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CPh")]
[ValidateNotNullOrEmpty()]
[string]$CompanyPhone="",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("CP")]
[ValidateNotNullOrEmpty()]
[string]$CoverPage="Sideline",

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Alias("UN")]
[ValidateNotNullOrEmpty()]
[string]$UserName=$env:username,

[parameter(ParameterSetName="HTML",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Switch]$HTML=$False,

[parameter(ParameterSetName="Word",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Switch]$MSWord=$False,

[parameter(ParameterSetName="PDF",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Switch]$PDF=$False,

[parameter(ParameterSetName="Text",Mandatory=$False)]
[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[Switch]$Text=$False,

[parameter(Mandatory=$False)]
[Switch]$AddDateTime=$False,

[parameter(Mandatory=$False)]
[string]$ADForest=$Env:USERDNSDOMAIN,

[parameter(Mandatory=$False)]
[string]$ADDomain="",

[parameter(Mandatory=$False)]
[Alias("ServerName")]
[string]$ComputerName=$Env:USERDNSDOMAIN,

[parameter(Mandatory=$False)]
[Switch]$DCDNSInfo=$False,

[parameter(Mandatory=$False)]
[string]$Folder="",

[parameter(Mandatory=$False)]
[Switch]$GPOInheritance=$False,

[parameter(Mandatory=$False)]
[Switch]$Hardware=$False,

[parameter(Mandatory=$False)]
[Alias("IU")]
[Switch]$IncludeUserInfo=$False,

[parameter(Mandatory=$False)]
[Alias("MAX")]
[Switch]$MaxDetails=$False,

[parameter(Mandatory=$False )]
[Switch]$Services=$False,

[parameter(Mandatory=$False)]
[array]$Section="All",

[parameter(ParameterSetName="SMTP",Mandatory=$True)]
[string]$SmtpServer="",

[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[int]$SmtpPort=25,

[parameter(ParameterSetName="SMTP",Mandatory=$False)]
[switch]$UseSSL=$False,

[parameter(ParameterSetName="SMTP",Mandatory=$True)]
[string]$From="",

[parameter(ParameterSetName="SMTP",Mandatory=$True)]
[string]$To="",

[parameter(Mandatory=$False)]
[Switch]$Dev=$False,
[parameter(Mandatory=$False)]
[Alias("SI")]
[Switch]$ScriptInfo=$False,

[parameter(Mandatory=$False)]
[Switch]$Log=$False

#[email protected]
#Sr. Solutions Architect at Choice Solutions, LLC
#@carlwebster on Twitter
#http://www.CarlWebster.com
#Created on April 10, 2014

#Version 1.0 released to the community on May 31, 2014


#
#Version 2.0 is based on version 1.20
#
#Version 2.22 14-Feb-2019
# Added a line under the OU table stating how many OUs are not protected
# Added color $wdColorYellow
# Added Exchange schema version 17000 for Exchange 2019
# Added to the "Gathering user misc data" section, the following console
message if there are more than 100,000 user accounts in AD:
# There are $($UsersCount) user accounts to process. The following 17
actions will take a long time. Be patient.
# Changed section heading "Domain trusts" to "Domain Trusts" to match the
capitalization of other sections
# Changed several $Var -eq $Null to $Null -eq $Var and on Get-Process line for
WinWord (thanks to MBS)
# Changed test for "No Certification Authority Root(s) were retrieved" by
Michael B. Smith who contributed the original code
# For HTML and Text output, for Heading1 and Heading2 output, added "/// " and
" \\\" surrounding the heading text
# This will help for those of us who read reports that contain > 100,000
OUs and users
# and > 1,000 GPOs
# Removed "Preview" from Windows Server 2019 AD Schema version 88
# Remove unused variables
# Updated help text
#
#Version 2.21 11-Nov-2018
# For HTML output, reverted the output Hardware and Service functions back to
using $rowdata = @()
# Using $rowdata = New-Object System.Collections.ArrayList did not always
work, which is weird
#
#Version 2.20 28-Sep-2018
# Added Domain Functional Level of 7 for Windows Server 2016
# Added Forest Functional Level of 7 for Windows Server 2016
# Added Domain Schema version 88 for Server 2019 Preview
# Added to Domain Information:
# Last logon replication interval
# Public key required password rolling (2012+)
# Added to Forest Information, Domain Controllers:
# Operation System
# Server Core (Y/N)
# Changed "renamed" to "changed" as it was freaking people out thinking I was
renaming their domain or computer
# Changed all but the Word and HTML arrays from "@() +=" to "New-Object
System.Collections.ArrayList .Add()"
# Changed the code where I checked for Singletons and -is [array] to use @()
around the cmdlets so the result
# is always an array. Thanks to fellow CTP Sam Jacobs for the tip. This
reduced the code by almost 500 lines.
# Changed the functions getting the computer WMI hardware and service info to
use
# "New-Object System.Collections.ArrayList .Add()" for Word and HTML
tables
# Changed the width of the Domain Controllers table to accommodate the new
columns
# Change the width of the AD Schema Items table to match the other tables
# Remove all the duplicate $VarName = $Null from Function ProcessDomains
# Reorder Change Log so the most recent is on top and the oldest is at the
bottom
# Reorder most of the domain properties to be in alphabetical order
# Reorder most of the forest properties to be in alphabetical order
# Updated Exchange schema version information
#
#Version 2.19 5-Apr-2018
# Added Event Log information to each domain controller and an appendix
# If the script is run from an elevated PowerShell session by a user with
Domain Admin rights
# Added Operating System information to Functions GetComputerWMIInfo and
OutputComputerItem
# Code clean-up for most recommendations made by Visual Studio Code
#
#Version 2.18 10-Mar-2018
# Added Log switch to create a transcript log
#
#Version 2.17 8-Dec-2017
# Updated Function WriteHTMLLine with fixes from the script template
#
#Version 2.16 4-Dec-2017
# Add checking for users with home drive set in Active Directory Users and
Computers (ADUC)
# Added function OutputHDUserInfo
# Add checking for users with RDS home drive set in ADUC
# Added function from Jeff Hicks Get-RDUserSetting
# Added function OutputRDSHDUserInfo
# Add checking for users whose Primary Group is not Domain Users
# Added function OutputPGUserInfo
# Add "DC: " in fron tof the domain controller name, in text output, for domain
controller information
# Add new parameter ADDomain to restrict report to a single domain in a multi-
domain Forest
# Add schema extension checking for the following items and add to Forest
section:
# 'User-Account-Control', #Flags that control the behavior of a user
account
# 'msNPAllowDialin', #RAS Server
# 'ms-Mcs-AdmPwd', #LAPS
# 'ms-Mcs-AdmPwdExpirationTime', #LAPS
# 'ms-SMS-Assignment-Site-Code', #SCCM
# 'ms-SMS-Capabilities', #SCCM
# 'msRTCSIP-UserRoutingGroupId', #Lync/SfB
# 'msRTCSIP-MirrorBackEndServer' #Lync/SfB
# 'ms-exch-schema-version-pt' #Exchange
# Add "Site: " in front of Site name when listing Subnets, Servers, and
Connection Objects
# Remove several large blocks of code that had been commented out
# Revise how $LinkedGPOs and $InheritedGPOs variables are set to work around
invalid property
# name DisplayName when collection is empty
# Sort Enabled Scopes in AD Optional Features
# Text output changes to tabular data:
# Domain Controllers (in Forest section)
# AD Schema Items (in Forest section)
# Services
# Organizational Units
# Domain Admins
# Enterprise Admins
# Schema Admins
# Users with AdminCount=1
# Updated Exchange schema versions
# Updated help text
# When reporting on the domain controllers in the Forest, if unable to get data
from a domain controller,
# instead of reporting "Unknown", use:
# Unable to retrieve Global Catalog status on <DCName>
# Unable to retrieve Read-only status on <DCName>
# When run for a single domain in a multi-domain forest
# Revise gathering list of domains
# Revise testing for $ComputerName
# Revise variable $ADContext in Function ProcessAllDCsInTheForest
#
#Version 2.15 26-Jun-2017
# Added new parameter MaxDetails:
# This is the same as using the following parameters:
# DCDNSInfo
# GPOInheritance
# HardWare
# IncludeUserInfo
# Services
# Fixed wrong loop variable for CA
# Removed code that made sure all Parameters were set to default values if for
some reason they did exist or values were $Null
# Reordered the parameters in the help text and parameter list so they match
and are grouped better
# Replaced _SetDocumentProperty function with Jim Moyle's Set-DocumentProperty
function
# Updated Function ProcessScriptEnd for the new Cover Page properties and
Parameters
# Updated Function ShowScriptOptions for the new Cover Page properties and
Parameters
# Updated Function UpdateDocumentProperties for the new Cover Page properties
and Parameters
# Updated help text
#
#Version 2.14 12-May-2017
# Add Certificate Authority Information section to Forest Information
# Check for the following CA related errors:
# Possible error: There are more than one Certification Authority
Root(s)
# Error: Certification Authority Root(s) exist, but no
Certification Authority Issuers(s) (also known as Enrollment Agents) exist
# Error: More Certification Authority Root(s) exist than there are
Certification Authority Issuers(s) (also known as Enrollment Agents)
# Error: Certification Authority Issuers(s) (also known as
Enrollment Agents) exist, but no Certification Authority Root(s) exist
# Change "Users with AdminCount=1 ($($AdminsCountStr) members):" to "Users with
AdminCount=1 ($($AdminsCountStr) users):"
# Reorder the Forest Information section
#
#Version 2.13 13-Feb-2017
# Fix French wording for Table of Contents 2
#
#Version 2.12 10-Nov-2016
# Add Chinese language support
# Add table for Time Server information if script is run from an elevated
PowerShell session
# Remove "Appendix A" from DC DNS Info table
#
#Version 2.11 6-Nov-2016
# Fixed Domain Trust Attributes (thanks GT)
# Fixed several Write-Warning statements that had no message
# Fixed using -AddDateTime with -HTML
# Remove duplicate setting for $Script:Title
# Reworked the use of [gc]::Collect()
#
#Version 2.10 released on 19-Oct-2016 (Happy Birthday Linz)
# Add a new parameter, IncludeUserInfo
# Add to the User Miscellaneous Data section, outputs a table with the
SamAccountName
# and DistinguishedName of the users in the All Users counts
# Add to the Domain section, listing Fine Grained Password Policies
# Add to the Forest section, Tombstone Lifetime
# Changed the HTML header for AD Optional Features from a table header to a
section header
# Changed "Site and Services" heading to "Sites and Services"
# Fixed formatting issues with HTML headings output
# The $AdminsCountStr variable was used, when it should not have been used,
# when privileged groups had no members or members could not be retrieved
# Update Forest and Domain schema tables for the released Server 2016 product
#
#Version 2.0 released 26-Sep-2016
#
# Added a parameter, GPOInheritance, to set whether to use the new GPOs by OU
with linked and inherited GPOs
# By default, the script will use the original GPOs by OU with only
directly linked GPOs
# Added a function, ElevatedSession, to test if the PowerShell session running
the script is elevated
# Added a Section parameter to allow specific sections only to be in the report
# Valid options are:
# Forest
# Sites
# Domains (includes Domain Controllers and optional Hardware,
Services and DCDNSInfo)
# OUs (Organizational Units)
# Groups
# GPOs
# Misc (Miscellaneous data)
# All (Default value)
# Added AD Database, logfile and SYSVOL locations along with AD Database size
# Added AD Optional Features
# Added an alias to the ComputerName parameter to ServerName
# Added checking the NIC's "Allow the computer to turn off this device to save
power" setting
# Added requires line for the GroupPolicy module
# Added Text and HTML output
# Added Time Server information
# Change checking for both DA rights and an elevated session for the Time
Server and AD file locations
# If the check fails, added a warning message as write-host with white
foreground
# Change object created for DCDNSINFO to storing blank data for DNS properties
# HTML output would not display a row if any of the DNS values were blank
or Null
# Fix test for domain admin rights for the user account
# Fix text and HTML output for the -Hardware parameter
# Fix the DC DNS Info table to handle two IP Addresses
# Fix the ProcessScriptSetup function
# Add checking for an elevated PowerShell session
# Add checking for DA rights and elevated session if using DCDNSINFO
parameter
# Add checking for elevated session if using the Hardware and Services
parameters
# Change the elevated session warning to write-host with a white
foreground to make it stand out
# Fix where variables were not being set properly
# Fix the user name not being displayed in the warning message about not having
domain admin rights
# If no ComputerName value is entered and $ComputerName –eq $Env:USERDNSDOMAIN
then the script queries for
# a domain controller that is also a global catalog server and will use
that as the value for ComputerName
# Modified GPOs by OU to show if the GPO is Linked or Inherited
# This necessitated a change in the Word/PDF/HTML table format
# Modified GPOs by OU to use the Get-GPInheritance cmdlet to list all directly
linked and inherited GPOs
# Organize script into functions and regions
# Replace Jeremy Saunder's Get-ComputerCountByOS function with his latest
version
# The ADForest parameter is no longer mandatory. It will now default to the
value in $Env:USERDNSDOMAIN
# The ComputerName parameter will also now default to the value in
$Env:USERDNSDOMAIN
# Update forest and domain schema information for the latest updates for
Exchange 2013/2016 and Server 2016 TP4/5
# Update help text
# Update Verbose messages for testing to see if -ComputerName is a domain
controller
# Worked around Get-ADDomainController issue when run from a child domain
#

Set-StrictMode -Version 2

#force on
$PSDefaultParameterValues = @{"*:Verbose"=$True}
$SaveEAPreference = $ErrorActionPreference
$ErrorActionPreference = 'SilentlyContinue'
#V2.18 added
If($Log)
{
#start transcript logging
$Script:ThisScriptPath = Split-Path -Parent
$MyInvocation.MyCommand.Definition
$Script:LogPath = "$Script:ThisScriptPath\ADDSDocScriptTranscript_$(Get-Date
-f yyyy-MM-dd_HHmm).txt"

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($Dev)
{
$Error.Clear()
$Script:DevErrorFile = "$($pwd.Path)\ADInventoryScriptErrors_$(Get-Date -f
yyyy-MM-dd_HHmm).txt"
}

If($Null -eq $MSWord)


{
If($Text -or $HTML -or $PDF)
{
$MSWord = $False
}
Else
{
$MSWord = $True
}
}

If($MSWord -eq $False -and $PDF -eq $False -and $Text -eq $False -and $HTML -eq
$False)
{
$MSWord = $True
}

Write-Verbose "$(Get-Date): Testing output parameters"

If($MSWord)
{
Write-Verbose "$(Get-Date): MSWord is set"
}
ElseIf($PDF)
{
Write-Verbose "$(Get-Date): PDF is set"
}
ElseIf($Text)
{
Write-Verbose "$(Get-Date): Text is set"
}
ElseIf($HTML)
{
Write-Verbose "$(Get-Date): HTML 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"
}
ElseIf($Null -eq $Text)
{
Write-Verbose "$(Get-Date): Text is Null"
}
ElseIf($Null -eq $HTML)
{
Write-Verbose "$(Get-Date): HTML is Null"
}
Else
{
Write-Verbose "$(Get-Date): MSWord is $($MSWord)"
Write-Verbose "$(Get-Date): PDF is $($PDF)"
Write-Verbose "$(Get-Date): Text is $($Text)"
Write-Verbose "$(Get-Date): HTML is $($HTML)"
}
Write-Error "Unable to determine output parameter. Script cannot continue"
Exit
}

If($ADForest -ne "" -and $ADDomain -ne "")


{
#2.16
#Make ADForest equal to ADDomain so no code has to change in the script
$ADForest = $ADDomain
}

#If the MaxDetails parameter is used, set a bunch of stuff true


If($MaxDetails)
{
$DCDNSInfo = $True
$GPOInheritance = $True
$HardWare = $True
$IncludeUserInfo = $True
$Services = $True
$Section = "All"
}

$ValidSection = $False
Switch ($Section)
{
"Forest" {$ValidSection = $True}
"Sites" {$ValidSection = $True}
"Domains" {$ValidSection = $True}
"OUs" {$ValidSection = $True}
"Groups" {$ValidSection = $True}
"GPOs" {$ValidSection = $True}
"Misc" {$ValidSection = $True}
"All" {$ValidSection = $True}
}

If($ValidSection -eq $False)


{
$ErrorActionPreference = $SaveEAPreference
Write-Error -Message "`n`tThe Section parameter specified, $($Section), is an
invalid Section option.`n`tValid options are:

`t`tForest
`t`tSites
`t`tDomains
`t`tOUs
`t`tGroups
`t`tGPOs
`t`tMisc
`t`tAll

`tScript cannot continue."


Exit
}

If($Folder -ne "")


{
Write-Verbose "$(Get-Date): Testing folder path"
#does it exist
If(Test-Path $Folder -EA 0)
{
#it exists, now check to see if it is a folder and not a file
If(Test-Path $Folder -pathType Container -EA 0)
{
#it exists and it is a folder
Write-Verbose "$(Get-Date): Folder path $Folder exists and is a
folder"
}
Else
{
#it exists but it is a file not a folder
Write-Error "Folder $Folder is a file, not a folder. Script
cannot continue"
Exit
}
}
Else
{
#does not exist
Write-Error "Folder $Folder does not exist. Script cannot continue"
Exit
}
}
#region initialize variables for word html and text
[string]$Script:RunningOS = (Get-WmiObject -class Win32_OperatingSystem -EA
0).Caption

If($MSWord -or $PDF)


{
#try and fix the issue with the $CompanyName variable
$Script:CoName = $CompanyName
Write-Verbose "$(Get-Date): CoName is $($Script:CoName)"

#the following values were attained from


#http://msdn.microsoft.com/en-us/library/office/aa211923(v=office.11).aspx
[int]$wdAlignPageNumberRight = 2
[long]$wdColorGray15 = 14277081
[long]$wdColorGray05 = 15987699
[int]$wdMove = 0
[int]$wdSeekMainDocument = 0
[int]$wdSeekPrimaryFooter = 4
[int]$wdStory = 6
[long]$wdColorRed = 255
[int]$wdColorBlack = 0
[long]$wdColorYellow = 65535 #added in ADDS script V2.22
[int]$wdWord2007 = 12
[int]$wdWord2010 = 14
[int]$wdWord2013 = 15
[int]$wdWord2016 = 16
[int]$wdFormatDocumentDefault = 16
[int]$wdFormatPDF = 17
#http://blogs.technet.com/b/heyscriptingguy/archive/2006/03/01/how-can-i-
right-align-a-single-column-in-a-word-table.aspx
#http://msdn.microsoft.com/en-
us/library/office/ff835817%28v=office.15%29.aspx
[int]$wdAlignParagraphLeft = 0
[int]$wdAlignParagraphCenter = 1
[int]$wdAlignParagraphRight = 2
#http://msdn.microsoft.com/en-
us/library/office/ff193345%28v=office.15%29.aspx
[int]$wdCellAlignVerticalTop = 0
[int]$wdCellAlignVerticalCenter = 1
[int]$wdCellAlignVerticalBottom = 2
#http://msdn.microsoft.com/en-
us/library/office/ff844856%28v=office.15%29.aspx
[int]$wdAutoFitFixed = 0
[int]$wdAutoFitContent = 1
[int]$wdAutoFitWindow = 2
#http://msdn.microsoft.com/en-
us/library/office/ff821928%28v=office.15%29.aspx
[int]$wdAdjustNone = 0
[int]$wdAdjustProportional = 1
[int]$wdAdjustFirstColumn = 2
[int]$wdAdjustSameWidth = 3

[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
}

If($HTML)
{
Set-Variable htmlredmask -Option AllScope -Value "#FF0000" 4>$Null
Set-Variable htmlcyanmask -Option AllScope -Value "#00FFFF" 4>$Null
Set-Variable htmlbluemask -Option AllScope -Value "#0000FF" 4>$Null
Set-Variable htmldarkbluemask -Option AllScope -Value "#0000A0" 4>$Null
Set-Variable htmllightbluemask -Option AllScope -Value "#ADD8E6" 4>$Null
Set-Variable htmlpurplemask -Option AllScope -Value "#800080" 4>$Null
Set-Variable htmlyellowmask -Option AllScope -Value "#FFFF00" 4>$Null
Set-Variable htmllimemask -Option AllScope -Value "#00FF00" 4>$Null
Set-Variable htmlmagentamask -Option AllScope -Value "#FF00FF" 4>$Null
Set-Variable htmlwhitemask -Option AllScope -Value "#FFFFFF" 4>$Null
Set-Variable htmlsilvermask -Option AllScope -Value "#C0C0C0" 4>$Null
Set-Variable htmlgraymask -Option AllScope -Value "#808080" 4>$Null
Set-Variable htmlblackmask -Option AllScope -Value "#000000" 4>$Null
Set-Variable htmlorangemask -Option AllScope -Value "#FFA500" 4>$Null
Set-Variable htmlmaroonmask -Option AllScope -Value "#800000" 4>$Null
Set-Variable htmlgreenmask -Option AllScope -Value "#008000" 4>$Null
Set-Variable htmlolivemask -Option AllScope -Value "#808000" 4>$Null

Set-Variable htmlbold -Option AllScope -Value 1 4>$Null


Set-Variable htmlitalics -Option AllScope -Value 2 4>$Null
Set-Variable htmlred -Option AllScope -Value 4 4>$Null
Set-Variable htmlcyan -Option AllScope -Value 8 4>$Null
Set-Variable htmlblue -Option AllScope -Value 16 4>$Null
Set-Variable htmldarkblue -Option AllScope -Value 32 4>$Null
Set-Variable htmllightblue -Option AllScope -Value 64 4>$Null
Set-Variable htmlpurple -Option AllScope -Value 128 4>$Null
Set-Variable htmlyellow -Option AllScope -Value 256 4>$Null
Set-Variable htmllime -Option AllScope -Value 512 4>$Null
Set-Variable htmlmagenta -Option AllScope -Value 1024 4>$Null
Set-Variable htmlwhite -Option AllScope -Value 2048 4>$Null
Set-Variable htmlsilver -Option AllScope -Value 4096 4>$Null
Set-Variable htmlgray -Option AllScope -Value 8192 4>$Null
Set-Variable htmlolive -Option AllScope -Value 16384 4>$Null
Set-Variable htmlorange -Option AllScope -Value 32768 4>$Null
Set-Variable htmlmaroon -Option AllScope -Value 65536 4>$Null
Set-Variable htmlgreen -Option AllScope -Value 131072 4>$Null
Set-Variable htmlblack -Option AllScope -Value 262144 4>$Null
}
If($TEXT)
{
$global:output = ""
}
#endregion

#region email function


Function SendEmail
{
Param([string]$Attachments)
Write-Verbose "$(Get-Date): Prepare to email"

$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()

$emailCredentials = Get-Credential -Message "Enter the email account


and password to send email"

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]

If($? -and $Null -eq $e)


{
Write-Verbose "$(Get-Date): Email successfully sent using new
credentials"
}
Else
{
Write-Verbose "$(Get-Date): Email was not sent:"
Write-Warning "$(Get-Date): Exception: $e.Exception"
}
}
Else
{
Write-Verbose "$(Get-Date): Email was not sent:"
Write-Warning "$(Get-Date): Exception: $e.Exception"
}
}
#endregion

#region code for -hardware switch


Function GetComputerWMIInfo
{
Param([string]$RemoteComputerName)

# original work by Kees Baggerman,


# Senior Technical Consultant @ Inter Access
# [email protected]
# @kbaggerman on Twitter
# http://blog.myvirtualvision.com
# modified 1-May-2014 to work in trusted AD Forests and using different
domain admin credentials
# modified 17-Aug-2016 to fix a few issues with Text and HTML output
# modified 29-Apr-2018 to change from Arrays to New-Object
System.Collections.ArrayList

#Get Computer info


Write-Verbose "$(Get-Date): `t`tProcessing WMI Computer information"
Write-Verbose "$(Get-Date): `t`t`tHardware information"
If($MSWord -or $PDF)
{
WriteWordLine 3 0 "Computer Information: $($RemoteComputerName)"
WriteWordLine 4 0 "General Computer"
}
ElseIf($Text)
{
Line 0 "Computer Information: $($RemoteComputerName)"
Line 1 "General Computer"
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Computer Information: $($RemoteComputerName)"
WriteHTMLLine 4 0 "General Computer"
}

Try
{
$Results = Get-WmiObject -computername $RemoteComputerName
win32_computersystem
}

Catch
{
$Results = $Null
}

If($? -and $Null -ne $Results)


{
$ComputerItems = $Results | Select-Object Manufacturer, Model, Domain,
`
@{N="TotalPhysicalRam"; E={[math]::round(($_.TotalPhysicalMemory /
1GB),0)}}, `
NumberOfProcessors, NumberOfLogicalProcessors
$Results = $Null
[string]$ComputerOS = (Get-WmiObject -class Win32_OperatingSystem
-computername $RemoteComputerName -EA 0).Caption

ForEach($Item in $ComputerItems)
{
OutputComputerItem $Item $ComputerOS
}
}
ElseIf(!$?)
{
Write-Verbose "$(Get-Date): Get-WmiObject win32_computersystem failed
for $($RemoteComputerName)"
Write-Warning "Get-WmiObject win32_computersystem failed for $
($RemoteComputerName)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "Get-WmiObject win32_computersystem failed for
$($RemoteComputerName)" "" $Null 0 $False $True
WriteWordLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteWordLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteWordLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "Get-WmiObject win32_computersystem failed for $
($RemoteComputerName)"
Line 2 "On $($RemoteComputerName) you may need to run winmgmt
/verifyrepository"
Line 2 "and winmgmt /salvagerepository. If this is a trusted
Forest, you may"
Line 2 "need to rerun the script with Domain Admin credentials
from the trusted Forest."
Line 2 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "Get-WmiObject win32_computersystem failed for
$($RemoteComputerName)" "" $Null 0 $False $True
WriteHTMLLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteHTMLLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteHTMLLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for Computer
information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for Computer information"
"" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for Computer information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for Computer information"
"" $Null 0 $False $True
}
}

#Get Disk info


Write-Verbose "$(Get-Date): `t`t`tDrive information"

If($MSWord -or $PDF)


{
WriteWordLine 4 0 "Drive(s)"
}
ElseIf($Text)
{
Line 1 "Drive(s)"
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Drive(s)"
}

Try
{
$Results = Get-WmiObject -computername $RemoteComputerName
Win32_LogicalDisk
}

Catch
{
$Results = $Null
}

If($? -and $Null -ne $Results)


{
$drives = $Results | Select-Object caption, @{N="drivesize";
E={[math]::round(($_.size / 1GB),0)}},
filesystem, @{N="drivefreespace"; E={[math]::round(($_.freespace /
1GB),0)}},
volumename, drivetype, volumedirty, volumeserialnumber
$Results = $Null
ForEach($drive in $drives)
{
If($drive.caption -ne "A:" -and $drive.caption -ne "B:")
{
OutputDriveItem $drive
}
}
}
ElseIf(!$?)
{
Write-Verbose "$(Get-Date): Get-WmiObject Win32_LogicalDisk failed for
$($RemoteComputerName)"
Write-Warning "Get-WmiObject Win32_LogicalDisk failed for $
($RemoteComputerName)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "Get-WmiObject Win32_LogicalDisk failed for $
($RemoteComputerName)" "" $Null 0 $False $True
WriteWordLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteWordLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteWordLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "Get-WmiObject Win32_LogicalDisk failed for $
($RemoteComputerName)"
Line 2 "On $($RemoteComputerName) you may need to run winmgmt
/verifyrepository"
Line 2 "and winmgmt /salvagerepository. If this is a trusted
Forest, you may"
Line 2 "need to rerun the script with Domain Admin credentials
from the trusted Forest."
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "Get-WmiObject Win32_LogicalDisk failed for $
($RemoteComputerName)" "" $Null 0 $False $True
WriteHTMLLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteHTMLLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteHTMLLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for Drive information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for Drive information" ""
$Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for Drive information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for Drive information" ""
$Null 0 $False $True
}
}

#Get CPU's and stepping


Write-Verbose "$(Get-Date): `t`t`tProcessor information"

If($MSWord -or $PDF)


{
WriteWordLine 4 0 "Processor(s)"
}
ElseIf($Text)
{
Line 1 "Processor(s)"
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Processor(s)"
}

Try
{
$Results = Get-WmiObject -computername $RemoteComputerName
win32_Processor
}

Catch
{
$Results = $Null
}

If($? -and $Null -ne $Results)


{
$Processors = $Results | Select-Object availability, name, description,
maxclockspeed,
l2cachesize, l3cachesize, numberofcores, numberoflogicalprocessors
$Results = $Null
ForEach($processor in $processors)
{
OutputProcessorItem $processor
}
}
ElseIf(!$?)
{
Write-Verbose "$(Get-Date): Get-WmiObject win32_Processor failed for $
($RemoteComputerName)"
Write-Warning "Get-WmiObject win32_Processor failed for $
($RemoteComputerName)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "Get-WmiObject win32_Processor failed for $
($RemoteComputerName)" "" $Null 0 $False $True
WriteWordLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteWordLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteWordLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "Get-WmiObject win32_Processor failed for $
($RemoteComputerName)"
Line 2 "On $($RemoteComputerName) you may need to run winmgmt
/verifyrepository"
Line 2 "and winmgmt /salvagerepository. If this is a trusted
Forest, you may"
Line 2 "need to rerun the script with Domain Admin credentials
from the trusted Forest."
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "Get-WmiObject win32_Processor failed for $
($RemoteComputerName)" "" $Null 0 $False $True
WriteHTMLLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteHTMLLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteHTMLLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for Processor
information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for Processor information"
"" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for Processor information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for Processor information"
"" $Null 0 $False $True
}
}

#Get Nics
Write-Verbose "$(Get-Date): `t`t`tNIC information"

If($MSWord -or $PDF)


{
WriteWordLine 4 0 "Network Interface(s)"
}
ElseIf($Text)
{
Line 1 "Network Interface(s)"
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Network Interface(s)"
}

[bool]$GotNics = $True

Try
{
$Results = Get-WmiObject -computername $RemoteComputerName
win32_networkadapterconfiguration
}

Catch
{
$Results = $Null
}

If($? -and $Null -ne $Results)


{
$Nics = $Results | Where-Object {$Null -ne $_.ipaddress}
$Results = $Null

If($Null -eq $Nics)


{
$GotNics = $False
}
Else
{
$GotNics = !($Nics.__PROPERTY_COUNT -eq 0)
}

If($GotNics)
{
ForEach($nic in $nics)
{
Try
{
$ThisNic = Get-WmiObject -computername
$RemoteComputerName win32_networkadapter | Where-Object {$_.index -eq $nic.index}
}

Catch
{
$ThisNic = $Null
}

If($? -and $Null -ne $ThisNic)


{
OutputNicItem $Nic $ThisNic
}
ElseIf(!$?)
{
Write-Warning "$(Get-Date): Error retrieving NIC
information"
Write-Verbose "$(Get-Date): Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)"
Write-Warning "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "Error retrieving NIC
information" "" $Null 0 $False $True
WriteWordLine 0 2 "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)" "" $Null 0
$False $True
WriteWordLine 0 2 "On $($RemoteComputerName)
you may need to run winmgmt /verifyrepository" "" $Null 0 $False $True
WriteWordLine 0 2 "and winmgmt
/salvagerepository. If this is a trusted Forest, you may" "" $Null 0 $False $True
WriteWordLine 0 2 "need to rerun the script
with Domain Admin credentials from the trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "Error retrieving NIC information"
Line 2 "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)"
Line 2 "On $($RemoteComputerName) you may need
to run winmgmt /verifyrepository"
Line 2 "and winmgmt /salvagerepository. If
this is a trusted Forest, you may"
Line 2 "need to rerun the script with Domain
Admin credentials from the trusted Forest."
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "Error retrieving NIC
information" "" $Null 0 $False $True
WriteHTMLLine 0 2 "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)" "" $Null 0
$False $True
WriteHTMLLine 0 2 "On $($RemoteComputerName)
you may need to run winmgmt /verifyrepository" "" $Null 0 $False $True
WriteHTMLLine 0 2 "and winmgmt
/salvagerepository. If this is a trusted Forest, you may" "" $Null 0 $False $True
WriteHTMLLine 0 2 "need to rerun the script
with Domain Admin credentials from the trusted Forest." "" $Null 0 $False $True
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for
NIC information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for NIC
information" "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for NIC
information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for NIC
information" "" $Null 0 $False $True
}
}
}
}
}
ElseIf(!$?)
{
Write-Warning "$(Get-Date): Error retrieving NIC configuration
information"
Write-Verbose "$(Get-Date): Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)"
Write-Warning "Get-WmiObject win32_networkadapterconfiguration failed
for $($RemoteComputerName)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "Error retrieving NIC configuration
information" "" $Null 0 $False $True
WriteWordLine 0 2 "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)" "" $Null 0
$False $True
WriteWordLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteWordLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteWordLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "Error retrieving NIC configuration information"
Line 2 "Get-WmiObject win32_networkadapterconfiguration failed
for $($RemoteComputerName)"
Line 2 "On $($RemoteComputerName) you may need to run winmgmt
/verifyrepository"
Line 2 "and winmgmt /salvagerepository. If this is a trusted
Forest, you may"
Line 2 "need to rerun the script with Domain Admin credentials
from the trusted Forest."
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "Error retrieving NIC configuration
information" "" $Null 0 $False $True
WriteHTMLLine 0 2 "Get-WmiObject
win32_networkadapterconfiguration failed for $($RemoteComputerName)" "" $Null 0
$False $True
WriteHTMLLine 0 2 "On $($RemoteComputerName) you may need to run
winmgmt /verifyrepository" "" $Null 0 $False $True
WriteHTMLLine 0 2 "and winmgmt /salvagerepository. If this is a
trusted Forest, you may" "" $Null 0 $False $True
WriteHTMLLine 0 2 "need to rerun the script with Domain Admin
credentials from the trusted Forest." "" $Null 0 $False $True
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for NIC configuration
information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for NIC configuration
information" "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for NIC configuration information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for NIC configuration
information" "" $Null 0 $False $True
}
}

If($MSWORD -or $PDF)


{
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 ""
}
}

Function OutputComputerItem
{
Param([object]$Item, [string]$OS)
If($MSWord -or $PDF)
{
$ItemInformation = New-Object System.Collections.ArrayList
$ItemInformation.Add(@{ Data = "Manufacturer"; Value =
$Item.manufacturer; }) > $Null
$ItemInformation.Add(@{ Data = "Model"; Value = $Item.model; }) > $Null
$ItemInformation.Add(@{ Data = "Domain"; Value = $Item.domain; }) >
$Null
$ItemInformation.Add(@{ Data = "Operating System"; Value = $OS; }) >
$Null
$ItemInformation.Add(@{ Data = "Total Ram"; Value = "$
($Item.totalphysicalram) GB"; }) > $Null
$ItemInformation.Add(@{ Data = "Physical Processors (sockets)"; Value =
$Item.NumberOfProcessors; }) > $Null
$ItemInformation.Add(@{ Data = "Logical Processors (cores w/HT)"; Value
= $Item.NumberOfLogicalProcessors; }) > $Null
$Table = AddWordTable -Hashtable $ItemInformation `
-Columns Data,Value `
-List `
-AutoFit $wdAutoFitFixed;

## Set first column format


SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

## IB - set column widths without recursion


$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 2 "Manufacturer`t`t`t: " $Item.manufacturer
Line 2 "Model`t`t`t`t: " $Item.model
Line 2 "Domain`t`t`t`t: " $Item.domain
Line 2 "Operating System`t`t: " $OS
Line 2 "Total Ram`t`t`t: $($Item.totalphysicalram) GB"
Line 2 "Physical Processors (sockets)`t: " $Item.NumberOfProcessors
Line 2 "Logical Processors (cores w/HT)`t: "
$Item.NumberOfLogicalProcessors
Line 2 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Manufacturer",($htmlsilver -bor $htmlbold),
$Item.manufacturer,$htmlwhite)
$rowdata += @(,('Model',($htmlsilver -bor $htmlbold),$Item.model,
$htmlwhite))
$rowdata += @(,('Domain',($htmlsilver -bor $htmlbold),$Item.domain,
$htmlwhite))
$rowdata += @(,('Total Ram',($htmlsilver -bor $htmlbold),"$
($Item.totalphysicalram) GB",$htmlwhite))
$rowdata += @(,('Physical Processors (sockets)',($htmlsilver -bor
$htmlbold),$Item.NumberOfProcessors,$htmlwhite))
$rowdata += @(,('Logical Processors (cores w/HT)',($htmlsilver -bor
$htmlbold),$Item.NumberOfLogicalProcessors,$htmlwhite))

$msg = ""
$columnWidths = @("150px","200px")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "350"
WriteHTMLLine 0 0 " "
}
}

Function OutputDriveItem
{
Param([object]$Drive)

$xDriveType = ""
Switch ($drive.drivetype)
{
0 {$xDriveType = "Unknown"; Break}
1 {$xDriveType = "No Root Directory"; Break}
2 {$xDriveType = "Removable Disk"; Break}
3 {$xDriveType = "Local Disk"; Break}
4 {$xDriveType = "Network Drive"; Break}
5 {$xDriveType = "Compact Disc"; Break}
6 {$xDriveType = "RAM Disk"; Break}
Default {$xDriveType = "Unknown"; Break}
}

$xVolumeDirty = ""
If(![String]::IsNullOrEmpty($drive.volumedirty))
{
If($drive.volumedirty)
{
$xVolumeDirty = "Yes"
}
Else
{
$xVolumeDirty = "No"
}
}

If($MSWORD -or $PDF)


{
$DriveInformation = New-Object System.Collections.ArrayList
$DriveInformation.Add(@{ Data = "Caption"; Value = $Drive.caption; }) >
$Null
$DriveInformation.Add(@{ Data = "Size"; Value = "$($drive.drivesize)
GB"; }) > $Null
If(![String]::IsNullOrEmpty($drive.filesystem))
{
$DriveInformation.Add(@{ Data = "File System"; Value =
$Drive.filesystem; }) > $Null
}
$DriveInformation.Add(@{ Data = "Free Space"; Value = "$
($drive.drivefreespace) GB"; }) > $Null
If(![String]::IsNullOrEmpty($drive.volumename))
{
$DriveInformation.Add(@{ Data = "Volume Name"; Value =
$Drive.volumename; }) > $Null
}
If(![String]::IsNullOrEmpty($drive.volumedirty))
{
$DriveInformation.Add(@{ Data = "Volume is Dirty"; Value =
$xVolumeDirty; }) > $Null
}
If(![String]::IsNullOrEmpty($drive.volumeserialnumber))
{
$DriveInformation.Add(@{ Data = "Volume Serial Number"; Value =
$Drive.volumeserialnumber; }) > $Null
}
$DriveInformation.Add(@{ Data = "Drive Type"; Value = $xDriveType; }) >
$Null
$Table = AddWordTable -Hashtable $DriveInformation `
-Columns Data,Value `
-List `
-AutoFit $wdAutoFitFixed;

## Set first column format


SetWordCellFormat -Collection $Table.Columns.Item(1).Cells `
-Bold `
-BackgroundColor $wdColorGray15;

## IB - set column widths without recursion


$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 2 ""
}
ElseIf($Text)
{
Line 2 "Caption`t`t: " $drive.caption
Line 2 "Size`t`t: $($drive.drivesize) GB"
If(![String]::IsNullOrEmpty($drive.filesystem))
{
Line 2 "File System`t: " $drive.filesystem
}
Line 2 "Free Space`t: $($drive.drivefreespace) GB"
If(![String]::IsNullOrEmpty($drive.volumename))
{
Line 2 "Volume Name`t: " $drive.volumename
}
If(![String]::IsNullOrEmpty($drive.volumedirty))
{
Line 2 "Volume is Dirty`t: " $xVolumeDirty
}
If(![String]::IsNullOrEmpty($drive.volumeserialnumber))
{
Line 2 "Volume Serial #`t: " $drive.volumeserialnumber
}
Line 2 "Drive Type`t: " $xDriveType
Line 2 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Caption",($htmlsilver -bor $htmlbold),
$Drive.caption,$htmlwhite)
$rowdata += @(,('Size',($htmlsilver -bor $htmlbold),"$
($drive.drivesize) GB",$htmlwhite))

If(![String]::IsNullOrEmpty($drive.filesystem))
{
$rowdata += @(,('File System',($htmlsilver -bor $htmlbold),
$Drive.filesystem,$htmlwhite))
}
$rowdata += @(,('Free Space',($htmlsilver -bor $htmlbold),"$
($drive.drivefreespace) GB",$htmlwhite))
If(![String]::IsNullOrEmpty($drive.volumename))
{
$rowdata += @(,('Volume Name',($htmlsilver -bor $htmlbold),
$Drive.volumename,$htmlwhite))
}
If(![String]::IsNullOrEmpty($drive.volumedirty))
{
$rowdata += @(,('Volume is Dirty',($htmlsilver -bor $htmlbold),
$xVolumeDirty,$htmlwhite))
}
If(![String]::IsNullOrEmpty($drive.volumeserialnumber))
{
$rowdata += @(,('Volume Serial Number',($htmlsilver -bor
$htmlbold),$Drive.volumeserialnumber,$htmlwhite))
}
$rowdata += @(,('Drive Type',($htmlsilver -bor $htmlbold),$xDriveType,
$htmlwhite))

$msg = ""
$columnWidths = @("150px","200px")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "350"
WriteHTMLLine 0 0 " "
}
}

Function OutputProcessorItem
{
Param([object]$Processor)

$xAvailability = ""
Switch ($processor.availability)
{
1 {$xAvailability = "Other"; Break}
2 {$xAvailability = "Unknown"; Break}
3 {$xAvailability = "Running or Full Power"; Break}
4 {$xAvailability = "Warning"; Break}
5 {$xAvailability = "In Test"; Break}
6 {$xAvailability = "Not Applicable"; Break}
7 {$xAvailability = "Power Off"; Break}
8 {$xAvailability = "Off Line"; Break}
9 {$xAvailability = "Off Duty"; Break}
10 {$xAvailability = "Degraded"; Break}
11 {$xAvailability = "Not Installed"; Break}
12 {$xAvailability = "Install Error"; Break}
13 {$xAvailability = "Power Save - Unknown"; Break}
14 {$xAvailability = "Power Save - Low Power Mode"; Break}
15 {$xAvailability = "Power Save - Standby"; Break}
16 {$xAvailability = "Power Cycle"; Break}
17 {$xAvailability = "Power Save - Warning"; Break}
Default {$xAvailability = "Unknown"; Break}
}

If($MSWORD -or $PDF)


{
$ProcessorInformation = New-Object System.Collections.ArrayList
$ProcessorInformation.Add(@{ Data = "Name"; Value = $Processor.name; })
> $Null
$ProcessorInformation.Add(@{ Data = "Description"; Value =
$Processor.description; }) > $Null
$ProcessorInformation.Add(@{ Data = "Max Clock Speed"; Value = "$
($processor.maxclockspeed) MHz"; }) > $Null
If($processor.l2cachesize -gt 0)
{
$ProcessorInformation.Add(@{ Data = "L2 Cache Size"; Value = "$
($processor.l2cachesize) KB"; }) > $Null
}
If($processor.l3cachesize -gt 0)
{
$ProcessorInformation.Add(@{ Data = "L3 Cache Size"; Value = "$
($processor.l3cachesize) KB"; }) > $Null
}
If($processor.numberofcores -gt 0)
{
$ProcessorInformation.Add(@{ Data = "Number of Cores"; Value =
$Processor.numberofcores; }) > $Null
}
If($processor.numberoflogicalprocessors -gt 0)
{
$ProcessorInformation.Add(@{ Data = "Number of Logical Processors
(cores w/HT)"; Value = $Processor.numberoflogicalprocessors; }) > $Null
}
$ProcessorInformation.Add(@{ Data = "Availability"; Value =
$xAvailability; }) > $Null
$Table = AddWordTable -Hashtable $ProcessorInformation `
-Columns Data,Value `
-List `
-AutoFit $wdAutoFitFixed;

## Set first column format


SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

## IB - set column widths without recursion


$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 2 "Name`t`t`t`t: " $processor.name
Line 2 "Description`t`t`t: " $processor.description
Line 2 "Max Clock Speed`t`t`t: $($processor.maxclockspeed) MHz"
If($processor.l2cachesize -gt 0)
{
Line 2 "L2 Cache Size`t`t`t: $($processor.l2cachesize) KB"
}
If($processor.l3cachesize -gt 0)
{
Line 2 "L3 Cache Size`t`t`t: $($processor.l3cachesize) KB"
}
If($processor.numberofcores -gt 0)
{
Line 2 "# of Cores`t`t`t: " $processor.numberofcores
}
If($processor.numberoflogicalprocessors -gt 0)
{
Line 2 "# of Logical Procs (cores w/HT)`t: "
$processor.numberoflogicalprocessors
}
Line 2 "Availability`t`t`t: " $xAvailability
Line 2 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Name",($htmlsilver -bor $htmlbold),$Processor.name,
$htmlwhite)
$rowdata += @(,('Description',($htmlsilver -bor $htmlbold),
$Processor.description,$htmlwhite))

$rowdata += @(,('Max Clock Speed',($htmlsilver -bor $htmlbold),"$


($processor.maxclockspeed) MHz",$htmlwhite))
If($processor.l2cachesize -gt 0)
{
$rowdata += @(,('L2 Cache Size',($htmlsilver -bor $htmlbold),"$
($processor.l2cachesize) KB",$htmlwhite))
}
If($processor.l3cachesize -gt 0)
{
$rowdata += @(,('L3 Cache Size',($htmlsilver -bor $htmlbold),"$
($processor.l3cachesize) KB",$htmlwhite))
}
If($processor.numberofcores -gt 0)
{
$rowdata += @(,('Number of Cores',($htmlsilver -bor $htmlbold),
$Processor.numberofcores,$htmlwhite))
}
If($processor.numberoflogicalprocessors -gt 0)
{
$rowdata += @(,('Number of Logical Processors (cores w/HT)',
($htmlsilver -bor $htmlbold),$Processor.numberoflogicalprocessors,$htmlwhite))
}
$rowdata += @(,('Availability',($htmlsilver -bor $htmlbold),
$xAvailability,$htmlwhite))

$msg = ""
$columnWidths = @("150px","200px")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "350"
WriteHTMLLine 0 0 " "
}
}

Function OutputNicItem
{
Param([object]$Nic, [object]$ThisNic)

$powerMgmt = Get-WmiObject MSPower_DeviceEnable -Namespace root\wmi | Where-


Object {$_.InstanceName -match [regex]::Escape($ThisNic.PNPDeviceID)}

If($? -and $Null -ne $powerMgmt)


{
If($powerMgmt.Enable -eq $True)
{
$PowerSaving = "Enabled"
}
Else
{
$PowerSaving = "Disabled"
}
}
Else
{
$PowerSaving = "N/A"
}

$xAvailability = ""
Switch ($processor.availability)
{
1 {$xAvailability = "Other"; Break}
2 {$xAvailability = "Unknown"; Break}
3 {$xAvailability = "Running or Full Power"; Break}
4 {$xAvailability = "Warning"; Break}
5 {$xAvailability = "In Test"; Break}
6 {$xAvailability = "Not Applicable"; Break}
7 {$xAvailability = "Power Off"; Break}
8 {$xAvailability = "Off Line"; Break}
9 {$xAvailability = "Off Duty"; Break}
10 {$xAvailability = "Degraded"; Break}
11 {$xAvailability = "Not Installed"; Break}
12 {$xAvailability = "Install Error"; Break}
13 {$xAvailability = "Power Save - Unknown"; Break}
14 {$xAvailability = "Power Save - Low Power Mode"; Break}
15 {$xAvailability = "Power Save - Standby"; Break}
16 {$xAvailability = "Power Cycle"; Break}
17 {$xAvailability = "Power Save - Warning"; Break}
Default {$xAvailability = "Unknown"; Break}
}

$xIPAddress = New-Object System.Collections.ArrayList


ForEach($IPAddress in $Nic.ipaddress)
{
$xIPAddress.Add("$($IPAddress)") > $Null
}

$xIPSubnet = New-Object System.Collections.ArrayList


ForEach($IPSubnet in $Nic.ipsubnet)
{
$xIPSubnet.Add("$($IPSubnet)") > $Null
}

If($Null -ne $nic.dnsdomainsuffixsearchorder -and


$nic.dnsdomainsuffixsearchorder.length -gt 0)
{
$nicdnsdomainsuffixsearchorder = $nic.dnsdomainsuffixsearchorder
$xnicdnsdomainsuffixsearchorder = New-Object
System.Collections.ArrayList
ForEach($DNSDomain in $nicdnsdomainsuffixsearchorder)
{
$xnicdnsdomainsuffixsearchorder.Add("$($DNSDomain)") > $Null
}
}

If($Null -ne $nic.dnsserversearchorder -and $nic.dnsserversearchorder.length


-gt 0)
{
$nicdnsserversearchorder = $nic.dnsserversearchorder
$xnicdnsserversearchorder = New-Object System.Collections.ArrayList
ForEach($DNSServer in $nicdnsserversearchorder)
{
$xnicdnsserversearchorder.Add("$($DNSServer)") > $Null
}
}

$xdnsenabledforwinsresolution = ""
If($nic.dnsenabledforwinsresolution)
{
$xdnsenabledforwinsresolution = "Yes"
}
Else
{
$xdnsenabledforwinsresolution = "No"
}

$xTcpipNetbiosOptions = ""
Switch ($nic.TcpipNetbiosOptions)
{
0 {$xTcpipNetbiosOptions = "Use NetBIOS setting from DHCP Server";
Break}
1 {$xTcpipNetbiosOptions = "Enable NetBIOS"; Break}
2 {$xTcpipNetbiosOptions = "Disable NetBIOS"; Break}
Default {$xTcpipNetbiosOptions = "Unknown"; Break}
}

$xwinsenablelmhostslookup = ""
If($nic.winsenablelmhostslookup)
{
$xwinsenablelmhostslookup = "Yes"
}
Else
{
$xwinsenablelmhostslookup = "No"
}

If($MSWORD -or $PDF)


{
$NicInformation = New-Object System.Collections.ArrayList
$NicInformation.Add(@{ Data = "Name"; Value = $ThisNic.Name; }) > $Null
If($ThisNic.Name -ne $nic.description)
{
$NicInformation.Add(@{ Data = "Description"; Value =
$Nic.description; }) > $Null
}
$NicInformation.Add(@{ Data = "Connection ID"; Value =
$ThisNic.NetConnectionID; }) > $Null
If(validObject $Nic Manufacturer)
{
$NicInformation.Add(@{ Data = "Manufacturer"; Value =
$Nic.manufacturer; }) > $Null
}
$NicInformation.Add(@{ Data = "Availability"; Value =
$xAvailability; }) > $Null
$NicInformation.Add(@{ Data = "Allow the computer to turn off this
device to save power"; Value = $PowerSaving; }) > $Null
$NicInformation.Add(@{ Data = "Physical Address"; Value =
$Nic.macaddress; }) > $Null
If($xIPAddress.Count -gt 1)
{
$NicInformation.Add(@{ Data = "IP Address"; Value =
$xIPAddress[0]; }) > $Null
$NicInformation.Add(@{ Data = "Default Gateway"; Value =
$Nic.Defaultipgateway; }) > $Null
$NicInformation.Add(@{ Data = "Subnet Mask"; Value =
$xIPSubnet[0]; }) > $Null
$cnt = -1
ForEach($tmp in $xIPAddress)
{
$cnt++
If($cnt -gt 0)
{
$NicInformation.Add(@{ Data = "IP Address"; Value =
$tmp; }) > $Null
$NicInformation.Add(@{ Data = "Subnet Mask"; Value =
$xIPSubnet[$cnt]; }) > $Null
}
}
}
Else
{
$NicInformation.Add(@{ Data = "IP Address"; Value =
$xIPAddress; }) > $Null
$NicInformation.Add(@{ Data = "Default Gateway"; Value =
$Nic.Defaultipgateway; }) > $Null
$NicInformation.Add(@{ Data = "Subnet Mask"; Value =
$xIPSubnet; }) > $Null
}
If($nic.dhcpenabled)
{
$DHCPLeaseObtainedDate =
$nic.ConvertToDateTime($nic.dhcpleaseobtained)
$DHCPLeaseExpiresDate =
$nic.ConvertToDateTime($nic.dhcpleaseexpires)
$NicInformation.Add(@{ Data = "DHCP Enabled"; Value =
$Nic.dhcpenabled; }) > $Null
$NicInformation.Add(@{ Data = "DHCP Lease Obtained"; Value =
$dhcpleaseobtaineddate; }) > $Null
$NicInformation.Add(@{ Data = "DHCP Lease Expires"; Value =
$dhcpleaseexpiresdate; }) > $Null
$NicInformation.Add(@{ Data = "DHCP Server"; Value =
$Nic.dhcpserver; }) > $Null
}
If(![String]::IsNullOrEmpty($nic.dnsdomain))
{
$NicInformation.Add(@{ Data = "DNS Domain"; Value =
$Nic.dnsdomain; }) > $Null
}
If($Null -ne $nic.dnsdomainsuffixsearchorder -and
$nic.dnsdomainsuffixsearchorder.length -gt 0)
{
$NicInformation.Add(@{ Data = "DNS Search Suffixes"; Value =
$xnicdnsdomainsuffixsearchorder[0]; }) > $Null
$cnt = -1
ForEach($tmp in $xnicdnsdomainsuffixsearchorder)
{
$cnt++
If($cnt -gt 0)
{
$NicInformation.Add(@{ Data = ""; Value = $tmp; }) >
$Null
}
}
}
$NicInformation.Add(@{ Data = "DNS WINS Enabled"; Value =
$xdnsenabledforwinsresolution; }) > $Null
If($Null -ne $nic.dnsserversearchorder -and
$nic.dnsserversearchorder.length -gt 0)
{
$NicInformation.Add(@{ Data = "DNS Servers"; Value =
$xnicdnsserversearchorder[0]; }) > $Null
$cnt = -1
ForEach($tmp in $xnicdnsserversearchorder)
{
$cnt++
If($cnt -gt 0)
{
$NicInformation.Add(@{ Data = ""; Value = $tmp; }) >
$Null
}
}
}
$NicInformation.Add(@{ Data = "NetBIOS Setting"; Value =
$xTcpipNetbiosOptions; }) > $Null
$NicInformation.Add(@{ Data = "WINS: Enabled LMHosts"; Value =
$xwinsenablelmhostslookup; }) > $Null
If(![String]::IsNullOrEmpty($nic.winshostlookupfile))
{
$NicInformation.Add(@{ Data = "Host Lookup File"; Value =
$Nic.winshostlookupfile; }) > $Null
}
If(![String]::IsNullOrEmpty($nic.winsprimaryserver))
{
$NicInformation.Add(@{ Data = "Primary Server"; Value =
$Nic.winsprimaryserver; }) > $Null
}
If(![String]::IsNullOrEmpty($nic.winssecondaryserver))
{
$NicInformation.Add(@{ Data = "Secondary Server"; Value =
$Nic.winssecondaryserver; }) > $Null
}
If(![String]::IsNullOrEmpty($nic.winsscopeid))
{
$NicInformation.Add(@{ Data = "Scope ID"; Value =
$Nic.winsscopeid; }) > $Null
}
$Table = AddWordTable -Hashtable $NicInformation -Columns Data,Value
-List -AutoFit $wdAutoFitFixed;

## Set first column format


SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

## IB - set column widths without recursion


$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 2 "Name`t`t`t: " $ThisNic.Name
If($ThisNic.Name -ne $nic.description)
{
Line 2 "Description`t`t: " $nic.description
}
Line 2 "Connection ID`t`t: " $ThisNic.NetConnectionID
If(validObject $Nic Manufacturer)
{
Line 2 "Manufacturer`t`t: " $Nic.manufacturer
}
Line 2 "Availability`t`t: " $xAvailability
Line 2 "Allow computer to turn "
Line 2 "off device to save power: " $PowerSaving
Line 2 "Physical Address`t: " $nic.macaddress
Line 2 "IP Address`t`t: " $xIPAddress[0]
$cnt = -1
ForEach($tmp in $xIPAddress)
{
$cnt++
If($cnt -gt 0)
{
Line 5 " " $tmp
}
}
Line 2 "Default Gateway`t`t: " $Nic.Defaultipgateway
Line 2 "Subnet Mask`t`t: " $xIPSubnet[0]
$cnt = -1
ForEach($tmp in $xIPSubnet)
{
$cnt++
If($cnt -gt 0)
{
Line 5 " " $tmp
}
}
If($nic.dhcpenabled)
{
$DHCPLeaseObtainedDate =
$nic.ConvertToDateTime($nic.dhcpleaseobtained)
$DHCPLeaseExpiresDate =
$nic.ConvertToDateTime($nic.dhcpleaseexpires)
Line 2 "DHCP Enabled`t`t: " $nic.dhcpenabled
Line 2 "DHCP Lease Obtained`t: " $dhcpleaseobtaineddate
Line 2 "DHCP Lease Expires`t: " $dhcpleaseexpiresdate
Line 2 "DHCP Server`t`t:" $nic.dhcpserver
}
If(![String]::IsNullOrEmpty($nic.dnsdomain))
{
Line 2 "DNS Domain`t`t: " $nic.dnsdomain
}
If($Null -ne $nic.dnsdomainsuffixsearchorder -and
$nic.dnsdomainsuffixsearchorder.length -gt 0)
{
[int]$x = 1
Line 2 "DNS Search Suffixes`t: "
$xnicdnsdomainsuffixsearchorder[0]
$cnt = -1
ForEach($tmp in $xnicdnsdomainsuffixsearchorder)
{
$cnt++
If($cnt -gt 0)
{
Line 5 " " $tmp
}
}
}
Line 2 "DNS WINS Enabled`t: " $xdnsenabledforwinsresolution
If($Null -ne $nic.dnsserversearchorder -and
$nic.dnsserversearchorder.length -gt 0)
{
[int]$x = 1
Line 2 "DNS Servers`t`t: " $xnicdnsserversearchorder[0]
$cnt = -1
ForEach($tmp in $xnicdnsserversearchorder)
{
$cnt++
If($cnt -gt 0)
{
Line 5 " " $tmp
}
}
}
Line 2 "NetBIOS Setting`t`t: " $xTcpipNetbiosOptions
Line 2 "WINS:"
Line 3 "Enabled LMHosts`t: " $xwinsenablelmhostslookup
If(![String]::IsNullOrEmpty($nic.winshostlookupfile))
{
Line 3 "Host Lookup File`t: " $nic.winshostlookupfile
}
If(![String]::IsNullOrEmpty($nic.winsprimaryserver))
{
Line 3 "Primary Server`t: " $nic.winsprimaryserver
}
If(![String]::IsNullOrEmpty($nic.winssecondaryserver))
{
Line 3 "Secondary Server`t: " $nic.winssecondaryserver
}
If(![String]::IsNullOrEmpty($nic.winsscopeid))
{
Line 3 "Scope ID`t`t: " $nic.winsscopeid
}
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Name",($htmlsilver -bor $htmlbold),$ThisNic.Name,
$htmlwhite)
If($ThisNic.Name -ne $nic.description)
{
$rowdata += @(,('Description',($htmlsilver -bor $htmlbold),
$Nic.description,$htmlwhite))
}
$rowdata += @(,('Connection ID',($htmlsilver -bor $htmlbold),
$ThisNic.NetConnectionID,$htmlwhite))
If(validObject $Nic Manufacturer)
{
$rowdata += @(,('Manufacturer',($htmlsilver -bor $htmlbold),
$Nic.manufacturer,$htmlwhite))
}
$rowdata += @(,('Availability',($htmlsilver -bor $htmlbold),
$xAvailability,$htmlwhite))
$rowdata += @(,('Allow the computer to turn off this device to save
power',($htmlsilver -bor $htmlbold),$PowerSaving,$htmlwhite))
$rowdata += @(,('Physical Address',($htmlsilver -bor $htmlbold),
$Nic.macaddress,$htmlwhite))
$rowdata += @(,('IP Address',($htmlsilver -bor $htmlbold),
$xIPAddress[0],$htmlwhite))
$cnt = -1
ForEach($tmp in $xIPAddress)
{
$cnt++
If($cnt -gt 0)
{
$rowdata += @(,('IP Address',($htmlsilver -bor $htmlbold),
$tmp,$htmlwhite))
}
}
$rowdata += @(,('Default Gateway',($htmlsilver -bor $htmlbold),
$Nic.Defaultipgateway[0],$htmlwhite))
$rowdata += @(,('Subnet Mask',($htmlsilver -bor $htmlbold),
$xIPSubnet[0],$htmlwhite))
$cnt = -1
ForEach($tmp in $xIPSubnet)
{
$cnt++
If($cnt -gt 0)
{
$rowdata += @(,('Subnet Mask',($htmlsilver -bor $htmlbold),
$tmp,$htmlwhite))
}
}
If($nic.dhcpenabled)
{
$DHCPLeaseObtainedDate =
$nic.ConvertToDateTime($nic.dhcpleaseobtained)
$DHCPLeaseExpiresDate =
$nic.ConvertToDateTime($nic.dhcpleaseexpires)
$rowdata += @(,('DHCP Enabled',($htmlsilver -bor $htmlbold),
$Nic.dhcpenabled,$htmlwhite))
$rowdata += @(,('DHCP Lease Obtained',($htmlsilver -bor
$htmlbold),$dhcpleaseobtaineddate,$htmlwhite))
$rowdata += @(,('DHCP Lease Expires',($htmlsilver -bor
$htmlbold),$dhcpleaseexpiresdate,$htmlwhite))
$rowdata += @(,('DHCP Server',($htmlsilver -bor $htmlbold),
$Nic.dhcpserver,$htmlwhite))
}
If(![String]::IsNullOrEmpty($nic.dnsdomain))
{
$rowdata += @(,('DNS Domain',($htmlsilver -bor $htmlbold),
$Nic.dnsdomain,$htmlwhite))
}
If($Null -ne $nic.dnsdomainsuffixsearchorder -and
$nic.dnsdomainsuffixsearchorder.length -gt 0)
{
$rowdata += @(,('DNS Search Suffixes',($htmlsilver -bor
$htmlbold),$xnicdnsdomainsuffixsearchorder[0],$htmlwhite))
$cnt = -1
ForEach($tmp in $xnicdnsdomainsuffixsearchorder)
{
$cnt++
If($cnt -gt 0)
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$rowdata += @(,('DNS WINS Enabled',($htmlsilver -bor $htmlbold),
$xdnsenabledforwinsresolution,$htmlwhite))
If($Null -ne $nic.dnsserversearchorder -and
$nic.dnsserversearchorder.length -gt 0)
{
$rowdata += @(,('DNS Servers',($htmlsilver -bor $htmlbold),
$xnicdnsserversearchorder[0],$htmlwhite))
$cnt = -1
ForEach($tmp in $xnicdnsserversearchorder)
{
$cnt++
If($cnt -gt 0)
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$rowdata += @(,('NetBIOS Setting',($htmlsilver -bor $htmlbold),
$xTcpipNetbiosOptions,$htmlwhite))
$rowdata += @(,('WINS: Enabled LMHosts',($htmlsilver -bor $htmlbold),
$xwinsenablelmhostslookup,$htmlwhite))
If(![String]::IsNullOrEmpty($nic.winshostlookupfile))
{
$rowdata += @(,('Host Lookup File',($htmlsilver -bor $htmlbold),
$Nic.winshostlookupfile,$htmlwhite))
}
If(![String]::IsNullOrEmpty($nic.winsprimaryserver))
{
$rowdata += @(,('Primary Server',($htmlsilver -bor $htmlbold),
$Nic.winsprimaryserver,$htmlwhite))
}
If(![String]::IsNullOrEmpty($nic.winssecondaryserver))
{
$rowdata += @(,('Secondary Server',($htmlsilver -bor $htmlbold),
$Nic.winssecondaryserver,$htmlwhite))
}
If(![String]::IsNullOrEmpty($nic.winsscopeid))
{
$rowdata += @(,('Scope ID',($htmlsilver -bor $htmlbold),
$Nic.winsscopeid,$htmlwhite))
}

$msg = ""
$columnWidths = @("150px","200px")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "350"
WriteHTMLLine 0 0 " "
}
}
#endregion

#region GetComputerServices
Function GetComputerServices
{
Param([string]$RemoteComputerName)
# modified 29-Apr-2018 to change from Arrays to New-Object
System.Collections.ArrayList

#Get Computer services info


Write-Verbose "$(Get-Date): `t`tProcessing Computer services information"
If($MSWORD -or $PDF)
{
WriteWordLine 3 0 "Services"
}
ElseIf($Text)
{
Line 0 "Services"
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Services"
}

Try
{
#Iain Brighton optimization 5-Jun-2014
#Replaced with a single call to retrieve services via WMI. The repeated
## "Get-WMIObject Win32_Service -Filter" calls were the major delays in
the script.
## If we need to retrieve the StartUp type might as well just use WMI.

#V2.20 changed to @()


$Services = @(Get-WMIObject Win32_Service -ComputerName
$RemoteComputerName | Sort-Object DisplayName)
}

Catch
{
$Services = $Null
}

If($? -and $Null -ne $Services)


{
[int]$NumServices = $Services.count
Write-Verbose "$(Get-Date): `t`t$($NumServices) Services found"

If($MSWord -or $PDF)


{
WriteWordLine 0 1 "Services ($NumServices Services found)"

$ServicesWordTable = New-Object System.Collections.ArrayList


## Create an array of hashtables to store references of cells
that we wish to highlight after the table has been added
$HighlightedCells = New-Object System.Collections.ArrayList
## Seed the $Services row index from the second row
[int] $CurrentServiceIndex = 2;
}
ElseIf($Text)
{
Line 0 "Services ($NumServices Services found)"
Line 0 ""

#V2.16 addition
[int]$MaxDisplayNameLength = ($Services.DisplayName | Measure-
Object -Maximum -Property Length).Maximum
If($MaxDisplayNameLength -gt 12) #12 is length of "Display Name"
{
#10 is length of "Display Name" minus 2 to allow for
spacing between columns
Line 1 ("Display Name" + (' ' * ($MaxDisplayNameLength -
10))) -NoNewLine
}
Else
{
Line 1 "Display Name " -NoNewLine
}
Line 1 "Status " -NoNewLine
Line 1 "Startup Type"
}
ElseIf($HTML)
{
WriteHTMLLine 0 1 "Services ($NumServices Services found)"
$rowdata = @()
}
ForEach($Service in $Services)
{
#Write-Verbose "$(Get-Date): `t`t`t Processing service $
($Service.DisplayName)";

If($MSWord -or $PDF)


{

## Add the required key/values to the hashtable


$WordTableRowHash = @{
DisplayName = $Service.DisplayName;
Status = $Service.State;
StartMode = $Service.StartMode
}

## Add the hash to the array


$ServicesWordTable.Add($WordTableRowHash) > $Null

## Store "to highlight" cell references


If($Service.State -like "Stopped" -and $Service.StartMode
-like "Auto")
{
$HighlightedCells.Add(@{ Row = $CurrentServiceIndex;
Column = 2; }) > $Null
}
$CurrentServiceIndex++;
}
ElseIf($Text)
{
#V2.16 change
If(($Service.DisplayName).Length -lt
($MaxDisplayNameLength))
{
[int]$NumOfSpaces = (($MaxDisplayNameLength) -
($Service.DisplayName.Length)) + 2 #+2 to allow for column spacing
$tmp1 = ($($Service.DisplayName) + (' ' *
$NumOfSPaces))
Line 1 $tmp1 -NoNewLine
}
Else
{
Line 1 "$($Service.DisplayName) " -NoNewLine
}
Line 1 "$($Service.State) " -NoNewLine
Line 1 $Service.StartMode
}
ElseIf($HTML)
{
If($Service.State -like "Stopped" -and $Service.StartMode
-like "Auto")
{
$HighlightedCells = $htmlred
}
Else
{
$HighlightedCells = $htmlwhite
}
$rowdata += @(,($Service.DisplayName,$htmlwhite,
$Service.State,$HighlightedCells,
$Service.StartMode,$htmlwhite))
}
}

If($MSWord -or $PDF)


{
## Add the table to the document, using the hashtable (-Alt is
short for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $ServicesWordTable `
-Columns DisplayName, Status, StartMode `
-Headers "Display Name", "Status", "Startup Type" `
-AutoFit $wdAutoFitContent;

## IB - Set the header row format after the


SetWordTableAlternateRowColor function as it will paint the header row!
SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;
## IB - Set the required highlighted cells
SetWordCellFormat -Coordinates $HighlightedCells -Table $Table
-Bold -BackgroundColor $wdColorRed -Solid;

#indent the entire table 1 tab stop


$Table.Rows.SetLeftIndent($Indent1TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
#V2.16 change
Line 0 ""
}
ElseIf($HTML)
{
$columnHeaders = @('Display Name',($htmlsilver -bor
$htmlbold),'Status',($htmlsilver -bor $htmlbold),'Startup Type',($htmlsilver -bor
$htmlbold))
$msg = ""
FormatHTMLTable $msg "auto" -rowArray $rowdata -columnArray
$columnHeaders
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
Write-Warning "No services were retrieved."
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "Warning: No Services were retrieved" "" $Null
0 $False $True
WriteWordLine 0 1 "If this is a trusted Forest, you may need to
rerun the" "" $Null 0 $False $True
WriteWordLine 0 1 "script with Domain Admin credentials from the
trusted Forest." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 "Warning: No Services were retrieved"
Line 1 "If this is a trusted Forest, you may need to rerun the"
Line 1 "script with Domain Admin credentials from the trusted
Forest."
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "Warning: No Services were retrieved" "" $Null
0 $htmlbold
WriteHTMLLine 0 1 "If this is a trusted Forest, you may need to
rerun the" "" $Null 0 $htmlbold
WriteHTMLLine 0 1 "script with Domain Admin credentials from the
trusted Forest." "" $Null 0 $htmlbold
}
}
Else
{
Write-Warning "Services retrieval was successful but no services were
returned."
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "Services retrieval was successful but no
services were returned." "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 "Services retrieval was successful but no services were
returned."
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "Services retrieval was successful but no
services were returned." "" $Null 0 $htmlbold
}
}
}
#endregion

#region BuildDCDNSIPConfigTable
Function BuildDCDNSIPConfigTable
{
Param([string]$RemoteComputerName, [string]$Site)

[bool]$GotNics = $True

Try
{
$Results = Get-WmiObject -computername $RemoteComputerName
win32_networkadapterconfiguration
}

Catch
{
$Results = $Null
}

If($? -and $Null -ne $Results)


{
$Nics = $Results | Where-Object {$Null -ne $_.ipaddress}
$Results = $Null
If($Null -eq $Nics)
{
$GotNics = $False
}
Else
{
$GotNics = !($Nics.__PROPERTY_COUNT -eq 0)
}

If($GotNics)
{
ForEach($nic in $nics)
{
Try
{
$ThisNic = Get-WmiObject -computername
$RemoteComputerName win32_networkadapter | Where-Object {$_.index -eq $nic.index}
}

Catch
{
$ThisNic = $Null
}

If($? -and $Null -ne $ThisNic)


{
Write-Verbose "$(Get-Date): `t`t`tGather DC DNS IP
Config info"
$xIPAddress = @()
ForEach($IPAddress in $Nic.ipaddress)
{
$xIPAddress += "$($IPAddress)"
}

If($Null -ne $nic.dnsserversearchorder -and


$nic.dnsserversearchorder.length -gt 0)
{
$nicdnsserversearchorder =
$nic.dnsserversearchorder
$xnicdnsserversearchorder = @()
ForEach($DNSServer in $nicdnsserversearchorder)
{
$xnicdnsserversearchorder += "$
($DNSServer)"
}
}

$obj = New-Object -TypeName PSObject


$obj | Add-Member -MemberType NoteProperty -Name
DCName -Value $RemoteComputerName
$obj | Add-Member -MemberType NoteProperty -Name
DCSite -Value $Site
If($xIPAddress.Count -gt 1)
{
$obj | Add-Member -MemberType NoteProperty
-Name DCIpAddress1 -Value $xIPAddress[0]
$obj | Add-Member -MemberType NoteProperty
-Name DCIpAddress2 -Value $xIPAddress[1]
}
Else
{
$obj | Add-Member -MemberType NoteProperty
-Name DCIpAddress1 -Value $xIPAddress[0]
$obj | Add-Member -MemberType NoteProperty
-Name DCIpAddress2 -Value ""
}

If($Null -ne $nic.dnsserversearchorder -and


$nic.dnsserversearchorder.length -gt 0)
{
$obj | Add-Member -MemberType NoteProperty
-Name DCDNS1 -Value $xnicdnsserversearchorder[0]
If($Null -ne $xnicdnsserversearchorder[1])
{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS2 -Value $xnicdnsserversearchorder[1]
}
Else
{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS2 -Value " "
}

If($Null -ne $xnicdnsserversearchorder[2])


{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS3 -Value $xnicdnsserversearchorder[2]
}
Else
{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS3 -Value " "
}

If($Null -ne $xnicdnsserversearchorder[3])


{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS4 -Value $xnicdnsserversearchorder[3]
}
Else
{
$obj | Add-Member -MemberType
NoteProperty -Name DCDNS4 -Value " "
}
}

[void]$Script:DCDNSIPInfo.Add($obj)
}
}
}
}
Else
{
Write-Verbose "$(Get-Date): No results Returned for NIC configuration
information"
If($MSWORD -or $PDF)
{
WriteWordLine 0 2 "No results Returned for NIC configuration
information" "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 2 "No results Returned for NIC configuration information"
}
ElseIf($HTML)
{
WriteHTMLLine 0 2 "No results Returned for NIC configuration
information" "" $Null 0 $False $True
}
}
}
#endregion

#region word specific functions


Function SetWordHashTable
{
Param([string]$CultureCode)

#optimized by Michael B. SMith

# DE and FR translations for Word 2010 by Vladimir Radojevic


# [email protected]

# DA translations for Word 2010 by Thomas Daugaard


# Citrix Infrastructure Specialist at edgemo A/S

# CA translations by Javier Sanchez


# CEO & Founder 101 Consulting

#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)

#codes obtained from http://support.microsoft.com/kb/221435


#http://msdn.microsoft.com/en-us/library/bb213877(v=office.12).aspx
$CatalanArray = 1027
$ChineseArray = 2052,3076,5124,4100
$DanishArray = 1030
$DutchArray = 2067, 1043
$EnglishArray = 3081, 10249, 4105, 9225, 6153, 8201, 5129, 13321, 7177,
11273, 2057, 1033, 12297
$FinnishArray = 1035
$FrenchArray = 2060, 1036, 11276, 3084, 12300, 5132, 13324, 6156, 8204,
10252, 7180, 9228, 4108
$GermanArray = 1031, 3079, 5127, 4103, 2055
$NorwegianArray = 1044, 2068
$PortugueseArray = 1046, 2070
$SpanishArray = 1034, 11274, 16394, 13322, 9226, 5130, 7178, 12298, 17418,
4106, 18442, 19466, 6154, 15370, 10250, 20490, 3082, 14346, 8202
$SwedishArray = 1053, 2077

#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",

"Blocs empilés", "Classique", "Contraste",


"Emplacements de bureau",
"Exposition", "Guide", "Ligne latérale", "Moderne",
"Mosaïques", "Mots croisés", "Papier journal",
"Perspective",
"Quadrillage", "Rayures fines", "Transcendant")
}
}

'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")
}
}
}

If($xArray -contains $xCP)


{
$xArray = $Null
Return $True
}
Else
{
$xArray = $Null
Return $False
}
}

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
}

#find out our session (usually "1" except on TS/RDC or Citrix)


$SessionID = (Get-Process -PID $PID).SessionId

#Find out if winword is running in our session


#[bool]$wordrunning = ((Get-Process 'WinWord' -ea 0)|Where-Object
{$_.SessionId -eq $SessionID}) -ne $Null
[bool]$wordrunning = $null –ne ((Get-Process 'WinWord' -ea 0) | Where-Object
{$_.SessionId -eq $SessionID})
If($wordrunning)
{
$ErrorActionPreference = $SaveEAPreference
Write-Host "`n`n`tPlease close all instances of Microsoft Word before
running this report.`n`n"
Exit
}
}

Function ValidateCompanyName
{
[bool]$xResult = Test-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "CompanyName"
If($xResult)
{
Return Get-LocalRegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "CompanyName"
}
Else
{
$xResult = Test-RegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "Company"
If($xResult)
{
Return Get-LocalRegistryValue
"HKCU:\Software\Microsoft\Office\Common\UserInfo" "Company"
}
Else
{
Return ""
}
}
}

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 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
}

Function SetupWord
{
Write-Verbose "$(Get-Date): Setting up Word"

# Setup word for output


Write-Verbose "$(Get-Date): Create Word comObject."
$Script:Word = New-Object -comobject "Word.Application" -EA 0 4>$Null

If(!$? -or $Null -eq $Script:Word)


{
Write-Warning "The Word object could not be created. You may need to
repair your Word installation."
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tThe Word object could not be created. You may
need to repair your Word installation.`n`n`t`tScript cannot continue.`n`n"
Exit
}

Write-Verbose "$(Get-Date): Determine Word language value"


If( ( validStateProp $Script:Word Language Value__ ) )
{
[int]$Script:WordLanguageValue = [int]$Script:Word.Language.Value__
}
Else
{
[int]$Script:WordLanguageValue = [int]$Script:Word.Language
}

If(!($Script:WordLanguageValue -gt -1))


{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tUnable to determine the Word language
value.`n`n`t`tScript cannot continue.`n`n"
AbortScript
}
Write-Verbose "$(Get-Date): Word language value is $
($Script:WordLanguageValue)"

$Script:WordCultureCode = GetCulture $Script:WordLanguageValue

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 [email protected]`n`n"
AbortScript
}

#only validate CompanyName if the field is blank


If([String]::IsNullOrEmpty($Script:CoName))
{
Write-Verbose "$(Get-Date): Company name is blank. Retrieve company
name from registry."
$TmpName = ValidateCompanyName

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)"
}
}

If($Script:WordCultureCode -ne "en-")


{
Write-Verbose "$(Get-Date): Check Default Cover Page for $
($WordCultureCode)"
[bool]$CPChanged = $False
Switch ($Script:WordCultureCode)
{
'ca-' {
If($CoverPage -eq "Sideline")
{
$CoverPage = "Línia lateral"
$CPChanged = $True
}
}
'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)"
}
}

Write-Verbose "$(Get-Date): Validate cover page $($CoverPage) for culture


code $($Script:WordCultureCode)"
[bool]$ValidCP = $False

$ValidCP = ValidateCoverPage $Script:WordVersion $CoverPage


$Script:WordCultureCode

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"}

Write-Verbose "$(Get-Date): Attempt to load cover page $($CoverPage)"


$part = $Null

$BuildingBlocksCollection |
ForEach-Object{
If ($_.BuildingBlockEntries.Item($CoverPage).Name -eq $CoverPage)
{
$BuildingBlocks = $_
}
}

If($Null -ne $BuildingBlocks)


{
$BuildingBlocksExist = $True

Try
{
$part = $BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}

Catch
{
$part = $Null
}

If($Null -ne $part)


{
$Script:CoverPagesExist = $True
}
}

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."
}

Write-Verbose "$(Get-Date): Create empty word doc"


$Script:Doc = $Script:Word.Documents.Add()
If($Null -eq $Script:Doc)
{
Write-Verbose "$(Get-Date): "
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tAn empty Word document could not be
created.`n`n`t`tScript cannot continue.`n`n"
AbortScript
}

$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."
}

#set the footer


Write-Verbose "$(Get-Date): Set the footer"
[string]$footertext = "Report created by $username"

#get the footer


Write-Verbose "$(Get-Date): Get the footer and format font"
$Script:Doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekPrimaryFooter
#get the footer and format font
$footers = $Script:Doc.Sections.Last.Footers
ForEach ($footer in $footers)
{
If($footer.exists)
{
$footer.range.Font.name = "Calibri"
$footer.range.Font.size = 8
$footer.range.Font.Italic = $True
$footer.range.Font.Bold = $True
}
} #end ForEach
Write-Verbose "$(Get-Date): Footer text"
$Script:Selection.HeaderFooter.Range.Text = $footerText

#add page numbering


Write-Verbose "$(Get-Date): Add page numbering"
$Script:Selection.HeaderFooter.PageNumbers.Add($wdAlignPageNumberRight) |
Out-Null

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

#Get the Coverpage XML part


$cp = $Script:Doc.CustomXMLParts | Where-Object {$_.NamespaceURI
-match "coverPageProps$"}
#get the abstract XML part
$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename
-eq "Abstract"}
#set the text
If([String]::IsNullOrEmpty($Script:CoName))
{
[string]$abstract = $AbstractTitle
}
Else
{
[string]$abstract = "$($AbstractTitle) for $
($Script:CoName)"
}
$ab.Text = $abstract

#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

$ab = $cp.documentelement.ChildNodes | Where-Object {$_.basename


-eq "PublishDate"}
#set the text
[string]$abstract = (Get-Date -Format d).ToString()
$ab.Text = $abstract

Write-Verbose "$(Get-Date): Update the Table of Contents"


#update the Table of Contents
$Script:Doc.TablesOfContents.item(1).Update()
$cp = $Null
$ab = $Null
$abstract = $Null
}
}
}
#endregion

#region registry functions


#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)
}

# Gets the specified local registry value or $Null if it is missing


Function Get-LocalRegistryValue($path, $name)
{
$key = Get-Item -LiteralPath $path -EA 0
If($key)
{
$key.GetValue($name, $Null)
}
Else
{
$Null
}
}

Function Get-RegistryValue
{
# Gets the specified registry value or $Null if it is missing
[CmdletBinding()]
Param([string]$path, [string]$name, [string]$ComputerName)
If($ComputerName -eq $env:computername -or $ComputerName -eq "LocalHost")
{
$key = Get-Item -LiteralPath $path -EA 0
If($key)
{
Return $key.GetValue($name, $Null)
}
Else
{
Return $Null
}
}
Else
{
#path needed here is different for remote registry access
$path1 = $path.SubString(6)
$path2 = $path1.Replace('\','\\')
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',
$ComputerName)
$RegKey= $Reg.OpenSubKey($path2)
$Results = $RegKey.GetValue($name)
If($Null -ne $Results)
{
Return $Results
}
Else
{
Return $Null
}
}
}
#endregion

#region word, text and html line output functions


Function line
#function created by Michael B. Smith, Exchange MVP
#@essentialexchange on Twitter
#http://TheEssentialExchange.com
#for creating the formatted text report
#created March 2011
#updated March 2014
{
Param( [int]$tabs = 0, [string]$name = '', [string]$value = '', [string]
$newline = "`r`n", [switch]$nonewline )
While( $tabs -gt 0 ) { $Global:Output += "`t"; $tabs--; }
If( $nonewline )
{
$Global:Output += $name + $value
}
Else
{
$Global:Output += $name + $value + $newline
}
}

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
{
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 output style


[string]$output = ""
Switch ($style)
{
0 {$Script:Selection.Style = $Script:MyHash.Word_NoSpacing; Break}
1 {$Script:Selection.Style = $Script:MyHash.Word_Heading1; Break}
2 {$Script:Selection.Style = $Script:MyHash.Word_Heading2; Break}
3 {$Script:Selection.Style = $Script:MyHash.Word_Heading3; Break}
4 {$Script:Selection.Style = $Script:MyHash.Word_Heading4; Break}
Default {$Script:Selection.Style = $Script:MyHash.Word_NoSpacing;
Break}
}

#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
}

If($italics -eq $True)


{
$Script:Selection.Font.Italic = $True
}

If($boldface -eq $True)


{
$Script:Selection.Font.Bold = $True
}

#output the rest of the parameters.


$output += $name + $value
$Script:Selection.TypeText($output)

#test for new WriteWordLine 0.


If($nonewline)
{
# Do nothing.
}
Else
{
$Script:Selection.TypeParagraph()
}
}

#**********************************************************************************
*************************
# WriteHTMLLine
#**********************************************************************************
*************************

<#
.Synopsis
Writes a line of output for HTML output
.DESCRIPTION
This function formats an HTML line
.USAGE
WriteHTMLLine <Style> <Tabs> <Name> <Value> <Font Name> <Font Size> <Options>

0 for Font Size denotes using the default font size of 2 or 10 point

.EXAMPLE
WriteHTMLLine 0 0 " "

Writes a blank line with no style or tab stops, obviously none needed.

.EXAMPLE
WriteHTMLLine 0 1 "This is a regular line of text indented 1 tab stops"

Writes a line with 1 tab stop.

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of text in the default font in
italics" "" $Null 0 $htmlitalics

Writes a line omitting font and font size and setting the italics attribute

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of text in the default font in
bold" "" $Null 0 $htmlbold

Writes a line omitting font and font size and setting the bold attribute

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of text in the default font in bold
italics" "" $Null 0 ($htmlbold -bor $htmlitalics)

Writes a line omitting font and font size and setting both italics and bold
options

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of text in the default font in 10
point" "" $Null 2 # 10 point font

Writes a line using 10 point font

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of text in Courier New font" ""
"Courier New" 0

Writes a line using Courier New Font and 0 font point size (default = 2 if
set to 0)

.EXAMPLE
WriteHTMLLine 0 0 "This is a regular line of RED text indented 0 tab stops
with the computer name as data in 10 point Courier New bold italics: "
$env:computername "Courier New" 2 ($htmlbold -bor $htmlred -bor $htmlitalics)

Writes a line using Courier New Font with first and second string values to
be used, also uses 10 point font with bold, italics and red color options set.

.NOTES

Font Size - Unlike word, there is a limited set of font sizes that can be
used in HTML. They are:
0 - default which actually gives it a 2 or 10 point.
1 - 7.5 point font size
2 - 10 point
3 - 13.5 point
4 - 15 point
5 - 18 point
6 - 24 point
7 - 36 point
Any number larger than 7 defaults to 7

Style - Refers to the headers that are used with output and resemble the
headers in word,
HTML supports headers h1-h6 and h1-h4 are more commonly used. Unlike word,
H1 will not
give you a blue colored font, you will have to set that yourself.

Colors and Bold/Italics Flags are:

htmlbold
htmlitalics
htmlred
htmlcyan
htmlblue
htmldarkblue
htmllightblue
htmlpurple
htmlyellow
htmllime
htmlmagenta
htmlwhite
htmlsilver
htmlgray
htmlolive
htmlorange
htmlmaroon
htmlgreen
htmlblack
#>

Function WriteHTMLLine
#Function created by Ken Avram
#Function created to make output to HTML easy in this script
#headings fixed 12-Oct-2016 by Webster
#errors with $HTMLStyle fixed 7-Dec-2017 by Webster
{
Param([int]$style=0,
[int]$tabs = 0,
[string]$name = '',
[string]$value = '',
[string]$fontName="Calibri",
[int]$fontSize=1,
[int]$options=$htmlblack)

#Build output style


[string]$output = ""

If([String]::IsNullOrEmpty($Name))
{
$HTMLBody = "<p></p>"
}
Else
{
$color = CheckHTMLColor $options

#build # of tabs

While($tabs -gt 0)
{
$output += "&nbsp;&nbsp;&nbsp;&nbsp;"; $tabs--;
}

$HTMLFontName = $fontName

$HTMLBody = ""

If($options -band $htmlitalics)


{
$HTMLBody += "<i>"
}

If($options -band $htmlbold)


{
$HTMLBody += "<b>"
}

#output the rest of the parameters.


$output += $name + $value

Switch ($style)
{
1 {$HTMLStyle = "<h1>"; Break}
2 {$HTMLStyle = "<h2>"; Break}
3 {$HTMLStyle = "<h3>"; Break}
4 {$HTMLStyle = "<h4>"; Break}
Default {$HTMLStyle = ""; Break}
}

$HTMLBody += $HTMLStyle + $output

Switch ($style)
{
1 {$HTMLStyle = "</h1>"; Break}
2 {$HTMLStyle = "</h2>"; Break}
3 {$HTMLStyle = "</h3>"; Break}
4 {$HTMLStyle = "</h4>"; Break}
Default {$HTMLStyle = ""; Break}
}

#added by webster 12-oct-2016


#if a heading, don't add the <br>
#moved to after the two switch statements on 7-Dec-2017 to fix
$HTMLStyle has not been set error
If($HTMLStyle -eq "")
{
$HTMLBody += "<br><font face='" + $HTMLFontName + "' " +
"color='" + $color + "' size='" + $fontsize + "'>"
}
Else
{
$HTMLBody += "<font face='" + $HTMLFontName + "' " + "color='" +
$color + "' size='" + $fontsize + "'>"
}

$HTMLBody += $HTMLStyle + "</font>"

If($options -band $htmlitalics)


{
$HTMLBody += "</i>"
}

If($options -band $htmlbold)


{
$HTMLBody += "</b>"
}

#added by webster 12-oct-2016


#if a heading, don't add the <br />
#moved to inside the Else statement on 7-Dec-2017 to fix $HTMLStyle has
not been set error
If($HTMLStyle -eq "")
{
$HTMLBody += "<br />"
}
}

out-file -FilePath $Script:FileName1 -Append -InputObject $HTMLBody 4>$Null


}
#endregion

#region HTML table functions


#**********************************************************************************
*************************
# AddHTMLTable - Called from FormatHTMLTable function
# Created by Ken Avram
# modified by Jake Rutski
#**********************************************************************************
*************************
Function AddHTMLTable
{
Param([string]$fontName="Calibri",
[int]$fontSize=2,
[int]$colCount=0,
[int]$rowCount=0,
[object[]]$rowInfo=@(),
[object[]]$fixedInfo=@())

For($rowidx = $RowIndex;$rowidx -le $rowCount;$rowidx++)


{
$rd = @($rowInfo[$rowidx - 2])
$htmlbody = $htmlbody + "<tr>"
For($columnIndex = 0; $columnIndex -lt $colCount; $columnindex+=2)
{
$tmp = CheckHTMLColor $rd[$columnIndex+1]

If($fixedInfo.Length -eq 0)
{
$htmlbody += "<td style=""background-color:$($tmp)""><font
face='$($fontName)' size='$($fontSize)'>"
}
Else
{
$htmlbody += "<td style=""width:$
($fixedInfo[$columnIndex/2]); background-color:$($tmp)""><font face='$($fontName)'
size='$($fontSize)'>"
}

If($rd[$columnIndex+1] -band $htmlbold)


{
$htmlbody += "<b>"
}
If($rd[$columnIndex+1] -band $htmlitalics)
{
$htmlbody += "<i>"
}
If($Null -ne $rd[$columnIndex])
{
$cell = $rd[$columnIndex].tostring()
If($cell -eq " " -or $cell.length -eq 0)
{
$htmlbody += "&nbsp;&nbsp;&nbsp;"
}
Else
{
For($i=0;$i -lt $cell.length;$i++)
{
If($cell[$i] -eq " ")
{
$htmlbody += "&nbsp;"
}
If($cell[$i] -ne " ")
{
Break
}
}
$htmlbody += $cell
}
}
Else
{
$htmlbody += "&nbsp;&nbsp;&nbsp;"
}
If($rd[$columnIndex+1] -band $htmlbold)
{
$htmlbody += "</b>"
}
If($rd[$columnIndex+1] -band $htmlitalics)
{
$htmlbody += "</i>"
}
$htmlbody += "</font></td>"
}
$htmlbody += "</tr>"
}
out-file -FilePath $Script:FileName1 -Append -InputObject $HTMLBody 4>$Null
}

#**********************************************************************************
*************************
# FormatHTMLTable
# Created by Ken Avram
# modified by Jake Rutski
#**********************************************************************************
*************************

<#
.Synopsis
Format table for HTML output document
.DESCRIPTION
This function formats a table for HTML from an array of strings
.PARAMETER noBorder
If set to $true, a table will be generated without a border (border='0')
.PARAMETER noHeadCols
This parameter should be used when generating tables without column headers
Set this parameter equal to the number of columns in the table
.PARAMETER rowArray
This parameter contains the row data array for the table
.PARAMETER columnArray
This parameter contains column header data for the table
.PARAMETER fixedWidth
This parameter contains widths for columns in pixel format ("100px") to
override auto column widths
The variable should contain a width for each column you wish to override the
auto-size setting
For example: $columnWidths = @("100px","110px","120px","130px","140px")

.USAGE
FormatHTMLTable <Table Header> <Table Format> <Font Name> <Font Size>

.EXAMPLE
FormatHTMLTable "Table Heading" "auto" "Calibri" 3

This example formats a table and writes it out into an html file. All of the
parameters are optional
defaults are used if not supplied.

for <Table format>, the default is auto which will autofit the text into the
columns and adjust to the longest text in that column. You can also use percentage
i.e. 25%
which will take only 25% of the line and will auto word wrap the text to the
next line in the column. Also, instead of using a percentage, you can use pixels
i.e. 400px.

FormatHTMLTable "Table Heading" "auto" -rowArray $rowData -columnArray


$columnData

This example creates an HTML table with a heading of 'Table Heading', auto
column spacing, column header data from $columnData and row data from $rowData

FormatHTMLTable "Table Heading" -rowArray $rowData -noHeadCols 3

This example creates an HTML table with a heading of 'Table Heading', auto
column spacing, no header, and row data from $rowData

FormatHTMLTable "Table Heading" -rowArray $rowData -fixedWidth $fixedColumns

This example creates an HTML table with a heading of 'Table Heading, no


header, row data from $rowData, and fixed columns defined by $fixedColumns

.NOTES
In order to use the formatted table it first has to be loaded with data.
Examples below will show how to load the table:

First, initialize the table array

$rowdata = @()
Then Load the array. If you are using column headers then load those into
the column headers array, otherwise the first line of the table goes into the
column headers array
and the second and subsequent lines go into the $rowdata table as shown
below:

$columnHeaders = @('Display Name',($htmlsilver -bor $htmlbold),'Status',


($htmlsilver -bor $htmlbold),'Startup Type',($htmlsilver -bor $htmlbold))

The first column is the actual name to display, the second are the attributes
of the column i.e. color anded with bold or italics. For the anding, parens are
required or it will
not format correctly.

This is following by adding rowdata as shown below. As more columns are


added the columns will auto adjust to fit the size of the page.

$rowdata = @()
$columnHeaders = @("User Name",($htmlsilver -bor $htmlbold),$UserName,
$htmlwhite)
$rowdata += @(,('Save as PDF',($htmlsilver -bor $htmlbold),$PDF.ToString(),
$htmlwhite))
$rowdata += @(,('Save as TEXT',($htmlsilver -bor $htmlbold),$TEXT.ToString(),
$htmlwhite))
$rowdata += @(,('Save as WORD',($htmlsilver -bor $htmlbold),
$MSWORD.ToString(),$htmlwhite))
$rowdata += @(,('Save as HTML',($htmlsilver -bor $htmlbold),$HTML.ToString(),
$htmlwhite))
$rowdata += @(,('Add DateTime',($htmlsilver -bor $htmlbold),
$AddDateTime.ToString(),$htmlwhite))
$rowdata += @(,('Hardware Inventory',($htmlsilver -bor $htmlbold),
$Hardware.ToString(),$htmlwhite))
$rowdata += @(,('Computer Name',($htmlsilver -bor $htmlbold),$ComputerName,
$htmlwhite))
$rowdata += @(,('Filename1',($htmlsilver -bor $htmlbold),
$Script:FileName1,$htmlwhite))
$rowdata += @(,('OS Detected',($htmlsilver -bor $htmlbold),$Script:RunningOS,
$htmlwhite))
$rowdata += @(,('PSUICulture',($htmlsilver -bor $htmlbold),$PSCulture,
$htmlwhite))
$rowdata += @(,('PoSH version',($htmlsilver -bor $htmlbold),
$Host.Version.ToString(),$htmlwhite))
FormatHTMLTable "Example of Horizontal AutoFitContents HTML Table" -rowArray
$rowdata

The 'rowArray' paramater is mandatory to build the table, but it is not set
as such in the function - if nothing is passed, the table will be empty.

Colors and Bold/Italics Flags are shown below:

htmlbold
htmlitalics
htmlred
htmlcyan
htmlblue
htmldarkblue
htmllightblue
htmlpurple
htmlyellow
htmllime
htmlmagenta
htmlwhite
htmlsilver
htmlgray
htmlolive
htmlorange
htmlmaroon
htmlgreen
htmlblack

#>

Function FormatHTMLTable
{
Param([string]$tableheader,
[string]$tablewidth="auto",
[string]$fontName="Calibri",
[int]$fontSize=2,
[switch]$noBorder=$false,
[int]$noHeadCols=1,
[object[]]$rowArray=@(),
[object[]]$fixedWidth=@(),
[object[]]$columnArray=@())

$HTMLBody = "<b><font face='" + $fontname + "' size='" + ($fontsize + 1) +


"'>" + $tableheader + "</font></b>"

If($columnArray.Length -eq 0)
{
$NumCols = $noHeadCols + 1
} # means we have no column headers, just a table
Else
{
$NumCols = $columnArray.Length
} # need to add one for the color attrib

If($Null -ne $rowArray)


{
$NumRows = $rowArray.length + 1
}
Else
{
$NumRows = 1
}

If($noBorder)
{
$htmlbody += "<table border='0' width='" + $tablewidth + "'>"
}
Else
{
$htmlbody += "<table border='1' width='" + $tablewidth + "'>"
}

If(!($columnArray.Length -eq 0))


{
$htmlbody += "<tr>"
For($columnIndex = 0; $columnIndex -lt $NumCols; $columnindex+=2)
{
$tmp = CheckHTMLColor $columnArray[$columnIndex+1]
If($fixedWidth.Length -eq 0)
{
$htmlbody += "<td style=""background-color:$($tmp)""><font
face='$($fontName)' size='$($fontSize)'>"
}
Else
{
$htmlbody += "<td style=""width:$
($fixedWidth[$columnIndex/2]); background-color:$($tmp)""><font face='$($fontName)'
size='$($fontSize)'>"
}

If($columnArray[$columnIndex+1] -band $htmlbold)


{
$htmlbody += "<b>"
}
If($columnArray[$columnIndex+1] -band $htmlitalics)
{
$htmlbody += "<i>"
}
If($Null -ne $columnArray[$columnIndex])
{
If($columnArray[$columnIndex] -eq " " -or
$columnArray[$columnIndex].length -eq 0)
{
$htmlbody += "&nbsp;&nbsp;&nbsp;"
}
Else
{
For($i=0;$i -lt $columnArray[$columnIndex].length;
$i+=2)
{
If($columnArray[$columnIndex][$i] -eq " ")
{
$htmlbody += "&nbsp;"
}
If($columnArray[$columnIndex][$i] -ne " ")
{
Break
}
}
$htmlbody += $columnArray[$columnIndex]
}
}
Else
{
$htmlbody += "&nbsp;&nbsp;&nbsp;"
}
If($columnArray[$columnIndex+1] -band $htmlbold)
{
$htmlbody += "</b>"
}
If($columnArray[$columnIndex+1] -band $htmlitalics)
{
$htmlbody += "</i>"
}
$htmlbody += "</font></td>"
}
$htmlbody += "</tr>"
}
$rowindex = 2
If($Null -ne $rowArray)
{
AddHTMLTable $fontName $fontSize -colCount $numCols -rowCount $NumRows
-rowInfo $rowArray -fixedInfo $fixedWidth
$rowArray = @()
$htmlbody = "</table>"
}
Else
{
$HTMLBody += "</table>"
}
out-file -FilePath $Script:FileName1 -Append -InputObject $HTMLBody 4>$Null
}
#endregion

#region other HTML functions


#**********************************************************************************
*************************
# CheckHTMLColor - Called from AddHTMLTable WriteHTMLLine and FormatHTMLTable
#**********************************************************************************
*************************
Function CheckHTMLColor
{
Param($hash)

If($hash -band $htmlwhite)


{
Return $htmlwhitemask
}
If($hash -band $htmlred)
{
Return $htmlredmask
}
If($hash -band $htmlcyan)
{
Return $htmlcyanmask
}
If($hash -band $htmlblue)
{
Return $htmlbluemask
}
If($hash -band $htmldarkblue)
{
Return $htmldarkbluemask
}
If($hash -band $htmllightblue)
{
Return $htmllightbluemask
}
If($hash -band $htmlpurple)
{
Return $htmlpurplemask
}
If($hash -band $htmlyellow)
{
Return $htmlyellowmask
}
If($hash -band $htmllime)
{
Return $htmllimemask
}
If($hash -band $htmlmagenta)
{
Return $htmlmagentamask
}
If($hash -band $htmlsilver)
{
Return $htmlsilvermask
}
If($hash -band $htmlgray)
{
Return $htmlgraymask
}
If($hash -band $htmlblack)
{
Return $htmlblackmask
}
If($hash -band $htmlorange)
{
Return $htmlorangemask
}
If($hash -band $htmlmaroon)
{
Return $htmlmaroonmask
}
If($hash -band $htmlgreen)
{
Return $htmlgreenmask
}
If($hash -band $htmlolive)
{
Return $htmlolivemask
}
}

Function SetupHTML
{
Write-Verbose "$(Get-Date): Setting up HTML"
If(!$AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName).html"
}
ElseIf($AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName)_$(Get-Date
-f yyyy-MM-dd_HHmm).html"
}

$htmlhead = "<html><head><meta http-equiv='Content-Language'


content='da'><title>" + $Script:Title + "</title></head><body>"
out-file -FilePath $Script:Filename1 -Force -InputObject $HTMLHead 4>$Null
}#endregion
#region Iain's Word table functions

<#
.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,
[Switch] $NoInternalGridLines,
# Built-in Word table formatting style constant
# Would recommend only $wdTableFormatContempory for normal usage
(possibly $wdTableFormatList5 for List view)
[Parameter(ValueFromPipelineByPropertyName=$True)] [int] $Format = 0
)

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(($Null -eq $Columns) -and ($Null -ne $Headers))
{
Write-Warning "No columns specified and therefore, specified
headers will be ignored.";
$Columns = $Null;
}
ElseIf(($Null -ne $Columns) -and ($Null -ne $Headers))
{
## 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($Null -eq $Columns)
{
## Build the available columns from all available
PSCustomObject note properties
[string[]] $Columns = @();
## Add each NoteProperty name to the array
ForEach($Property in ($CustomObject | Get-Member
-MemberType NoteProperty))
{
$Columns += $Property.Name;
}
}

## Add the table headers from -Headers or -Columns (except


when in -List(view)
If(-not $List)
{
Write-Debug ("$(Get-Date): `t`tBuilding table
headers");
If($Null -ne $Headers)
{
[ref] $Null = $WordRangeString.AppendFormat("{0}`n",
[string]::Join("`t", $Headers));
}
Else
{
[ref] $Null = $WordRangeString.AppendFormat("{0}`n",
[string]::Join("`t", $Columns));
}
}
## Iterate through each PSCustomObject
Write-Debug ("$(Get-Date): `t`tBuilding table rows");
ForEach($Object in $CustomObject)
{
$OrderedValues = @();
## Add each row item in the specified order
ForEach($Column in $Columns)
{
$OrderedValues += $Object.$Column;
}
## Use the ordered list to add each column in
specified order
[ref] $Null = $WordRangeString.AppendFormat("{0}`n",
[string]::Join("`t", $OrderedValues));
} ## end ForEach
Write-Debug ("$(Get-Date): `t`t`tAdded '{0}' table rows" -f
($CustomObject.Count));
} ## end CustomObject

Default
{ ## Hashtable
If($Null -eq $Columns)
{
## 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;
}

## Add the table headers from -Headers or -Columns (except


when in -List(view)
If(-not $List)
{
Write-Debug ("$(Get-Date): `t`tBuilding table
headers");
If($Null -ne $Headers)
{
[ref] $Null =
$WordRangeString.AppendFormat("{0}`n", [string]::Join("`t", $Headers));
}
Else
{
[ref] $Null =
$WordRangeString.AppendFormat("{0}`n", [string]::Join("`t", $Columns));
}
}

## Iterate through each Hashtable


Write-Debug ("$(Get-Date): `t`tBuilding table rows");
ForEach($Hash in $Hashtable)
{
$OrderedValues = @();
## Add each row item in the specified order
ForEach($Column in $Columns)
{
$OrderedValues += $Hash.$Column;
}
## Use the ordered list to add each column in
specified order
[ref] $Null = $WordRangeString.AppendFormat("{0}`n",
[string]::Join("`t", $OrderedValues));
} ## end ForEach

Write-Debug ("$(Get-Date): `t`t`tAdded '{0}' table rows" -f


$Hashtable.Count);
} ## end default
} ## end switch

## Create a MS Word range and set its text to our tab-delimited,


concatenated string
Write-Debug ("$(Get-Date): `t`tBuilding table range");
$WordRange = $Script:Doc.Application.Selection.Range;
$WordRange.Text = $WordRangeString.ToString();

## Create hash table of named arguments to pass to the ConvertToTable


method
$ConvertToTableArguments = @{ Separator =
[Microsoft.Office.Interop.Word.WdTableFieldSeparator]::wdSeparateByTabs; }

## Negative built-in styles are not supported by the ConvertToTable


method
If($Format -ge 0)
{
$ConvertToTableArguments.Add("Format", $Format);
$ConvertToTableArguments.Add("ApplyBorders", $True);
$ConvertToTableArguments.Add("ApplyShading", $True);
$ConvertToTableArguments.Add("ApplyFont", $True);
$ConvertToTableArguments.Add("ApplyColor", $True);
If(!$List)
{
$ConvertToTableArguments.Add("ApplyHeadingRows", $True);
}
$ConvertToTableArguments.Add("ApplyLastRow", $True);
$ConvertToTableArguments.Add("ApplyFirstColumn", $True);
$ConvertToTableArguments.Add("ApplyLastColumn", $True);
}

## Invoke ConvertToTable method - with named arguments - to convert


Word range to a table
## See http://msdn.microsoft.com/en-
us/library/office/aa171893(v=office.11).aspx
Write-Debug ("$(Get-Date): `t`tConverting range to table");
## Store the table reference just in case we need to set alternate row
coloring
$WordTable = $WordRange.GetType().InvokeMember(
"ConvertToTable", # Method name
[System.Reflection.BindingFlags]::InvokeMethod, # Flags
$Null, # Binder
$WordRange, # Target (self!)
([Object[]]($ConvertToTableArguments.Values)), ## Named argument
values
$Null, # Modifiers
$Null, # Culture
([String[]]($ConvertToTableArguments.Keys)) ## Named argument
names
);
## Implement grid lines (will wipe out any existing formatting
If($Format -lt 0)
{
Write-Debug ("$(Get-Date): `t`tSetting table format");
$WordTable.Style = $Format;
}

## Set the table autofit behavior


If($AutoFit -ne -1)
{
$WordTable.AutoFitBehavior($AutoFit);
}

If(!$List)
{
#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;
}
If($NoGridLines)
{
$WordTable.Borders.InsideLineStyle = $wdLineStyleNone;
$WordTable.Borders.OutsideLineStyle = $wdLineStyleNone;
}
If($NoInternalGridLines)
{
$WordTable.Borders.InsideLineStyle = $wdLineStyleNone;
$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($Null -ne $BackgroundColor)
{ $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($Null -ne $Font) { $Cell.Range.Font.Name =
$Font; }
If($Null -ne $Color) { $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($Null -ne $Font) { $Cell.Range.Font.Name = $Font; }
If($Null -ne $Color) { $Cell.Range.Font.Color = $Color; }
If($Size -ne 0) { $Cell.Range.Font.Size = $Size; }
If($Null -ne $BackgroundColor)
{ $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($Null -ne $Font) { $Cell.Range.Font.Name =
$Font; }
If($Null -ne $Color) { $Cell.Range.Font.Color =
$Color; }
If($Size -ne 0) { $Cell.Range.Font.Size = $Size; }
If($Null -ne $BackgroundColor)
{ $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 :'(

Note: this function is called by the AddWordTable function if an alternate


row
format is specified.
.EXAMPLE
SetWordTableAlternateRowColor -Table $TableReference -BackgroundColor 255

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);
}
}
#endregion

#region general script functions


Function Get-ADTrustInfo
{
Param ($TrustInfo)

$propertyhash = @{
TrustType = $NULL
TrustAttribute = $NULL
TrustDirection = $NULL
}

$TrustDirectionNumber = $TrustInfo.TrustDirection
$TrustTypeNumber = $TrustInfo.TrustType
$TrustAttributesNumber = $TrustInfo.TrustAttributes

#http://msdn.microsoft.com/en-us/library/cc234293.aspx
Switch ($TrustTypeNumber)
{
1 { $propertyhash['TrustType'] = "Trust with a Windows domain not
running Active Directory"; Break}
2 { $propertyhash['TrustType'] = "Trust with a Windows domain running
Active Directory"; Break}
3 { $propertyhash['TrustType'] = "Trust with a non-Windows-compliant
Kerberos distribution"; Break}
4 { $propertyhash['TrustType'] = "Trust with a DCE realm (not used)";
Break}
Default { $propertyhash['TrustType'] = "Invalid Trust Type of $
($TrustTypeNumber)" ; Break}
}
$propertyhash['TrustAttribute'] = @()
#$hextrustAttributesValue = '{0:X}' -f $trustAttributesNumber
Switch ($trustAttributesNumber)
{
1 {$propertyhash['TrustAttribute'] += "Non-Transitive"}
2 {$propertyhash['TrustAttribute'] += "Uplevel clients only"}
4 {$propertyhash['TrustAttribute'] += "Quarantined Domain (External,
SID Filtering)"}
8 {$propertyhash['TrustAttribute'] += "Cross-Organizational Trust
(Selective Authentication)"}
16 {$propertyhash['TrustAttribute'] += "Interforest Trust"}
32 {$propertyhash['TrustAttribute'] += "Intraforest Trust"}
64 {$propertyhash['TrustAttribute'] += "MIT Trust using RC4
Encryption"}
512 {$propertyhash['TrustAttribute'] += "Cross organization Trust no
TGT delegation"}
}

Switch ($TrustDirectionNumber)
{
0 { $propertyhash['TrustDirection'] = "Disabled"; Break}
1 { $propertyhash['TrustDirection'] = "Inbound"; Break}
2 { $propertyhash['TrustDirection'] = "Outbound"; Break}
3 { $propertyhash['TrustDirection'] = "Bidirectional"; Break}
Default { $propertyhash['TrustDirection'] = $TrustDirectionNumber ;
Break}
}

New-Object -Type PSObject -property $propertyhash


}

Function validStateProp( [object] $object, [string] $topLevel, [string]


$secondLevel )
{
#function created 8-jan-2014 by Michael B. Smith
If( $object )
{
If((Get-Member -Name $topLevel -InputObject $object))
{
If((Get-Member -Name $secondLevel -InputObject $object.
$topLevel))
{
Return $True
}
}
}
Return $False
}

Function validObject( [object] $object, [string] $topLevel )


{
#function created 8-jan-2014 by Michael B. Smith
If( $object )
{
If((Get-Member -Name $topLevel -InputObject $object))
{
Return $True
}
}
Return $False
}

Function AbortScript
{
If($MSWord -or $PDF)
{
$Script:Word.quit()
Write-Verbose "$(Get-Date): System Cleanup"

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Script:Word) |
Out-Null
If(Test-Path variable:global:word)
{
Remove-Variable -Name word -Scope Global
}
}
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Write-Verbose "$(Get-Date): Script has been aborted"
$ErrorActionPreference = $SaveEAPreference
Exit
}

Function BuildMultiColumnTable
{
Param([Array]$xArray, [String]$xType)

#divide by 0 bug reported 9-Apr-2014 by Lee Dehmer


#if security group name or OU name was longer than 60 characters it caused a
divide by 0 error

#added a second parameter to the function so the verbose message would say
whether
#the function is processing servers, security groups or OUs.

If(-not ($xArray -is [Array]))


{
$xArray = (,$xArray)
}
[int]$MaxLength = 0
[int]$TmpLength = 0
#remove 60 as a hard-coded value
#60 is the max width the table can be when indented 36 points
[int]$MaxTableWidth = 60
ForEach($xName in $xArray)
{
$TmpLength = $xName.Length
If($TmpLength -gt $MaxLength)
{
$MaxLength = $TmpLength
}
}
$TableRange = $doc.Application.Selection.Range
#removed hard-coded value of 60 and replace with MaxTableWidth variable
[int]$Columns = [Math]::Floor($MaxTableWidth / $MaxLength)
If($xArray.count -lt $Columns)
{
[int]$Rows = 1
#not enough array items to fill columns so use array count
$MaxCells = $xArray.Count
#reset column count so there are no empty columns
$Columns = $xArray.Count
}
ElseIf($Columns -eq 0)
{
#divide by 0 bug if this condition is not handled
#number was larger than $MaxTableWidth so there can only be one column
#with one cell per row
[int]$Rows = $xArray.count
$Columns = 1
$MaxCells = 1
}
Else
{
[int]$Rows = [Math]::Floor( ( $xArray.count + $Columns - 1 ) /
$Columns)
#more array items than columns so don't go past last column
$MaxCells = $Columns
}
$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle
[int]$xRow = 1
[int]$ArrayItem = 0
While($xRow -le $Rows)
{
For($xCell=1; $xCell -le $MaxCells; $xCell++)
{
$Table.Cell($xRow,$xCell).Range.Text = $xArray[$ArrayItem]
$ArrayItem++
}
$xRow++
}
$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

FindWordDocumentEnd
$TableRange = $Null
$Table = $Null
$xArray = $Null
}

Function UserIsaDomainAdmin
{
#function adapted from sample code provided by Thomas Vuylsteke
$IsDA = $False
$name = $env:username
Write-Verbose "$(Get-Date): TokenGroups - Checking groups for $name"

$root = [ADSI]""
$filter = "(sAMAccountName=$name)"
$props = @("distinguishedName")
$Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,
$filter,$props)
$account = $Searcher.FindOne().properties.distinguishedname
$user = [ADSI]"LDAP://$Account"
$user.GetInfoEx(@("tokengroups"),0)
$groups = $user.Get("tokengroups")

$domainAdminsSID = New-Object System.Security.Principal.SecurityIdentifier


(((Get-ADDomain -Server $ADForest -EA 0).DomainSid).Value+"-512")

ForEach($group in $groups)
{
$ID = New-Object System.Security.Principal.SecurityIdentifier($group,0)

If($ID.CompareTo($domainAdminsSID) -eq 0)
{
$IsDA = $True
Break
}
}

Return $IsDA
}

Function ElevatedSession
{
$currentPrincipal = New-Object
Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurre
nt() )

If($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrat
or ))
{
Write-Verbose "$(Get-Date): This is an elevated PowerShell session"
Return $True
}
Else
{
Write-Host "" -Foreground White
Write-Host "$(Get-Date): This is NOT an elevated PowerShell session"
-Foreground White
Write-Host "" -Foreground White
Return $False
}
}

Function Get-ComputerCountByOS
{
<#
This function is provided by Jeremy Saunders and used with his permission

http://www.jhouseconsulting.com/2014/06/22/script-to-create-an-overview-of-
all-computer-objects-in-a-domain-1385

Jeremy sent me version 1.8 of his script to use as the basis for this
function

This function will provide an overview and count of all computer objects in a
domain based on Operating System and Service Pack. It helps an organisation
to understand the number of stale and active computers against the different
types of operating systems deployed in their environment.

Computer objects are filtered into 4 categories:


1) Windows Servers
2) Windows Workstations
3) Other non-Windows (Linux, Mac, etc)
4) Windows Cluster Name Objects (CNOs) and Virtual Computer Objects (VCOs)

A Stale object is derived from 2 values ANDed together:


1) PasswordLastChanged > $MaxPasswordLastChanged days ago
2) LastLogonDate > $MaxLastLogonDate days ago

By default the script variable for $MaxPasswordLastChanged is set to 90 and


the variable for $MaxLastLogonDate is set to 30. These can easily be adjusted
to suite your definition of a stale object.

The Active objects column is calculated by subtracting the Enabled_Stale


value from the Enabled value. This gives us an accurate number of active
objects against each Operating System.

To help provide a high level overview of the computer object landscape, we


calculate the number of stale objects of enabled and disabled objects
separately. Disabled objects are often ignored, but it's pointless leaving
old disabled computer objects in the domain.

For viewing purposes we sort the output by Operating System and not count.

You may notice a question mark (?) in some of the OperatingSystem strings.
This is a representation of each Double-Byte character that was unable to
be translated. Refer to Microsoft KB829856 for an explanation.

Computers change their passwword if and when they feel like it. The domain
doesn't initiate the change. It is controlled by three values under the
following registry key:
- HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters
- DisablePasswordChange
- MaximumPasswordAge
- RefusePasswordChange
If these values are not present, the default value of 30 days will be used.

Non Windows operating systems and appliances vary in the way they manage
their password change, with some not doing it at all. It's best to do some
research before drawing any conclusions about the validity of these objects.
i.e. Don't just go and delete them because my script says they are stale.

Be aware that a cluster updates the lastLogonTimeStamp of the CNO/VNO when


it brings a clustered network name resource online. So it could be running
for months without an update to the lastLogonTimeStamp attribute.

Syntax examples:

- To execute the script in the current Domain:


Get-ComputerCountByOS.ps1

- To execute the script against a trusted Domain:


Get-ComputerCountByOS.ps1 -TrustedDomain mydemosthatrock.com

Script Name: Get-ComputerCountByOS.ps1


Release: 1.6
Written by [email protected] 20th May 2012
Modified by [email protected] 4th December 2015

#>

#-------------------------------------------------------------
param([String]$TrustedDomain)

#-------------------------------------------------------------

# Set this to true to include service pack level. This makes the
# output more ganular, as the counts are then based on Operating
# System + Service Pack.
$OperatingSystemIncludesServicePack = $True

# Set this to the maximum value in number of days when the computer
# password last changed. Do not go beyond 90 days.
$MaxPasswordLastChanged = 90

# Set this to the maximum value in number of days when the computer
# last logged onto the domain.
$MaxLastLogonDate = 30

#-------------------------------------------------------------

$TotalComputersProcessed = 0
$ComputerCount = 0
$TotalStaleObjects = 0
$TotalEnabledStaleObjects = 0
$TotalEnabledObjects = 0
$TotalDisabledObjects = 0
$TotalDisabledStaleObjects = 0
$AllComputerObjects = New-Object System.Collections.ArrayList
$WindowsServerObjects = New-Object System.Collections.ArrayList
$WindowsWorkstationObjects = New-Object System.Collections.ArrayList
$NonWindowsComputerObjects = New-Object System.Collections.ArrayList
$CNOandVCOObjects = New-Object System.Collections.ArrayList
$ComputersHashTable = @{}

$context = new-object
System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$TrustedDomain)
Try
{
$domain =
[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context)
}
Catch [exception]
{
Write-Error $_.Exception.Message
Exit
}

# Get AD Distinguished Name


$DomainDistinguishedName = $Domain.GetDirectoryEntry() | Select-Object
-ExpandProperty DistinguishedName

$ADSearchBase = $DomainDistinguishedName

Write-Verbose "$(Get-Date): `t`tGathering computer misc data"


# Create an LDAP search for all computer objects
$ADFilter = "(objectCategory=computer)"

# There is a known bug in PowerShell requiring the DirectorySearcher


# properties to be in lower case for reliability.
$ADPropertyList =
@("distinguishedname","name","operatingsystem","operatingsystemversion", `
"operatingsystemservicepack", "description", "info", "useraccountcontrol", `
"pwdlastset","lastlogontimestamp","whencreated","serviceprincipalname")

$ADScope = "SUBTREE"
$ADPageSize = 1000
$ADSearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$
($ADSearchBase)")
$ADSearcher = New-Object System.DirectoryServices.DirectorySearcher
$ADSearcher.SearchRoot = $ADSearchRoot
$ADSearcher.PageSize = $ADPageSize
$ADSearcher.Filter = $ADFilter
$ADSearcher.SearchScope = $ADScope
If($ADPropertyList)
{
ForEach($ADProperty in $ADPropertyList)
{
[Void]$ADSearcher.PropertiesToLoad.Add($ADProperty)
}
}
Try
{
Write-Verbose "Please be patient whilst the script retrieves all
computer objects and specified attributes..."
$colResults = $ADSearcher.Findall()
# Dispose of the search and results properly to avoid a memory leak
$ADSearcher.Dispose()
$ComputerCount = $colResults.Count
}
Catch
{
$ComputerCount = 0
Write-Warning "The $ADSearchBase structure cannot be found!"
}

If($ComputerCount -ne 0)
{
Write-Verbose "Processing $ComputerCount computer objects in the
$domain Domain..."
ForEach($objResult in $colResults)
{
$Name = $objResult.Properties.name[0]
$DistinguishedName = $objResult.Properties.distinguishedname[0]

$ParentDN = $DistinguishedName -split '(?<![\\]),'


$ParentDN = $ParentDN[1..$($ParentDN.Count-1)] -join ','

Try
{
If(($objResult.Properties.operatingsystem | Measure-
Object).Count -gt 0)
{
$OperatingSystem =
$objResult.Properties.operatingsystem[0]
}
Else
{
$OperatingSystem = "Undefined"
}
}
Catch
{
$OperatingSystem = "Undefined"
}
Try
{
If(($objResult.Properties.operatingsystemversion | Measure-
Object).Count -gt 0)
{
$OperatingSystemVersion =
$objResult.Properties.operatingsystemversion[0]
}
Else
{
$OperatingSystemVersion = ""
}
}
Catch
{
$OperatingSystemVersion = ""
}
Try
{
If(($objResult.Properties.operatingsystemservicepack |
Measure-Object).Count -gt 0)
{
$OperatingSystemServicePack =
$objResult.Properties.operatingsystemservicepack[0]
}
Else
{
$OperatingSystemServicePack = ""
}
}
Catch
{
$OperatingSystemServicePack = ""
}
Try
{
If(($objResult.Properties.description | Measure-
Object).Count -gt 0)
{
$Description = $objResult.Properties.description[0]
}
Else
{
$Description = ""
}
}
Catch
{
$Description = ""
}
$PasswordTooOld = $False
$PasswordLastSet =
[System.DateTime]::FromFileTime($objResult.Properties.pwdlastset[0])
If($PasswordLastSet -lt (Get-Date).AddDays(-
$MaxPasswordLastChanged))
{
$PasswordTooOld = $True
}
$HasNotRecentlyLoggedOn = $False
Try
{
If(($objResult.Properties.lastlogontimestamp | Measure-
Object).Count -gt 0)
{
$LastLogonTimeStamp =
$objResult.Properties.lastlogontimestamp[0]
$LastLogon =
[System.DateTime]::FromFileTime($LastLogonTimeStamp)
If($LastLogon -le (Get-Date).AddDays(-
$MaxLastLogonDate))
{
$HasNotRecentlyLoggedOn = $True
}
If($LastLogon -match "1/01/1601")
{
$LastLogon = "Never logged on before"
}
}
Else
{
$LastLogon = "Never logged on before"
}
}
Catch
{
$LastLogon = "Never logged on before"
}
$WhenCreated = $objResult.Properties.whencreated[0]

# If it's never logged on before and was created more than


$MaxLastLogonDate days
# ago, set the $HasNotRecentlyLoggedOn variable to True.
# An example of this would be if you prestaged the account but
never ended up using
# it.
If($lastLogon -eq "Never logged on before")
{
If($whencreated -le (Get-Date).AddDays(-$MaxLastLogonDate))
{
$HasNotRecentlyLoggedOn = $True
}
}

# Check if it's a stale object.


$IsStale = $False
If($PasswordTooOld -eq $True -AND $HasNotRecentlyLoggedOn -eq
$True)
{
$IsStale = $True
}
Try
{
$ServicePrincipalName =
$objResult.Properties.serviceprincipalname
}
Catch
{
$ServicePrincipalName = ""
}
$UserAccountControl = $objResult.Properties.useraccountcontrol[0]
$Enabled = $True
Switch($UserAccountControl)
{
{($UserAccountControl -bor 0x0002) -eq $UserAccountControl}

{
$Enabled = $False
}
}
Try
{
If(($objResult.Properties.info | Measure-Object).Count -gt
0)
{
$notes = $objResult.Properties.info[0]
$notes = $notes -replace "`r`n", "|"
}
Else
{
$notes = ""
}
}
Catch
{
$notes = ""
}
If($IsStale)
{
$TotalStaleObjects = $TotalStaleObjects + 1
}
If($Enabled)
{
$TotalEnabledObjects = $TotalEnabledObjects + 1
}
If($Enabled -eq $False)
{
$TotalDisabledObjects = $TotalDisabledObjects + 1
}
If($IsStale -AND $Enabled)
{
$TotalEnabledStaleObjects = $TotalEnabledStaleObjects + 1
}
If($IsStale -AND $Enabled -eq $False)
{
$TotalDisabledStaleObjects = $TotalDisabledStaleObjects + 1
}

$obj = New-Object -TypeName PSObject


$obj | Add-Member -MemberType NoteProperty -Name "Name" -value
$Name
$obj | Add-Member -MemberType NoteProperty -Name "ParentOU"
-value $ParentDN
$obj | Add-Member -MemberType NoteProperty -Name
"OperatingSystem" -value $OperatingSystem
$obj | Add-Member -MemberType NoteProperty -Name "Version" -value
$OperatingSystemVersion
$obj | Add-Member -MemberType NoteProperty -Name "ServicePack"
-value $OperatingSystemServicePack
$obj | Add-Member -MemberType NoteProperty -Name "Description"
-value $Description

If(!($ServicePrincipalName -match 'MSClusterVirtualServer'))


{
If($OperatingSystem -match 'windows' -AND $OperatingSystem
-match 'server')
{
$Category = "Server"
}
If($OperatingSystem -match 'windows' -AND !
($OperatingSystem -match 'server'))
{
$Category = "Workstation"
}
If(!($OperatingSystem -match 'windows'))
{
$Category = "Other"
}
}
Else
{
$Category = "CNO or VCO"
$OperatingSystem = $OperatingSystem + " - " + $Category
}
If($Category -eq "")
{
$Category = "Undefined"
}

$obj | Add-Member -MemberType NoteProperty -Name "Category"


-value $Category
$obj | Add-Member -MemberType NoteProperty -Name
"PasswordLastSet" -value $PasswordLastSet
$obj | Add-Member -MemberType NoteProperty -Name "LastLogon"
-value $LastLogon
$obj | Add-Member -MemberType NoteProperty -Name "Enabled" -value
$Enabled
$obj | Add-Member -MemberType NoteProperty -Name "IsStale" -value
$IsStale
$obj | Add-Member -MemberType NoteProperty -Name "WhenCreated"
-value $WhenCreated
$obj | Add-Member -MemberType NoteProperty -Name "Notes" -value
$notes

$AllComputerObjects.Add($obj) > $Null


Switch($Category)
{
"Server" {$WindowsServerObjects.Add($obj) > $Null;
break}
"Workstation" {$WindowsWorkstationObjects.Add($obj) >
$Null; break}
"Other"
{$NonWindowsComputerObjects.Add($obj) > $Null; break}
"CNO or VCO" {$CNOandVCOObjects.Add($obj) > $Null;
break}
"Undefined" {$NonWindowsComputerObjects.Add($obj) >
$Null; break}
}
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name
"OperatingSystem" -value $OperatingSystem
If($OperatingSystemIncludesServicePack -eq $False)
{
$FullOperatingSystem = $OperatingSystem
}
Else
{
$FullOperatingSystem = $OperatingSystem + " " +
$OperatingSystemServicePack
$obj | Add-Member -MemberType NoteProperty -Name
"ServicePack" -value $OperatingSystemServicePack
}
$obj | Add-Member -MemberType NoteProperty -Name "Category"
-value $Category

# Create a hashtable to capture a count of each Operating System


If(!($ComputersHashTable.ContainsKey($FullOperatingSystem)))
{
$TotalCount = 1
$StaleCount = 0
$EnabledStaleCount = 0
$DisabledStaleCount = 0
If($IsStale -eq $True)
{
$StaleCount = 1
}
If($Enabled -eq $True)
{
$EnabledCount = 1
$DisabledCount = 0
If($IsStale -eq $True)
{
$EnabledStaleCount = 1
}
}
If($Enabled -eq $False)
{
$DisabledCount = 1
$EnabledCount = 0
If($IsStale -eq $True)
{
$DisabledStaleCount = 1
}
}
$obj | Add-Member -MemberType NoteProperty -Name "Total"
-value $TotalCount
$obj | Add-Member -MemberType NoteProperty -Name "Stale"
-value $StaleCount
$obj | Add-Member -MemberType NoteProperty -Name "Enabled"
-value $EnabledCount
$obj | Add-Member -MemberType NoteProperty -Name
"Enabled_Stale" -value $EnabledStaleCount
$obj | Add-Member -MemberType NoteProperty -Name "Active"
-value ($EnabledCount - $EnabledStaleCount)
$obj | Add-Member -MemberType NoteProperty -Name "Disabled"
-value $DisabledCount
$obj | Add-Member -MemberType NoteProperty -Name
"Disabled_Stale" -value $DisabledStaleCount
$ComputersHashTable = $ComputersHashTable +
@{$FullOperatingSystem = $obj}
}
Else
{
$value = $ComputersHashTable.Get_Item($FullOperatingSystem)
$TotalCount = $value.Total + 1
$StaleCount = $value.Stale
$EnabledStaleCount = $value.Enabled_Stale
$DisabledStaleCount = $value.Disabled_Stale
If($IsStale -eq $True)
{
$StaleCount = $value.Stale + 1
}
If($Enabled -eq $True)
{
$EnabledCount = $value.Enabled + 1
$DisabledCount = $value.Disabled
If($IsStale -eq $True)
{
$EnabledStaleCount = $value.Enabled_Stale + 1
}
}
If($Enabled -eq $False)
{
$DisabledCount = $value.Disabled + 1
$EnabledCount = $value.Enabled
If($IsStale -eq $True)
{
$DisabledStaleCount = $value.Disabled_Stale + 1

}
}
$obj | Add-Member -MemberType NoteProperty -Name "Total"
-value $TotalCount
$obj | Add-Member -MemberType NoteProperty -Name "Stale"
-value $StaleCount
$obj | Add-Member -MemberType NoteProperty -Name "Enabled"
-value $EnabledCount
$obj | Add-Member -MemberType NoteProperty -Name
"Enabled_Stale" -value $EnabledStaleCount
$obj | Add-Member -MemberType NoteProperty -Name "Active"
-value ($EnabledCount - $EnabledStaleCount)
$obj | Add-Member -MemberType NoteProperty -Name "Disabled"
-value $DisabledCount
$obj | Add-Member -MemberType NoteProperty -Name
"Disabled_Stale" -value $DisabledStaleCount
$ComputersHashTable.Set_Item($FullOperatingSystem,$obj)
} # end if
$TotalComputersProcessed ++
}

# Dispose of the search and results properly to avoid a memory leak


$colResults.Dispose()

$Output = $ComputersHashTable.values | ForEach-Object {$_ } | ForEach-


Object {$_ } | Sort-Object OperatingSystem -descending

If($MSWORD -or $PDF)


{
WriteWordLine 3 0 "Computer Operating Systems"
}
ElseIf($Text)
{
Line 0 "Computer Operating Systems"
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Computer Operating Systems"
}

ForEach($Item in $Output)
{
If($MSWORD -or $PDF)
{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Operating System"; Value =
$Item.OperatingSystem; }
$ScriptInformation += @{ Data = "Service Pack"; Value =
$Item.ServicePack; }
$ScriptInformation += @{ Data = "Category"; Value =
$Item.Category; }
$ScriptInformation += @{ Data = "Total"; Value =
$Item.Total; }
$ScriptInformation += @{ Data = "Stale"; Value =
$Item.Stale; }
$ScriptInformation += @{ Data = "Enabled"; Value =
$Item.Enabled; }
$ScriptInformation += @{ Data = "Enabled/Stale"; Value =
$Item.Enabled_Stale; }
$ScriptInformation += @{ Data = "Active"; Value =
$Item.Active; }
$ScriptInformation += @{ Data = "Disabled"; Value =
$Item.Disabled; }
$ScriptInformation += @{ Data = "Disabled/Stale"; Value =
$Item.Disabled_Stale; }

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;
SetWordCellFormat -Collection $Table.Columns.Item(1).Cells
-Bold -BackgroundColor $wdColorGray15;

## IB - set column widths without recursion


$Table.Columns.Item(1).Width = 100;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 1 "Operating System`t: " $Item.OperatingSystem
Line 1 "Service Pack`t`t: " $Item.ServicePack
Line 1 "Category`t`t: " $Item.Category
Line 1 "Total`t`t`t: " $Item.Total
Line 1 "Stale`t`t`t: " $Item.Stale
Line 1 "Enabled`t`t`t: " $Item.Enabled
Line 1 "Enabled/Stale`t`t: " $Item.Enabled_Stale
Line 1 "Active`t`t`t: " $Item.Active
Line 1 "Disabled`t`t: " $Item.Disabled
Line 1 "Disabled/Stale`t`t: " $Item.Disabled_Stale
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Operating System",($htmlsilver -bor
$htmlbold),$Item.OperatingSystem,$htmlwhite)
$rowdata += @(,('Service Pack',($htmlsilver -bor
$htmlbold),$Item.ServicePack,$htmlwhite))
$rowdata += @(,('Category',($htmlsilver -bor $htmlbold),
$Item.Category,$htmlwhite))
$rowdata += @(,('Total',($htmlsilver -bor $htmlbold),
$Item.Total,$htmlwhite))
$rowdata += @(,('Stale',($htmlsilver -bor $htmlbold),
$Item.Stale,$htmlwhite))
$rowdata += @(,('Enabled',($htmlsilver -bor $htmlbold),
$Item.Enabled,$htmlwhite))
$rowdata += @(,('Enabled/Stale',($htmlsilver -bor
$htmlbold),$Item.Enabled_Stale,$htmlwhite))
$rowdata += @(,('Active',($htmlsilver -bor $htmlbold),
$Item.Active,$htmlwhite))
$rowdata += @(,('Disabled',($htmlsilver -bor $htmlbold),
$Item.Disabled,$htmlwhite))
$rowdata += @(,('Disabled/Stale',($htmlsilver -bor
$htmlbold),$Item.Disabled_Stale,$htmlwhite))

$msg = ""
$columnWidths = @("100","200")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "300"
WriteHTMLLine 0 0 " "
}
}
$percent = "{0:P}" -f ($TotalStaleObjects/$ComputerCount)

If($MSWORD -or $PDF)


{
WriteWordLine 0 0 "A breakdown of the $ComputerCount Computer
Objects in the $domain Domain"
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Total Computer Objects"; Value =
$ComputerCount; }
$ScriptInformation += @{ Data = "Total Stale Computer Objects
(count)"; Value = $TotalStaleObjects; }
$ScriptInformation += @{ Data = "Total Stale Computer Objects
(percent)"; Value = $percent; }
$ScriptInformation += @{ Data = "Total Enabled Computer Objects";
Value = $TotalEnabledObjects; }
$ScriptInformation += @{ Data = "Total Enabled Stale Computer
Objects"; Value = $TotalEnabledStaleObjects; }
$ScriptInformation += @{ Data = "Total Active Computer Objects";
Value = $($TotalEnabledObjects - $TotalEnabledStaleObjects); }
$ScriptInformation += @{ Data = "Total Disabled Computer
Objects"; Value = $TotalDisabledObjects; }
$ScriptInformation += @{ Data = "Total Disabled Stale Computer
Objects"; Value = $TotalDisabledStaleObjects; }

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitContent;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 "A breakdown of the $ComputerCount Computer Objects in the
$domain Domain"

Line 1 "Total Computer Objects`t`t`t: " $ComputerCount


Line 1 "Total Stale Computer Objects (count)`t: "
$TotalStaleObjects
Line 1 "Total Stale Computer Objects (percent)`t: " $percent
Line 1 "Total Enabled Computer Objects`t`t: "
$TotalEnabledObjects
Line 1 "Total Enabled Stale Computer Objects`t: "
$TotalEnabledStaleObjects
Line 1 "Total Active Computer Objects`t`t: " $
($TotalEnabledObjects - $TotalEnabledStaleObjects)
Line 1 "Total Disabled Computer Objects`t`t: "
$TotalDisabledObjects
Line 1 "Total Disabled Stale Computer Objects`t: "
$TotalDisabledStaleObjects
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Total Computer Objects",($htmlsilver -bor
$htmlbold),$ComputerCount.ToString(),$htmlwhite)
$rowdata += @(,('Total Stale Computer Objects (count)',
($htmlsilver -bor $htmlbold),$TotalStaleObjects,$htmlwhite))
$rowdata += @(,('Total Stale Computer Objects (percent)',
($htmlsilver -bor $htmlbold),$percent,$htmlwhite))
$rowdata += @(,('Total Enabled Computer Objects',($htmlsilver
-bor $htmlbold),$TotalEnabledObjects,$htmlwhite))
$rowdata += @(,('Total Enabled Stale Computer Objects',
($htmlsilver -bor $htmlbold),$TotalEnabledStaleObjects,$htmlwhite))
$rowdata += @(,('Total Active Computer Objects',($htmlsilver -bor
$htmlbold),$($TotalEnabledObjects - $TotalEnabledStaleObjects),$htmlwhite))
$rowdata += @(,('Total Disabled Computer Objects',($htmlsilver
-bor $htmlbold),$TotalDisabledObjects,$htmlwhite))
$rowdata += @(,('Total Disabled Stale Computer Objects',
($htmlsilver -bor $htmlbold),$TotalDisabledStaleObjects,$htmlwhite))

$msg = "A breakdown of the $ComputerCount Computer Objects in the


$domain Domain"
$columnWidths = @("250","50")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "300"
WriteHTMLLine 0 0 " "
}

#Notes:
# - Computer objects are filtered into 4 categories:
# - Windows Servers
# - Windows Workstations
# - Other non-Windows (Linux, Mac, etc)
# - CNO or VCO (Windows Cluster Name Objects and Virtual Computer
Objects)
# - A Stale object is derived from 2 values ANDed together:
# PasswordLastChanged > $MaxPasswordLastChanged days ago
# AND
# LastLogonDate > $MaxLastLogonDate days ago
# - If it's never logged on before and was created more than
$MaxLastLogonDate days ago, set the
# HasNotRecentlyLoggedOn variable to True. This will also be used to
help determine if
# it's a stale account. An example of this would be if you prestaged
the account but
# never ended up using it.
# - The Active objects column is calculated by subtracting the
Enabled_Stale value from
# the Enabled value. This gives us an accurate number of active
objects against each
# Operating System.
# - To help provide a high level overview of the computer object
landscape we calculate
# the number of stale objects of enabled and disabled objects
separately.
# Disabled objects are often ignored, but it's pointless leaving old
disabled computer
# objects in the domain.
# - For viewing purposes we sort the output by Operating System and not
count.
# - You may notice a question mark (?) in some of the OperatingSystem
strings. This is a
# representation of each Double-Byte character that was unable to be
translated. Refer
# to Microsoft KB829856 for an explanation.
# - Be aware that a cluster updates the lastLogonTimeStamp of the
CNO/VNO when it brings
# a clustered network name resource online. So it could be running
for months without
# an update to the lastLogonTimeStamp attribute.
# - When a VMware ESX host has been added to Active Directory its
associated computer
# object will appear as an Operating System of unknown, version:
unknown, and service
# pack: Likewise Identity 5.3.0. The lsassd.conf manages the machine
password expiration
# lifespan, which by default is set to 30 days.
# - When a Riverbed SteelHead has been added to Active Directory the
password set on the
# Computer Object will never change by default. This must be enabled
on the command
# line using the 'domain settings pwd-refresh-int <number of days>'
command. The
# lastLogon timestamp is only updated when the SteelHead appliance is
restarted.
}
}

Function ShowScriptOptions
{
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): "
Write-Verbose "$(Get-Date): AddDateTime : $AddDateTime"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): Company Name : $Script:CoName"
}
Write-Verbose "$(Get-Date): ComputerName : $ComputerName"
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 Phone : $CompanyPhone"
Write-Verbose "$(Get-Date): Cover Page : $CoverPage"
}
Write-Verbose "$(Get-Date): DCDNSInfo : $DCDNSInfo"
Write-Verbose "$(Get-Date): Dev : $Dev"
If($Dev)
{
Write-Verbose "$(Get-Date): DevErrorFile : $Script:DevErrorFile"
}
Write-Verbose "$(Get-Date): Domain Name : $ADDomain"
Write-Verbose "$(Get-Date): Elevated : $Script:Elevated"
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): Forest Name : $ADForest"
Write-Verbose "$(Get-Date): From : $From"
Write-Verbose "$(Get-Date): GPOInheritance : $GPOInheritance"
Write-Verbose "$(Get-Date): HW Inventory : $Hardware"
Write-Verbose "$(Get-Date): IncludeUserInfo : $IncludeUserInfo"
Write-Verbose "$(Get-Date): Log : $($Log)"
Write-Verbose "$(Get-Date): MaxDetail : $MaxDetails"
Write-Verbose "$(Get-Date): Save As HTML : $HTML"
Write-Verbose "$(Get-Date): Save As PDF : $PDF"
Write-Verbose "$(Get-Date): Save As TEXT : $TEXT"
Write-Verbose "$(Get-Date): Save As WORD : $MSWORD"
Write-Verbose "$(Get-Date): ScriptInfo : $ScriptInfo"
Write-Verbose "$(Get-Date): Section : $Section"
Write-Verbose "$(Get-Date): Services : $Services"
Write-Verbose "$(Get-Date): Smtp Port : $SmtpPort"
Write-Verbose "$(Get-Date): Smtp Server : $SmtpServer"
Write-Verbose "$(Get-Date): Title : $Script:Title"
Write-Verbose "$(Get-Date): To : $To"
Write-Verbose "$(Get-Date): Use SSL : $UseSSL"
If($MSWORD -or $PDF)
{
Write-Verbose "$(Get-Date): User Name : $UserName"
}
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 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

Write-Verbose "$(Get-Date): Save and Close document and Shutdown Word"


If($Script:WordVersion -eq $wdWord2010)
{
#the $saveFormat below passes StrictMode 2
#I found this at the following two links
#http://blogs.technet.com/b/bshukla/archive/2011/09/27/3347395.aspx
#http://msdn.microsoft.com/en-
us/library/microsoft.office.interop.word.wdsaveformat(v=office.14).aspx
If($PDF)
{
Write-Verbose "$(Get-Date): Saving as DOCX file first before
saving to PDF"
}
Else
{
Write-Verbose "$(Get-Date): Saving DOCX file"
}
If($AddDateTime)
{
$Script:FileName1 += "_$(Get-Date -f yyyy-MM-dd_HHmm).docx"
If($PDF)
{
$Script:FileName2 += "_$(Get-Date -f yyyy-MM-dd_HHmm).pdf"
}
}
Write-Verbose "$(Get-Date): Running $($Script:WordProduct) and detected
operating system $($Script:RunningOS)"
$saveFormat =
[Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat],
"wdFormatDocumentDefault")
$Script:Doc.SaveAs([REF]$Script:FileName1, [ref]$SaveFormat)
If($PDF)
{
Write-Verbose "$(Get-Date): Now saving as PDF"
$saveFormat =
[Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatPDF")
$Script:Doc.SaveAs([REF]$Script:FileName2, [ref]$saveFormat)
}
}
ElseIf($Script:WordVersion -eq $wdWord2013 -or $Script:WordVersion -eq
$wdWord2016)
{
If($PDF)
{
Write-Verbose "$(Get-Date): Saving as DOCX file first before
saving to PDF"
}
Else
{
Write-Verbose "$(Get-Date): Saving DOCX file"
}
If($AddDateTime)
{
$Script:FileName1 += "_$(Get-Date -f yyyy-MM-dd_HHmm).docx"
If($PDF)
{
$Script:FileName2 += "_$(Get-Date -f yyyy-MM-dd_HHmm).pdf"
}
}
Write-Verbose "$(Get-Date): Running $($Script:WordProduct) and detected
operating system $($Script:RunningOS)"
$Script:Doc.SaveAs2([REF]$Script:FileName1, [ref]
$wdFormatDocumentDefault)
If($PDF)
{
Write-Verbose "$(Get-Date): Now saving as PDF"
$Script:Doc.SaveAs([REF]$Script:FileName2, [ref]$wdFormatPDF)
}
}

Write-Verbose "$(Get-Date): Closing Word"


$Script:Doc.Close()
$Script:Word.Quit()
If($PDF)
{
[int]$cnt = 0
While(Test-Path $Script:FileName1)
{
$cnt++
If($cnt -gt 1)
{
Write-Verbose "$(Get-Date): Waiting another 10 seconds to
allow Word to fully close (try # $($cnt))"
Start-Sleep -Seconds 10
$Script:Word.Quit()
If($cnt -gt 2)
{
#kill the winword process

#find out our session (usually "1" except on TS/RDC


or Citrix)
$SessionID = (Get-Process -PID $PID).SessionId

#Find out if winword is running in our session


$wordprocess = ((Get-Process 'WinWord' -ea 0)|Where-
Object {$_.SessionId -eq $SessionID}).Id
If($wordprocess -gt 0)
{
Write-Verbose "$(Get-Date): Attempting to stop
WinWord process # $($wordprocess)"
Stop-Process $wordprocess -EA 0
}
}
}
Write-Verbose "$(Get-Date): Attempting to delete $
($Script:FileName1) since only $($Script:FileName2) is needed (try # $($cnt))"
Remove-Item $Script:FileName1 -EA 0 4>$Null
}
}
Write-Verbose "$(Get-Date): System Cleanup"
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Script:Word) |
Out-Null
If(Test-Path variable:global:word)
{
Remove-Variable -Name word -Scope Global 4>$Null
}
$SaveFormat = $Null
[gc]::collect()
[gc]::WaitForPendingFinalizers()

#is the winword process still running? kill it

#find out our session (usually "1" except on TS/RDC or Citrix)


$SessionID = (Get-Process -PID $PID).SessionId

#Find out if winword is running in our session


$wordprocess = $Null
$wordprocess = ((Get-Process 'WinWord' -ea 0)|Where-Object {$_.SessionId -eq
$SessionID}).Id
If($null -ne $wordprocess -and $wordprocess -gt 0)
{
Write-Verbose "$(Get-Date): WinWord process is still running.
Attempting to stop WinWord process # $($wordprocess)"
Stop-Process $wordprocess -EA 0
}
}

Function SaveandCloseTextDocument
{
If($AddDateTime)
{
$Script:FileName1 += "_$(Get-Date -f yyyy-MM-dd_HHmm).txt"
}

Write-Output $Global:Output | Out-File $Script:Filename1 4>$Null


}

Function SaveandCloseHTMLDocument
{
Out-File -FilePath $Script:FileName1 -Append -InputObject
"<p></p></body></html>" 4>$Null
}

Function SetFileName1andFileName2
{
Param([string]$OutputFileName)

If($Folder -eq "")


{
$pwdpath = $pwd.Path
}
Else
{
$pwdpath = $Folder
}

If($pwdpath.EndsWith("\"))
{
#remove the trailing \
$pwdpath = $pwdpath.SubString(0, ($pwdpath.Length - 1))
}

#set $filename1 and $filename2 with no file extension


If($AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName)"
If($PDF)
{
[string]$Script:FileName2 = "$($pwdpath)\$($OutputFileName)"
}
}

If($MSWord -or $PDF)


{
CheckWordPreReq
If(!$AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName).docx"
If($PDF)
{
[string]$Script:FileName2 = "$($pwdpath)\$
($OutputFileName).pdf"
}
}

SetupWord
}
ElseIf($Text)
{
If(!$AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName).txt"
}
ShowScriptOptions
}
ElseIf($HTML)
{
If(!$AddDateTime)
{
[string]$Script:FileName1 = "$($pwdpath)\$($OutputFileName).html"
}
SetupHTML
ShowScriptOptions
}
}

#endregion

#Script begins

#region script setup function


Function ProcessScriptSetup
{
#If hardware inventory or services are requested, make sure user is running
the script with Domain Admin rights
Write-Verbose "$(Get-Date): `tTesting to see if $env:username has Domain
Admin rights"
$Script:DARights = $False
$Script:Elevated = $False

$AmIReallyDA = UserIsaDomainAdmin
If($AmIReallyDA -eq $True)
{
#user has Domain Admin rights
If($ADDomain -ne "")
{
Write-Verbose "$(Get-Date): $env:username has Domain Admin rights
in the $ADDomain Domain"
}
Else
{
Write-Verbose "$(Get-Date): $env:username has Domain Admin rights
in the $ADForest Forest"
}
$Script:DARights = $True
}
Else
{
#user does nto have Domain Admin rights
If($ADDomain -ne "")
{
Write-Verbose "$(Get-Date): $env:username does not have Domain
Admin rights in the $ADDomain Domain"
}
Else
{
Write-Verbose "$(Get-Date): $env:username does not have Domain
Admin rights in the $ADForest Forest"
}
}

$Script:Elevated = ElevatedSession

If($Hardware -or $Services -or $DCDNSINFO)


{
If($Hardware -and -not $Services)
{
Write-Verbose "$(Get-Date): Hardware inventory requested"
}
ElseIf($Services -and -not $Hardware)
{
Write-Verbose "$(Get-Date): Services requested"
}
ElseIf($Hardware -and $Services)
{
Write-Verbose "$(Get-Date): Hardware inventory and Services
requested"
}

If($DCDNSINFO)
{
Write-Verbose "$(Get-Date): Domain Controller DNS configuration
information requested"
}

If($Script:DARights -eq $False)


{
#user does not have Domain Admin rights
If($Hardware -and -not $Services)
{
#don't abort script, set $hardware to false
Write-Warning "`n`n`t`tHardware inventory was requested but
$($env:username) does not have Domain Admin rights."
Write-Warning "`n`n`t`tHardware inventory option will be
turned off."
$Script:Hardware = $False
}
ElseIf($Services -and -not $Hardware)
{
#don't abort script, set $services to false
Write-Warning "`n`n`t`tServices were requested but $
($env:username) does not have Domain Admin rights."
Write-Warning "`n`n`t`tServices option will be turned off."
$Script:Services = $False
}
ElseIf($Hardware -and $Services)
{
#don't abort script, set $hardware and $services to false
Write-Warning "`n`n`t`tHardware inventory and Services were
requested but $($env:username) does not have Domain Admin rights."
Write-Warning "`n`n`t`tHardware inventory and Services
options will be turned off."
$Script:Hardware = $False
$Script:Services = $False
}

If($DCDNSINFO)
{
#don't abort script, set $DCDNSINFO to false
Write-Warning "`n`n`t`tDCDNSINFO information was requested
but $($env:username) does not have Domain Admin rights."
Write-Warning "`n`n`t`tDCDNSINFO option will be turned
off."
$Script:DCDNSINFO = $False
}
}

If( ($Hardware -or $Services) -and -not $Script:Elevated )


{
Write-Host "Warning: " -Foreground White
Write-Host "Warning: Hardware inventory or Services were
requested but this is not an elevated PowerShell session." -Foreground White
Write-Host "Warning: Hardware inventory and Services options will
be turned off." -Foreground White
Write-Host "Warning: To obtain Hardware inventory and Services
data, please run the script from an elevated PowerShell session." -Foreground White
Write-Host "Warning: " -Foreground White
$Script:Hardware = $False
$Script:Services = $False
}

If( $DCDNSINFO -and -not $Script:Elevated )


{
Write-Host "Warning: " -Foreground White
Write-Host "Warning: Domain Controller DNS information was
requested but this is not an elevated PowerShell session." -Foreground White
Write-Host "Warning: DCDNSINFO option will be turned off."
-Foreground White
Write-Host "Warning: To obtain DCDNSINFO data, please run the
script from an elevated PowerShell session." -Foreground White
Write-Host "Warning: " -Foreground White
$Script:DCDNSINFO = $False
}

If(!$Script:DARights -and !$Script:Elevated)


{
Write-Host "Warning: " -Foreground White
Write-Host "Warning: To obtain Time Server and AD file location
data, please run the script from an elevated PowerShell session using an account
with Domain Admin rights." -Foreground White
Write-Host "Warning: " -Foreground White
}
}

#if computer name is localhost, get actual server name


If($ComputerName -eq "localhost")
{
$Script:ComputerName = $env:ComputerName
#V2.20 change from "renamed" to "changed"
Write-Verbose "$(Get-Date): Server name has been changed from localhost
to $($ComputerName)"
}

#see if default value of $Env:USERDNSDOMAIN was used


If($ComputerName -eq $Env:USERDNSDOMAIN)
{
#change $ComputerName to a found global catalog server
$Results = (Get-ADDomainController -DomainName $ADForest -Discover
-Service GlobalCatalog -EA 0).Name

If($? -and $Null -ne $Results)


{
$Script:ComputerName = $Results
#V2.20 change from "renamed" to "changed"
Write-Verbose "$(Get-Date): Server name has been changed from
$Env:USERDNSDOMAIN to $ComputerName"
}
ElseIf(!$?) #changed for 2.16
{
#may be in a child domain where -Service GlobalCatalog doesn't
work. Try PrimaryDC
$Results = (Get-ADDomainController -DomainName $ADForest
-Discover -Service PrimaryDC -EA 0).Name

If($? -and $Null -ne $Results)


{
$Script:ComputerName = $Results
#V2.20 change from "renamed" to "changed"
Write-Verbose "$(Get-Date): Server name has been changed
from $Env:USERDNSDOMAIN to $ComputerName"
}
}
}

#if computer name is an IP address, get host name from DNS


#http://blogs.technet.com/b/gary/archive/2009/08/29/resolve-ip-addresses-to-
hostname-using-powershell.aspx
#help from Michael B. Smith
$ip = $ComputerName -as [System.Net.IpAddress]
If($ip)
{
$Result = [System.Net.Dns]::gethostentry($ip)

If($? -and $Null -ne $Result)


{
$Script:ComputerName = $Result.HostName
#V2.20 change from "renamed" to "changed"
Write-Verbose "$(Get-Date): Server name has been changed from $ip
to $ComputerName"
}
Else
{
Write-Warning "Unable to resolve $ComputerName to a hostname"
}
}
Else
{
#server is online but for some reason $ComputerName cannot be converted
to a System.Net.IpAddress
}

If(![String]::IsNullOrEmpty($ComputerName))
{
#get server name
#first test to make sure the server is reachable
Write-Verbose "$(Get-Date): Testing to see if $ComputerName is online
and reachable"
If(Test-Connection -ComputerName $ComputerName -quiet -EA 0)
{
Write-Verbose "$(Get-Date): Server $ComputerName is online."
Write-Verbose "$(Get-Date): `tTest #1 to see if $ComputerName is
a Domain Controller."
#the server may be online but is it really a domain controller?

#is the ComputerName in the current domain


$Results = Get-ADDomainController $ComputerName -EA 0

If(!$? -or $Null -eq $Results)


{
#try using the Forest name
Write-Verbose "$(Get-Date): `tTest #2 to see if
$ComputerName is a Domain Controller."
$Results = Get-ADDomainController $ComputerName -Server
$ADForest -EA 0
If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`t$ComputerName is not a domain
controller for $ADForest.`n`t`tScript cannot continue.`n`n"
Exit
}
Else
{
Write-Verbose "$(Get-Date): `tTest #2 succeeded.
$ComputerName is a Domain Controller."
}
}
Else
{
Write-Verbose "$(Get-Date): `tTest #1 succeeded.
$ComputerName is a Domain Controller."
}

$Results = $Null
}
Else
{
Write-Verbose "$(Get-Date): Computer $ComputerName is offline"
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tComputer $ComputerName is offline.`nScript
cannot continue.`n`n"
Exit
}
}

If($ADForest -ne $ADDomain)


{
#get forest information so output filename can be generated
Write-Verbose "$(Get-Date): Testing to see if $($ADForest) is a valid
forest name"
If([String]::IsNullOrEmpty($ComputerName))
{
$Script:Forest = Get-ADForest -Identity $ADForest -EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a forest identified by:
$ADForest.`nScript cannot continue.`n`n"
Exit
}
}
Else
{
$Script:Forest = Get-ADForest -Identity $ADForest -Server
$ComputerName -EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a forest with the name
of $ADForest.`n`n`t`tScript cannot continue.`n`n`t`tIs $ComputerName running Active
Directory Web Services?"
Exit
}
}
Write-Verbose "$(Get-Date): $ADForest is a valid forest name"
[string]$Script:Title = "AD Inventory Report for the $ADForest Forest"
$Script:Domains = $Script:Forest.Domains | Sort-Object
$Script:ConfigNC = (Get-ADRootDSE -Server $ADForest -EA
0).ConfigurationNamingContext
}

If($ADDomain -ne "")


{
If([String]::IsNullOrEmpty($ComputerName))
{
$results = Get-ADDomain -Identity $ADDomain -EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a domain identified by:
$ADDomain.`nScript cannot continue.`n`n"
Exit
}
}
Else
{
$results = Get-ADDomain -Identity $ADDomain -Server $ComputerName
-EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a domain with the name
of $ADDomain.`n`n`t`tScript cannot continue.`n`n`t`tIs $ComputerName running Active
Directory Web Services?"
Exit
}
}
Write-Verbose "$(Get-Date): $ADDomain is a valid domain name"
$Script:Domains = $results.DNSRoot
$Script:DomainDNSRoot = $results.DNSRoot
[string]$Script:Title = "AD Inventory Report for the $Script:Domains
Domain"

$tmp = $results.Forest
#get forest info
Write-Verbose "$(Get-Date): Retrieving forest information"
If([String]::IsNullOrEmpty($ComputerName))
{
$Script:Forest = Get-ADForest -Identity $tmp -EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a forest identified by:
$tmp.`nScript cannot continue.`n`n"
Exit
}
}
Else
{
$Script:Forest = Get-ADForest -Identity $tmp -Server
$ComputerName -EA 0

If(!$?)
{
$ErrorActionPreference = $SaveEAPreference
Write-Error "`n`n`t`tCould not find a forest with the name
of $tmp.`n`n`t`tScript cannot continue.`n`n`t`tIs $ComputerName running Active
Directory Web Services?"
Exit
}
}
Write-Verbose "$(Get-Date): Found forest information for $tmp"
$Script:ConfigNC = (Get-ADRootDSE -Server $tmp -EA
0).ConfigurationNamingContext
}

#store root domain so it only has to be accessed once


[string]$Script:ForestRootDomain = $Script:Forest.RootDomain
[string]$Script:ForestName = $Script:Forest.Name
#set naming context
}
#endregion
######################START OF BUILDING REPORT

#region Forest information


Function ProcessForestInformation
{
Write-Verbose "$(Get-Date): Writing forest data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Forest Information"
}
ElseIf($Text)
{
Line 0 "/// Forest Information \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Forest Information&nbsp;&nbsp;\\\"
}

Switch ($Script:Forest.ForestMode)
{
"0" {$ForestMode = "Windows 2000"; Break}
"1" {$ForestMode = "Windows Server 2003 interim"; Break}
"2" {$ForestMode = "Windows Server 2003"; Break}
"3" {$ForestMode = "Windows Server 2008"; Break}
"4" {$ForestMode = "Windows Server 2008 R2"; Break}
"5" {$ForestMode = "Windows Server 2012"; Break}
"6" {$ForestMode = "Windows Server 2012 R2"; Break}
"7" {$ForestMode = "Windows Server 2016"; Break} #added V2.20
"Windows2000Forest" {$ForestMode = "Windows 2000"; Break}
"Windows2003InterimForest" {$ForestMode = "Windows Server 2003
interim"; Break}
"Windows2003Forest" {$ForestMode = "Windows Server 2003"; Break}
"Windows2008Forest" {$ForestMode = "Windows Server 2008"; Break}
"Windows2008R2Forest" {$ForestMode = "Windows Server 2008 R2";
Break}
"Windows2012Forest" {$ForestMode = "Windows Server 2012"; Break}
"Windows2012R2Forest" {$ForestMode = "Windows Server 2012 R2";
Break}
"WindowsThresholdForest" {$ForestMode = "Windows Server 2016 TP4";
Break}
"Windows2016Forest" {$ForestMode = "Windows Server 2016";
Break}
"UnknownForest" {$ForestMode = "Unknown Forest Mode"; Break}
Default {$ForestMode = "Unable to determine Forest
Mode: $($Script:Forest.ForestMode)"; Break}
}

$AppPartitions = $Script:Forest.ApplicationPartitions | Sort-Object


$CrossForestReferences = $Script:Forest.CrossForestReferences | Sort-Object
$SPNSuffixes = $Script:Forest.SPNSuffixes | Sort-Object
$UPNSuffixes = $Script:Forest.UPNSuffixes | Sort-Object
$Sites = $Script:Forest.Sites | Sort-Object

#added 9-oct-2016
#https://adsecurity.org/?p=81
$DirectoryServicesConfigPartition = Get-ADObject -Identity "CN=Directory
Service,CN=Windows NT,CN=Services,$Script:ConfigNC" -Partition $Script:ConfigNC
-Properties *

$TombstoneLifetime = $DirectoryServicesConfigPartition.tombstoneLifetime

If($Null -eq $TombstoneLifetime -or $TombstoneLifetime -eq 0)


{
$TombstoneLifetime = 60
}

#2.16
#move this duplicated block of code outside the output format test
If($ADDomain -ne "")
{
#2.16 don't mess with the $Script:Domains variable
#redo list of domains so forest root domain is listed first
[array]$tmpDomains = "$Script:ForestRootDomain"
[array]$tmpDomains2 = "$($Script:ForestRootDomain)"
ForEach($Domain in $Forest.Domains)
{
If($Domain -ne $Script:ForestRootDomain)
{
$tmpDomains += "$($Domain.ToString())"
$tmpDomains2 += "$($Domain.ToString())"
}
}
}
Else
{
#redo list of domains so forest root domain is listed first
[array]$tmpDomains = "$Script:ForestRootDomain"
[array]$tmpDomains2 = "$($Script:ForestRootDomain)"
ForEach($Domain in $Script:Domains)
{
If($Domain -ne $Script:ForestRootDomain)
{
$tmpDomains += "$($Domain.ToString())"
$tmpDomains2 += "$($Domain.ToString())"
}
}

$Script:Domains = $tmpDomains
}

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Forest mode"; Value = $ForestMode; }
$ScriptInformation += @{ Data = "Forest name"; Value =
$Script:Forest.Name; }
#V2.20 reorder to alpha order
$tmp = ""
If($Null -eq $AppPartitions)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "Application partitions"; Value =
$tmp; }
}
Else
{
$cnt = 0
ForEach($AppPartition in $AppPartitions)
{
$cnt++
$tmp = "$($AppPartition.ToString())"

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Application
partitions"; Value = $tmp; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value = $tmp; }
}
}
}
$tmp = ""
If($Null -eq $CrossForestReferences)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "Cross forest references"; Value
= $tmp; }
}
Else
{
$cnt = 0
ForEach($CrossForestReference in $CrossForestReferences)
{
$cnt++
$tmp = "$($CrossForestReference.ToString())"

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Cross forest
references"; Value = $tmp; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value = $tmp; }
}
}
}
$ScriptInformation += @{ Data = "Domain naming master"; Value =
$Script:Forest.DomainNamingMaster; }
$tmp = ""
If($Null -eq $Script:Domains)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "Domains in forest"; Value =
$tmp; }
}
Else
{
$cnt = 0
ForEach($Domain in $tmpDomains2)
{
$cnt++
$tmp = "$($Domain.ToString())"

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Domains in forest";
Value = $tmp; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value = $tmp; }
}
}
}
$ScriptInformation += @{ Data = "Partitions container"; Value =
$Script:Forest.PartitionsContainer; }
$ScriptInformation += @{ Data = "Root domain"; Value =
$Script:ForestRootDomain; }
$ScriptInformation += @{ Data = "Schema master"; Value =
$Script:Forest.SchemaMaster; }
$tmp = ""
If($Null -eq $Sites)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "Sites"; Value = $tmp; }
}
Else
{
$cnt = 0
ForEach($Site in $Sites)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Sites"; Value =
$Site.ToString(); }
}
Else
{
$ScriptInformation += @{ Data = ""; Value =
$Site.ToString(); }
}
}
}
$tmp = ""
If($Null -eq $SPNSuffixes)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "SPN suffixes"; Value = $tmp; }
}
Else
{
$cnt = 0
ForEach($SPNSuffix in $SPNSuffixes)
{
$cnt++
$tmp = "$($SPNSuffix.ToString())"

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "SPN suffixes"; Value
= $tmp; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value = $tmp; }
}
}
}
$ScriptInformation += @{ Data = "Tombstone lifetime"; Value = "$
($TombstoneLifetime) days"; }
$tmp = ""
If($Null -eq $UPNSuffixes)
{
$tmp = "<None>"
$ScriptInformation += @{ Data = "UPN suffixes"; Value = $tmp; }
}
Else
{
$cnt = 0
ForEach($UPNSuffix in $UPNSuffixes)
{
$cnt++
$tmp = "$($UPNSuffix.ToString())"

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "UPN suffixes"; Value
= $tmp; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value = $tmp; }
}
}
}
$tmp = $Null

Write-Verbose "$(Get-Date): `t`tCreate Forest Word table"


$Table = AddWordTable -Hashtable $ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 125;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 "Forest mode`t`t: " $ForestMode
Line 0 "Forest name`t`t: " $Script:Forest.Name
#V2.20 reorder to alpha order
If($Null -eq $AppPartitions)
{
Line 0 "Application partitions`t: <None>"
}
Else
{
Line 0 "Application partitions`t: " -NoNewLine
$cnt = 0
ForEach($AppPartition in $AppPartitions)
{
$cnt++

If($cnt -eq 1)
{
Line 0 "$($AppPartition.ToString())"
}
Else
{
Line 3 " $($AppPartition.ToString())"
}
}
}
If($Null -eq $CrossForestReferences)
{
Line 0 "Cross forest references`t: <None>"
}
Else
{
Line 0 "Cross forest references`t: " -NoNewLine
$cnt = 0
ForEach($CrossForestReference in $CrossForestReferences)
{
$cnt++

If($cnt -eq 1)
{
Line 0 "$($CrossForestReference.ToString())"
}
Else
{
Line 3 " $($CrossForestReference.ToString())"
}
}
}
Line 0 "Domain naming master`t: " $Script:Forest.DomainNamingMaster
If($Null -eq $Script:Domains)
{
Line 0 "Domains in forest`t: <None>"
}
Else
{
Line 0 "Domains in forest`t: " -NoNewLine
$cnt = 0

ForEach($Domain in $tmpDomains2)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Domain
}
Else
{
Line 3 " " $Domain
}
}
}
Line 0 "Partitions container`t: " $Script:Forest.PartitionsContainer
Line 0 "Root domain`t`t: " $Script:ForestRootDomain
Line 0 "Schema master`t`t: " $Script:Forest.SchemaMaster
If($Null -eq $Sites)
{
Line 0 "Sites`t`t`t: <None>"
}
Else
{
Line 0 "Sites`t`t`t: " -NoNewLine
$cnt = 0
ForEach($Site in $Sites)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Site.ToString()
}
Else
{
Line 3 " $($Site.ToString())"
}
}
}
If($Null -eq $SPNSuffixes)
{
Line 0 "SPN suffixes`t`t: <None>"
}
Else
{
Line 0 "SPN suffixes`t`t: " -NoNewLine
$cnt = 0
ForEach($SPNSuffix in $SPNSuffixes)
{
$cnt++

If($cnt -eq 1)
{
Line 0 "$($SPNSuffix.ToString())"
}
Else
{
Line 3 " $($SPNSuffix.ToString())"
}
}
}
Line 0 "Tombstone lifetime`t: " "$($TombstoneLifetime) days"
If($Null -eq $UPNSuffixes)
{
Line 0 "UPN Suffixes`t`t: <None>"
}
Else
{
Line 0 "UPN Suffixes`t`t: " -NoNewLine
$cnt = 0
ForEach($UPNSuffix in $UPNSuffixes)
{
$cnt++

If($cnt -eq 1)
{
Line 0 "$($UPNSuffix.ToString())"
}
Else
{
Line 3 " $($UPNSuffix.ToString())"
}
}
}
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Forest mode",($htmlsilver -bor $htmlbold),
$ForestMode,$htmlwhite)
$rowdata += @(,('Forest name',($htmlsilver -bor $htmlbold),
$Script:Forest.Name,$htmlwhite))
$rowdata += @(,('Root domain',($htmlsilver -bor $htmlbold),
$Script:ForestRootDomain,$htmlwhite))
#V2.20 reorder to alpha order
$tmp = ""
If($Null -eq $AppPartitions)
{
$tmp = "None"

$rowdata += @(,('Application partitions',($htmlsilver -bor


$htmlbold),$tmp,$htmlwhite))
}
Else
{
$cnt = 0
ForEach($AppPartition in $AppPartitions)
{
$cnt++
$tmp = "$($AppPartition.ToString())"

If($cnt -eq 1)
{
$rowdata += @(,('Application partitions',($htmlsilver
-bor $htmlbold),$tmp,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$tmp = ""
If($Null -eq $CrossForestReferences)
{
$tmp = "None"
$rowdata += @(,('Cross forest references',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
}
Else
{
$cnt = 0
ForEach($CrossForestReference in $CrossForestReferences)
{
$cnt++
$tmp = "$($CrossForestReference.ToString())"

If($cnt -eq 1)
{
$rowdata += @(,('Cross forest references',
($htmlsilver -bor $htmlbold),$tmp,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$rowdata += @(,('Domain naming master',($htmlsilver -bor $htmlbold),
$Script:Forest.DomainNamingMaster,$htmlwhite))
$tmp = ""
If($Null -eq $Script:Domains)
{
$tmp = "None"
$rowdata += @(,('Domains in forest',($htmlsilver -bor $htmlbold),
$tmp,$htmlwhite))
}
Else
{
$cnt = 0
ForEach($Domain in $tmpDomains2)
{
$cnt++
$tmp = "$($Domain.ToString())"

If($cnt -eq 1)
{
$rowdata += @(,('Domains in forest',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$rowdata += @(,('Partitions container',($htmlsilver -bor $htmlbold),
$Script:Forest.PartitionsContainer,$htmlwhite))
$rowdata += @(,('Schema master',($htmlsilver -bor $htmlbold),
$Script:Forest.SchemaMaster,$htmlwhite))
$tmp = ""
If($Null -eq $Sites)
{
$tmp = "None"
$rowdata += @(,('Sites',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
Else
{
$cnt = 0
ForEach($Site in $Sites)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Sites',($htmlsilver -bor $htmlbold),
$Site.ToString(),$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),
$Site.ToString(),$htmlwhite))
}
}
}
$tmp = ""
If($Null -eq $SPNSuffixes)
{
$tmp = "None"
$rowdata += @(,('SPN suffixes',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
Else
{
$cnt = 0
ForEach($SPNSuffix in $SPNSuffixes)
{
$cnt++
$tmp = "$($SPNSuffix.ToString())"

If($cnt -eq 1)
{
$rowdata += @(,('SPN suffixes',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$rowdata += @(,('Tombstone lifetime',($htmlsilver -bor $htmlbold),"$
($TombstoneLifetime) days",$htmlwhite))
$tmp = ""
If($Null -eq $UPNSuffixes)
{
$tmp = "None"
$rowdata += @(,('UPN suffixes',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
Else
{
$cnt = 0
ForEach($UPNSuffix in $UPNSuffixes)
{
$cnt++
$tmp = "$($UPNSuffix.ToString())"

If($cnt -eq 1)
{
$rowdata += @(,('UPN suffixes',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
}
}
}
$tmp = $Null

$msg = ""
$columnWidths = @("125","300")
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "425"
WriteHTMLLine 0 0 " "
}
}
#endregion

#region get all DCs in the forest


Function ProcessAllDCsInTheForest
{
Write-Verbose "$(Get-Date): `tDomain controllers"

$txt = "Domain Controllers"


If($MSWORD -or $PDF)
{
WriteWordLine 3 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 $txt
}
#http://www.superedge.net/2012/09/how-to-get-ad-forest-in-powershell.html
#http://msdn.microsoft.com/en-
us/library/vstudio/system.directoryservices.activedirectory.forest.getforest
%28v=vs.90%29
#$ADContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext("forest", $ADForest)
#2.16 change
$ADContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext("forest",
$Script:Forest.Name)
$Forest2 =
[system.directoryservices.activedirectory.Forest]::GetForest($ADContext)
Write-Verbose "$(Get-Date): `t`tBuild list of Domain controllers in the
Forest"
$AllDCs = $Forest2.domains | ForEach-Object {$_.DomainControllers} | ForEach-
Object {$_.Name}
Write-Verbose "$(Get-Date): `t`tSort list of all Domain controllers"
$AllDCs = $AllDCs | Sort-Object
$ADContext = $Null
$Forest2 = $Null

If($Null -eq $AllDCs)


{
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "<None>"
}
ElseIf($Text)
{
Line 0 "<None>"
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "None"
}
}
Else
{
If($MSWORD -or $PDF)
{
[System.Collections.Hashtable[]] $WordTableRowHash = @();
ForEach($DC in $AllDCs)
{
Write-Verbose "$(Get-Date): `t`t`t$DC"
$DCName = $DC.SubString(0,$DC.IndexOf("."))
$SrvName = $DC.SubString($DC.IndexOf(".")+1)

$Results = Get-ADDomainController -Identity $DCName -Server


$SrvName -EA 0

If($? -and $Null -ne $Results)


{
$GC = $Results.IsGlobalCatalog.ToString()
$ReadOnly = $Results.IsReadOnly.ToString()
#ServerOS and ServerCore added in V2.20
$ServerOS = $Results.OperatingSystem
#https://blogs.msmvps.com/russel/2017/03/16/how-to-
tell-if-youre-running-on-windows-server-core/
$tmp = Get-RegistryValue
"HKLM:\software\microsoft\windows nt\currentversion" "installationtype" $DCName
If($tmp -eq "Server Core")
{
$ServerCore = "Yes"
}
Else
{
$ServerCore = "No"
}
}
Else
{
$GC = "Unable to retrieve status"
$ReadOnly = "Unable to retrieve status"
$ServerOS = "Unable to retrieve status"
$ServerCore = "Unable to retrieve status"
}

$WordTableRowHash += @{
DCName = $DC;
GC = $GC;
ReadOnly = $ReadOnly;
ServerOS = $ServerOS;
ServerCore = $ServerCore
}

$Results = $Null
}
}
ElseIf($Text)
{
#V2.16 addition
[int]$MaxDCNameLength = ($AllDCs | Measure-Object -Maximum
-Property Length).Maximum

If($MaxDCNameLength -gt 4) #4 is length of "Name"


{
#2 is to allow for spacing between columns
Line 1 ("Name" + (' ' * ($MaxDCNameLength - 2))) -NoNewLine
Line 0 "Global Catalog Read-only Server OS
Server Core"
Line 1 ('=' * $MaxDCNameLength) -NoNewLine
Line 0
"==================================================================================
============="
}
Else
{
Line 1 "Name Global Catalog Read-only Server OS
Server Core"
Line 1
"============================================================================"
}

ForEach($DC in $AllDCs)
{
Write-Verbose "$(Get-Date): `t`t`t$DC"
$DCName = $DC.SubString(0,$DC.IndexOf("."))
$SrvName = $DC.SubString($DC.IndexOf(".")+1)
$Results = Get-ADDomainController -Identity $DCName -Server
$SrvName -EA 0

#V2.16 change
If($? -and $Null -ne $Results)
{
$xGC = $Results.IsGlobalCatalog.ToString()
$xRO = $Results.IsReadOnly.ToString()
#ServerOS and ServerCore added in V2.20
$ServerOS = $Results.OperatingSystem
#https://blogs.msmvps.com/russel/2017/03/16/how-to-
tell-if-youre-running-on-windows-server-core/
$tmp = Get-RegistryValue
"HKLM:\software\microsoft\windows nt\currentversion" "installationtype" $DCName
If($tmp -eq "Server Core")
{
$ServerCore = "Yes"
}
Else
{
$ServerCore = "No"
}
}
Else
{
$xGC = "Unable to retrieve status"
$xRO = "Unable to retrieve status"
$ServerOS = "Unable to retrieve"
$ServerCore = "N/A"
}

If(($DC).Length -lt ($MaxDCNameLength))


{
[int]$NumOfSpaces = ($MaxDCNameLength * -1)
}
Else
{
[int]$NumOfSpaces = -4
}
Line 1 ( "{0,$NumOfSpaces} {1,-15} {2,-10} {3,-31} {4,-3}"
-f $DC,$xGC,$xRO,$ServerOS,$ServerCore)

$Results = $Null
}
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()

ForEach($DC in $AllDCs)
{
Write-Verbose "$(Get-Date): `t`t`t$DC"
$DCName = $DC.SubString(0,$DC.IndexOf("."))
$SrvName = $DC.SubString($DC.IndexOf(".")+1)

$Results = Get-ADDomainController -Identity $DCName -Server


$SrvName -EA 0
If($? -and $Null -ne $Results)
{
$GC = $Results.IsGlobalCatalog.ToString()
$ReadOnly = $Results.IsReadOnly.ToString()
#ServerOS and ServerCore added in V2.20
$ServerOS = $Results.OperatingSystem
#https://blogs.msmvps.com/russel/2017/03/16/how-to-
tell-if-youre-running-on-windows-server-core/
$tmp = Get-RegistryValue
"HKLM:\software\microsoft\windows nt\currentversion" "installationtype" $DCName
If($tmp -eq "Server Core")
{
$ServerCore = "Yes"
}
Else
{
$ServerCore = "No"
}
}
Else
{
$GC = "Unable to retrieve status"
$ReadOnly = "Unable to retrieve status"
$ServerOS = "Unable to retrieve status"
$ServerCore = "Unable to retrieve status"
}

$rowdata += @(,($DC,$htmlwhite,
$GC,$htmlwhite,
$ReadOnly,$htmlwhite,
$ServerOS,$htmlwhite,
$ServerCore,$htmlwhite
))
}
}
}
If($MSWord -or $PDF)
{
Write-Verbose "$(Get-Date): `t`tCreate Domain Controller in Forest Word
table"
$Table = AddWordTable -Hashtable $WordTableRowHash `
-Columns DCName, GC, ReadOnly, ServerOS, ServerCore `
-Headers "Name", "Global Catalog", "Read-only", "Server OS", "Server
Core" `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 50;
$Table.Columns.Item(3).Width = 50;
$Table.Columns.Item(4).Width = 130;
$Table.Columns.Item(5).Width = 45;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
#nothing to do
}
ElseIf($HTML)
{
$columnHeaders = @('Name',($htmlsilver -bor $htmlbold),
'Global Catalog',($htmlsilver -bor
$htmlbold),
'Read-only',($htmlsilver -bor $htmlbold),
'Server OS',($htmlsilver -bor $htmlbold),
'Server Core',($htmlsilver -bor
$htmlbold))
$msg = ""
FormatHTMLTable $msg "auto" -rowArray $rowdata -columnArray
$columnHeaders
WriteHTMLLine 0 0 " "
}
$AllDCs = $Null
}
#endregion

#region process CA information


Function ProcessCAInformation
{
Write-Verbose "$(Get-Date): `tCA Information"

$txt = "Certificate Authority Information"


If($MSWORD -or $PDF)
{
WriteWordLine 3 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 $txt
}

$txt = "Certification Authority Root(s)"


If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt
}

$rootDSE = [ADSI]"LDAP://RootDSE"
$configNC = $rootDSE.Properties[ 'configurationNamingContext' ].Value -as
[String]

$rootCA = 'CN=Certification Authorities,CN=Public Key Services,CN=Services,'


+ $configNC
$rootObj = [ADSI] ( 'LDAP://' + $rootCA )
$RootCnt = 0
$AllCnt = 0

If($Null -ne $rootObj)


{
ForEach($obj in $rootObj.psbase.children)
{
$RootCnt++
If($MSWORD -or $PDF)
{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Common name"; Value =
$obj.cn; }
$ScriptInformation += @{ Data = "Distinguished name"; Value
= $obj.distinguishedName; }
Write-Verbose "$(Get-Date): `t`tCreate Certification
Authority Root(s) Word table"
$Table = AddWordTable -Hashtable $ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells


-Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 125;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 1 "Common name`t`t: " $obj.cn
Line 1 "Distinguished name`t: " $obj.distinguishedName
Line 1 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Common name",($htmlsilver -bor
$htmlbold),$obj.cn,$htmlwhite)
$rowdata += @(,('Distinguished name',($htmlsilver -bor
$htmlbold),$obj.distinguishedName,$htmlwhite))
$msg = ""
$columnWidths = @("125","400")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "525"
WriteHTMLLine 0 0 " "
}
}

If($RootCnt -gt 1)
{
$txt = "Possible error: There are more than one Certification
Authority Root(s)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
WriteHTMLLine 0 0 ""
}
}
}
#ElseIf($Null -eq $rootObj) changed in V2.22 by Michael B. Smith
If($RootCnt -eq 0 -or $Null -eq $rootObj)
{
$txt = "No Certification Authority Root(s) were retrieved"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt "" $Null 0 $False $True
WriteHTMLLine 0 0 " "
}
}

$txt = "Certification Authority Issuer(s)"


If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt
}

$allCA = 'CN=Enrollment Services,CN=Public Key Services,CN=Services,' +


$configNC
$allObj = [ADSI] ( 'LDAP://' + $allCA )

If([string]::isnullorempty($allObj.psbase.children) -and !
([string]::isnullorempty($rootObj.psbase.children)))
{
#uh oh error
$txt = "Error: Certification Authority Root(s) exist, but no
Certification Authority Issuers(s) (also known as Enrollment Agents) exist"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
WriteHTMLLine 0 0 ""
}
}
ElseIf(!([string]::isnullorempty($allObj.psbase.children)) -and !
([string]::isnullorempty($rootObj.psbase.children)))
{
$AllCnt = 0
ForEach($obj in $allObj.psbase.children)
{
$AllCnt++
If($MSWORD -or $PDF)
{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Common name"; Value =
$obj.cn; }
$ScriptInformation += @{ Data = "Distinguished name"; Value
= $obj.distinguishedName; }
Write-Verbose "$(Get-Date): `t`tCreate CA Authorities Word
table"
$Table = AddWordTable -Hashtable $ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells


-Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 125;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 1 "Common name`t`t: " $obj.cn
Line 1 "Distinguished name`t: " $obj.distinguishedName
Line 1 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Common name",($htmlsilver -bor
$htmlbold),$obj.cn,$htmlwhite)
$rowdata += @(,('Distinguished name',($htmlsilver -bor
$htmlbold),$obj.distinguishedName,$htmlwhite))
$msg = ""
$columnWidths = @("125","400")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "525"
WriteHTMLLine 0 0 " "
}
}
If($AllCnt -lt $RootCnt)
{
$txt = "Error: More Certification Authority Root(s) exist than
there are Certification Authority Issuers(s) (also known as Enrollment Agents)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
WriteHTMLLine 0 0 ""
}
}
}
ElseIf(([string]::isnullorempty($allObj.psbase.children)) -and
([string]::isnullorempty($rootObj.psbase.children)))
{
$txt = "No Certification Authority Issuer(s) were retrieved"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt "" $Null 0 $False $True
WriteHTMLLine 0 0 " "
}
}

#if you have enrollment authorities and no roots – that’s a BIG error
If($AllCnt -gt 0 -and $RootCnt -eq 0)
{
$txt = "Error: Certification Authority Issuers(s) (also known as
Enrollment Agents) exist, but no Certification Authority Root(s) exist"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
WriteHTMLLine 0 0 ""
}
}

}
#endregion

#region process ad optional features


Function ProcessADOptionalFeatures
{
Write-Verbose "$(Get-Date): `tAD Optional Features"

$txt = "AD Optional Features"


If($MSWORD -or $PDF)
{
WriteWordLine 3 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 $txt
}

$ADOptionalFeatures = Get-ADOptionalFeature -Filter * -EA 0

If($? -and $Null -ne $ADOptionalFeatures)


{
ForEach($Item in $ADOptionalFeatures)
{
$Enabled = "No"
If($Item.EnabledScopes.Count -gt 0)
{
$Enabled = "Yes"
$EnabledScopes = $Item.EnabledScopes | Sort-Object
}

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Feature name"; Value =
$Item.Name; }
$ScriptInformation += @{ Data = "Enabled"; Value =
$Enabled; }

If($Enabled -eq "Yes")


{

$cnt = 0
ForEach($Scope in $EnabledScopes)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Enabled
Scopes"; Value = $Scope; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= $($Scope); }
}
}
}
Write-Verbose "$(Get-Date): `t`tCreate AD Optional Features
Word table"
$Table = AddWordTable -Hashtable $ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells


-Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 125;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 1 "Feature Name`t: " $Item.Name
Line 1 "Enabled`t`t: " $Enabled

If($Enabled -eq "Yes")


{
Line 1 "Enabled Scopes`t: " -NoNewLine

$cnt = 0
ForEach($Scope in $EnabledScopes)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Scope
}
Else
{
Line 3 " $($Scope)"
}
}
Line 0 ""
}
Else
{
Line 0 ""
}
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Feature Name",($htmlsilver -bor
$htmlbold),$Item.Name,$htmlwhite)
$rowdata += @(,('Enabled',($htmlsilver -bor $htmlbold),
$Enabled,$htmlwhite))

If($Enabled -eq "Yes")


{

$cnt = 0
ForEach($Scope in $EnabledScopes)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Enabled Scopes',
($htmlsilver -bor $htmlbold),$Scope,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),$Scope,$htmlwhite))
}
}
}
$msg = ""
$columnWidths = @("125","400")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "525"
WriteHTMLLine 0 0 " "
}
}
}
ElseIf($? -and $Null -eq $ADOptionalFeatures)
{
$txt = "No AD Optional Features were retrieved"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt "" $Null 0 $False $True
WriteHTMLLine 0 0 " "
}
}
Else
{
$txt = "Error retieving AD Optional Features"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $txt
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt "" $Null 0 $False $True
WriteHTMLLine 0 0 " "
}
}
}
#endregion

#region process ad schema items

#new for 2.16


Function ProcessADSchemaItems
{
Param(
[String []] $Name = @(
'User-Account-Control', #Flags that control the behavior of a user
account
'msNPAllowDialin', #RAS Server
'ms-Mcs-AdmPwd', #LAPS
'ms-Mcs-AdmPwdExpirationTime', #LAPS
'ms-SMS-Assignment-Site-Code', #SCCM
'ms-SMS-Capabilities', #SCCM
'msRTCSIP-UserRoutingGroupId', #Lync/SfB
'msRTCSIP-MirrorBackEndServer', #Lync/SfB
'ms-exch-schema-version-pt' #Exchange
)
)

Write-Verbose "$(Get-Date): `tAD Schema Items"

$txt = "AD Schema Items"


$txt1 = "Just because a schema extension is Present does not mean it is in
use."
If($MSWORD -or $PDF)
{
WriteWordLine 3 0 $txt
WriteWordLine 0 0 $txt1 "" $Null 8 $False $True
}
ElseIf($Text)
{
Line 0 $txt
Line 0 $txt1
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 $txt
WriteHTMLLine 0 0 $txt1 "" "Calibri" 1
}

$rootDS = [ADSI] 'LDAP://RootDSE'


$schemaNC = $rootDS.schemaNamingContext.Item( 0 )

$SchemaItems = New-Object System.Collections.ArrayList


ForEach( $item in $Name )
{
Write-Verbose "$(Get-Date): `t`tChecking for $item declared in schema"

$objDN = 'LDAP://' + 'CN=' + $item + ',' + $schemaNC

$obj = [ADSI] $objDN


$mem = Get-Member -Name name -InputObject $obj

$Itemobj = New-Object -TypeName PSObject

Switch ($item)
{
'User-Account-Control' {$tmp = "Flags that control
the behavior of a user account"}
'msNPAllowDialin' {$tmp = "RAS Server"}
'ms-Mcs-AdmPwd' {$tmp = "LAPS"}
'ms-Mcs-AdmPwdExpirationTime' {$tmp = "LAPS"}
'ms-SMS-Assignment-Site-Code' {$tmp = "SCCM"}
'ms-SMS-Capabilities' {$tmp = "SCCM"}
'msRTCSIP-UserRoutingGroupId' {$tmp = "Lync/Skype for Business"}
'msRTCSIP-MirrorBackEndServer' {$tmp = "Lync/Skype for
Business"}
'ms-exch-schema-version-pt' {$tmp = "Exchange"}
Default {$tmp = "Unknown"}
}

If( $mem )
{
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemName
-Value $item
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemState
-Value "Present"
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemDesc
-Value $tmp
$SchemaItems.Add($Itemobj) > $Null
}
Else
{
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemName
-Value $item
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemState
-Value "Not Present"
$Itemobj | Add-Member -MemberType NoteProperty -Name ItemDesc
-Value $tmp
$SchemaItems.Add($Itemobj) > $Null
}
$mem = $null
$obj = $null
}

If($MSWORD -or $PDF)


{
$TableRange = $doc.Application.Selection.Range
[int]$Columns = 3
[int]$Rows = $SchemaItems.Count + 1
[int]$xRow = 1

$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)


$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat = $wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Schema item name"

$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Present"

$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Used for"

}
ElseIf($Text)
{
#V2.16 change
Line 1 "Schema item name Present Used for
"
Line 1
"==================================================================================
============"
}
ElseIf($HTML)
{
$rowdata = @()
}

ForEach($item in $SchemaItems)
{
If($MSWORD -or $PDF)
{
$xRow++
If($xRow % 2 -eq 0)
{
$Table.Cell($xRow,1).Shading.BackgroundPatternColor =
$wdColorGray05
$Table.Cell($xRow,2).Shading.BackgroundPatternColor =
$wdColorGray05
$Table.Cell($xRow,3).Shading.BackgroundPatternColor =
$wdColorGray05
}
$Table.Cell($xRow,1).Range.Text = $Item.ItemName
$Table.Cell($xRow,2).Range.Text = $Item.ItemState
$Table.Cell($xRow,3).Range.Text = $Item.ItemDesc
}
ElseIf($Text)
{
#V2.16 change
Line 1 ( "{0,-30} {1,-11} {2,-50}" -f $Item.ItemName,
$Item.ItemState,$Item.ItemDesc)
}
ElseIf($HTML)
{
$rowdata += @(,(
$Item.ItemName,$htmlwhite,
$Item.ItemState,$htmlwhite,
$Item.ItemDesc,$htmlwhite))
}
}

If($MSWORD -or $PDF)


{
#set column widths
$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 175; Break}
2 {$xcol.width = 75; Break}
3 {$xcol.width = 175; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$selection.EndKey($wdStory,$wdMove) | Out-Null
WriteWordLine 0 0 ""
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
#V2.16 change
Line 0 ""
}
ElseIf($HTML)
{
$columnHeaders = @('Schema item name',($htmlsilver -bor $htmlbold),
'Present',($htmlsilver -bor $htmlbold),
'Used for',($htmlsilver -bor $htmlbold)
)
$msg = ""
$columnWidths = @("175","75","175")
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "475"
WriteHTMLLine 0 0 " "
}

$rootDS = $null
$schemaNC = $null
$objDN = $null
$SchemaItems = $null
}
#endregion

#region Site information


Function ProcessSiteInformation
{
Write-Verbose "$(Get-Date): Writing sites and services data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Sites and Services"
}
ElseIf($Text)
{
Line 0 "/// Sites and Services \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Sites and Services&nbsp;&nbsp;\\\"
}

#get site information


#some of the following was taken from
#http://blogs.msdn.com/b/adpowershell/archive/2009/08/18/active-directory-
powershell-to-manage-sites-and-subnets-part-3-getting-site-and-subnets.aspx

$tmp = $Script:Forest.PartitionsContainer
$ConfigurationBase = $tmp.SubString($tmp.IndexOf(",") + 1)
$Sites = $Null
$Sites = Get-ADObject -Filter 'ObjectClass -eq "site"' -SearchBase
$ConfigurationBase -Properties Name, SiteObjectBl -Server $ADForest -EA 0 | Sort-
Object Name

$siteContainerDN = ("CN=Sites," + $Script:ConfigNC)

If($? -and $Null -ne $Sites)


{
Write-Verbose "$(Get-Date): `tProcessing Inter-Site Transports"
$AllSiteLinks = Get-ADObject -Searchbase $Script:ConfigNC -Server
$ADForest `
-Filter 'objectClass -eq "siteLink"' -Property Description, Options,
Cost, ReplInterval, SiteList, Schedule -EA 0 `
| Select-Object Name, Description,
@{Name="SiteCount";Expression={$_.SiteList.Count}}, Cost, ReplInterval, `
@{Name="Schedule";Expression={If($_.Schedule){If(($_.Schedule -Join "
").Contains("240")){"NonDefault"}Else{"24x7"}}Else{"24x7"}}}, `
Options, SiteList, DistinguishedName

If($MSWORD -or $PDF)


{
WriteWordLine 2 0 "Inter-Site Transports"
#adapted from code provided by Goatee PFE

#http://blogs.technet.com/b/ashleymcglone/archive/2011/06/29/report-and-edit-
ad-site-links-from-powershell-turbo-your-ad-replication.aspx
# Report of all site links and related settings

If($? -and $Null -ne $AllSiteLinks)


{
ForEach($SiteLink in $AllSiteLinks)
{
Write-Verbose "$(Get-Date): `t`tProcessing site link
$($SiteLink.Name)"
[System.Collections.Hashtable[]] $ScriptInformation =
@()
$SiteLinkTypeDN = @()
$SiteLinkTypeDN =
$SiteLink.DistinguishedName.Split(",")
$SiteLinkType = $SiteLinkTypeDN[1].SubString(3)
$SitesInLink = New-Object
System.Collections.ArrayList
$SiteLinkSiteList = $SiteLink.SiteList
ForEach($xSite in $SiteLinkSiteList)
{
$tmp = $xSite.Split(",")
$SitesInLink.Add("$($tmp[0].SubString(3))") >
$Null
}

$ScriptInformation += @{ Data = "Name"; Value =


$SiteLink.Name; }
If(![String]::IsNullOrEmpty($SiteLink.Description))
{
$ScriptInformation += @{ Data = "Description";
Value = $SiteLink.Description; }
}
If($SitesInLink -ne "")
{
$cnt = 0
ForEach($xSite in $SitesInLink)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data =
"Sites in Link"; Value = $xSite; }
}
Else
{
$ScriptInformation += @{ Data = "";
Value = $xSite; }
}
}
}
Else
{
$ScriptInformation += @{ Data = "Sites in
Link"; Value = <None>; }
}
$ScriptInformation += @{ Data = "Cost"; Value =
$SiteLink.Cost.ToString(); }
$ScriptInformation += @{ Data = "Replication
Interval"; Value = $SiteLink.ReplInterval.ToString(); }
$ScriptInformation += @{ Data = "Schedule"; Value =
$SiteLink.Schedule; }

#https://msdn.microsoft.com/en-
us/library/cc223552.aspx
$tmp = ""
If([String]::IsNullOrEmpty($SiteLink.Options) -or
$SiteLink.Options -eq "0")
{
$tmp = "Change Notification is Disabled"
}
ElseIf($SiteLink.Options -eq "1")
{
$tmp = "Change Notification is Enabled with
Compression"
}
ElseIf($SiteLink.Options -eq "2")
{
$tmp = "Force sync in opposite direction at end
of sync"
}
ElseIf($SiteLink.Options -eq "3")
{
$tmp = "Change Notification is Enabled with
Compression and Force sync in opposite direction at end of sync"
}
ElseIf($SiteLink.Options -eq "4")
{
$tmp = "Disable compression of Change
Notification messages"
}
ElseIf($SiteLink.Options -eq "5")
{
$tmp = "Change Notification is Enabled without
Compression"
}
ElseIf($SiteLink.Options -eq "6")
{
$tmp = "Force sync in opposite direction at end
of sync and Disable compression of Change Notification messages"
}
ElseIf($SiteLink.Options -eq "7")
{
$tmp = "Change Notification is Enabled without
Compression and Force sync in opposite direction at end of sync"
}
Else
{
$tmp = "Unknown"
}
$ScriptInformation += @{ Data = "Options"; Value =
$tmp; }
$ScriptInformation += @{ Data = "Type"; Value =
$SiteLinkType; }

Write-Verbose "$(Get-Date): `t`t`tCreate Site Links


Word table"
$Table = AddWordTable -Hashtable $ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitContent;

SetWordCellFormat -Collection
$Table.Columns.Item(1).Cells -Bold -BackgroundColor $wdColorGray15;

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
}
$AllSiteLinks = $Null

ForEach($Site in $Sites)
{
Write-Verbose "$(Get-Date): `tProcessing site $
($Site.Name)"
WriteWordLine 2 0 "Site: " $Site.Name

WriteWordLine 3 0 "Subnets"
Write-Verbose "$(Get-Date): `t`tProcessing subnets"
$subnetArray = New-Object -Type string[] -ArgumentList
$Site.siteObjectBL.Count
$i = 0
$SitesiteObjectBL = $Site.siteObjectBL
ForEach($subnetDN in $SitesiteObjectBL)
{
$subnetName = $subnetDN.SubString(3,
$subnetDN.IndexOf(",CN=Subnets,CN=Sites,") - 3)
$subnetArray[$i] = $subnetName
$i++
}
$subnetArray = $subnetArray | Sort-Object
If($Null -eq $subnetArray)
{
WriteWordLine 0 0 "<None>"
}
Else
{
BuildMultiColumnTable $subnetArray "Subnets"
}

Write-Verbose "$(Get-Date): `t`tProcessing servers"


WriteWordLine 3 0 "Servers"
$siteName = $Site.Name

#build array of connect objects


Write-Verbose "$(Get-Date): `t`t`tProcessing automatic
connection objects"
$Connections = New-Object System.Collections.ArrayList
$ConnnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and options -bor 1' -Searchbase $Script:ConfigNC -Property
DistinguishedName, fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value "<automatically generated>"
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections.Add($obj) > $Null
}
}

Write-Verbose "$(Get-Date): `t`t`tProcessing manual


connection objects"
$ConnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and -not options -bor 1' -Searchbase $Script:ConfigNC -Property
Name, DistinguishedName, fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value $ConnectionObject.Name
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections += $obj
}
}

If($Null -ne $Connections)


{
$Connections = $Connections | Sort-Object Name,
ToServer, FromServer
}

#list each server


$serverContainerDN = "CN=Servers,CN=" + $siteName + "," +
$siteContainerDN
$SiteServers = $Null
$SiteServers = Get-ADObject -SearchBase $serverContainerDN
-SearchScope OneLevel `
-Filter { objectClass -eq "Server" } -Properties
"DNSHostName" -Server $ADForest -EA 0 | `
Select-Object DNSHostName, Name | Sort-Object DNSHostName

If($? -and $Null -ne $SiteServers)


{
$First = $True
ForEach($SiteServer in $SiteServers)
{
If(!$First)
{
WriteWordLine 0 0 ""
}
WriteWordLine 0 0 $SiteServer.DNSHostName
#for each server list each connection object
If($Null -ne $Connections)
{
$Results = $Connections | Where-Object
{$_.ToServer -eq $SiteServer.Name}

If($? -and $Null -ne $Results)


{
WriteWordLine 0 1 "Connection
Objects to source server $($SiteServer.Name)"
[System.Collections.Hashtable[]]
$WordTableRowHash = @();
ForEach($Result in $Results)
{
$WordTableRowHash += @{
ConnectionName =
$Result.Name;
FromServer =
$Result.FromServer;
FromSite =
$Result.FromServerSite
}
}
$Table = AddWordTable -Hashtable
$WordTableRowHash `
-Columns ConnectionName,
FromServer, FromSite `
-Headers "Name", "From Server",
"From Site" `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection
$Table.Rows.Item(1).Cells -Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 200;
$Table.Columns.Item(2).Width = 100;
$Table.Columns.Item(3).Width = 100;

#indent the entire table 1 tab stop

$Table.Rows.SetLeftIndent($Indent1TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
}
Else
{
WriteWordLine 0 3 "Connection Objects: "
WriteWordLine 0 4 "<None>"
WriteWordLine 0 0 ""
}
$First = $False
}
}
ElseIf(!$?)
{
Write-Warning "No Site Servers were retrieved."
WriteWordLine 0 0 "Warning: No Site Servers were
retrieved" "" $Null 0 $False $True
WriteWordLine 0 0 ""
}
Else
{
WriteWordLine 0 0 "No servers in this site"
WriteWordLine 0 0 ""
}
}
}
ElseIf($Text)
{
Line 0 "/// Inter-Site Transports \\\"
If($? -and $Null -ne $AllSiteLinks)
{
ForEach($SiteLink in $AllSiteLinks)
{
Write-Verbose "$(Get-Date): `t`tProcessing site link
$($SiteLink.Name)"
$SiteLinkTypeDN = @()
$SiteLinkTypeDN =
$SiteLink.DistinguishedName.Split(",")
$SiteLinkType = $SiteLinkTypeDN[1].SubString(3)
$SitesInLink = New-Object
System.Collections.ArrayList
$SiteLinkSiteList = $SiteLink.SiteList
ForEach($xSite in $SiteLinkSiteList)
{
$tmp = $xSite.Split(",")
$SitesInLink.Add("$($tmp[0].SubString(3))") >
$Null
}

Line 0 "Name`t`t`t: " $SiteLink.Name


If(![String]::IsNullOrEmpty($SiteLink.Description))
{
Line 0 "Description`t`t: "
$SiteLink.Description
}
Line 0 "Sites in Link`t`t: " -NoNewLine
If($SitesInLink -ne "")
{
$cnt = 0
ForEach($xSite in $SitesInLink)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $xSite
}
Else
{
Line 3 " $($xSite)"
}
}
}
Else
{
Line 0 "<None>"
}
Line 0 "Cost`t`t`t: " $SiteLink.Cost.ToString()
Line 0 "Replication Interval`t: "
$SiteLink.ReplInterval.ToString()
Line 0 "Schedule`t`t: " $SiteLink.Schedule
Line 0 "Options`t`t`t: " -NoNewLine
#https://msdn.microsoft.com/en-
us/library/cc223552.aspx
If([String]::IsNullOrEmpty($SiteLink.Options) -or
$SiteLink.Options -eq "0")
{
Line 0 "Change Notification is Disabled"
}
ElseIf($SiteLink.Options -eq "1")
{
Line 0 "Change Notification is Enabled with
Compression"
}
ElseIf($SiteLink.Options -eq "2")
{
Line 0 "Force sync in opposite direction at end
of sync"
}
ElseIf($SiteLink.Options -eq "3")
{
Line 0 "Change Notification is Enabled with
Compression and Force sync in opposite direction at end of sync"
}
ElseIf($SiteLink.Options -eq "4")
{
Line 0 "Disable compression of Change
Notification messages"
}
ElseIf($SiteLink.Options -eq "5")
{
Line 0 "Change Notification is Enabled without
Compression"
}
ElseIf($SiteLink.Options -eq "6")
{
Line 0 "Force sync in opposite direction at end
of sync and Disable compression of Change Notification messages"
}
ElseIf($SiteLink.Options -eq "7")
{
Line 0 "Change Notification is Enabled without
Compression and Force sync in opposite direction at end of sync"
}
Else
{
Line 0 "Unknown"
}
Line 0 "Type`t`t`t: " $SiteLinkType
Line 0 ""
}
}
$AllSiteLinks = $Null

ForEach($Site in $Sites)
{
Write-Verbose "$(Get-Date): `tProcessing site $
($Site.Name)"
Line 0 "/// Site: $($Site.Name) \\\"

Line 1 "Subnets"
Write-Verbose "$(Get-Date): `t`tProcessing subnets"
$subnetArray = New-Object -Type string[] -ArgumentList
$Site.siteObjectBL.Count
$i = 0
$SitesiteObjectBL = $Site.siteObjectBL
ForEach($subnetDN in $SitesiteObjectBL)
{
$subnetName = $subnetDN.SubString(3,
$subnetDN.IndexOf(",CN=Subnets,CN=Sites,") - 3)
$subnetArray[$i] = $subnetName
$i++
}
$subnetArray = $subnetArray | Sort-Object
If($Null -eq $subnetArray)
{
Line 2 "<None>"
}
Else
{
ForEach($xSubnet in $subnetArray)
{
Line 2 $xSubnet
}
}
Line 0 ""

Write-Verbose "$(Get-Date): `t`tProcessing servers"


Line 1 "Servers"
$siteName = $Site.Name

#build array of connect objects


Write-Verbose "$(Get-Date): `t`t`tProcessing automatic
connection objects"
$Connections = New-Object System.Collections.ArrayList
$ConnnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and options -bor 1' `
-Searchbase $Script:ConfigNC -Property DistinguishedName,
fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value "<automatically generated>"
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections.Add($obj) > $Null
}
}

Write-Verbose "$(Get-Date): `t`t`tProcessing manual


connection objects"
$ConnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and -not options -bor 1' `
-Searchbase $Script:ConfigNC -Property Name,
DistinguishedName, fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value $ConnectionObject.Name
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections += $obj
}
}
If($Null -ne $Connections)
{
$Connections = $Connections | Sort-Object Name,
ToServer, FromServer
}

#list each server


$serverContainerDN = "CN=Servers,CN=" + $siteName + "," +
$siteContainerDN
$SiteServers = $Null
$SiteServers = Get-ADObject -SearchBase $serverContainerDN
-SearchScope OneLevel `
-Filter { objectClass -eq "Server" } -Properties
"DNSHostName" -Server $ADForest -EA 0 | `
Select-Object DNSHostName, Name | Sort-Object DNSHostName

If($? -and $Null -ne $SiteServers)


{
ForEach($SiteServer in $SiteServers)
{
Line 2 $SiteServer.DNSHostName
Line 0 ""
#for each server list each connection object
If($Null -ne $Connections)
{
$Results = $Connections | Where-Object
{$_.ToServer -eq $SiteServer.Name}

If($? -and $Null -ne $Results)


{
Line 2 "Connection Objects to
source server $($SiteServer.Name)"
ForEach($Result in $Results)
{
Line 3 "Name`t`t: "
$Result.Name
Line 3 "From Server`t: "
$Result.FromServer
Line 3 "From Site`t: "
$Result.FromServerSite
Line 0 ""
}
}
}
Else
{
Line 3 "Connection Objects: <None>"
}
}
}
ElseIf(!$?)
{
Write-Warning "No Site Servers were retrieved."
Line 2 "Warning: No Site Servers were retrieved"
Line 0 ""
}
Else
{
Line 2 "No servers in this site"
Line 0 ""
}
}
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;Inter-Site
Transports&nbsp;&nbsp;\\\"
#adapted from code provided by Goatee PFE

#http://blogs.technet.com/b/ashleymcglone/archive/2011/06/29/report-and-edit-
ad-site-links-from-powershell-turbo-your-ad-replication.aspx
# Report of all site links and related settings

If($? -and $Null -ne $AllSiteLinks)


{
ForEach($SiteLink in $AllSiteLinks)
{
Write-Verbose "$(Get-Date): `t`tProcessing site link
$($SiteLink.Name)"
$rowdata = @()
$SiteLinkTypeDN = @()
$SiteLinkTypeDN =
$SiteLink.DistinguishedName.Split(",")
$SiteLinkType = $SiteLinkTypeDN[1].SubString(3)
$SitesInLink = New-Object
System.Collections.ArrayList
$SiteLinkSiteList = $SiteLink.SiteList
ForEach($xSite in $SiteLinkSiteList)
{
$tmp = $xSite.Split(",")
$SitesInLink.Add("$($tmp[0].SubString(3))") >
$Null
}

$columnHeaders = @("Name",($htmlsilver -bor


$htmlbold),$SiteLink.Name,$htmlwhite)
If(![String]::IsNullOrEmpty($SiteLink.Description))
{
$rowdata += @(,('Description',($htmlsilver -bor
$htmlbold),$SiteLink.Description,$htmlwhite))
}
If($SitesInLink -ne "")
{
$cnt = 0
ForEach($xSite in $SitesInLink)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Sites in Link',
($htmlsilver -bor $htmlbold),$xSite,$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver
-bor $htmlbold),$xSite,$htmlwhite))
}
}
}
Else
{
$rowdata += @(,('Sites in Link',($htmlsilver
-bor $htmlbold),"None",$htmlwhite))
}
$rowdata += @(,('Cost',($htmlsilver -bor $htmlbold),
$SiteLink.Cost.ToString(),$htmlwhite))
$rowdata += @(,('Replication Interval',($htmlsilver
-bor $htmlbold),$SiteLink.ReplInterval.ToString(),$htmlwhite))
$rowdata += @(,('Schedule',($htmlsilver -bor
$htmlbold),$SiteLink.Schedule,$htmlwhite))

#https://msdn.microsoft.com/en-
us/library/cc223552.aspx
$tmp = ""
If([String]::IsNullOrEmpty($SiteLink.Options) -or
$SiteLink.Options -eq "0")
{
$tmp = "Change Notification is Disabled"
}
ElseIf($SiteLink.Options -eq "1")
{
$tmp = "Change Notification is Enabled with
Compression"
}
ElseIf($SiteLink.Options -eq "2")
{
$tmp = "Force sync in opposite direction at end
of sync"
}
ElseIf($SiteLink.Options -eq "3")
{
$tmp = "Change Notification is Enabled with
Compression and Force sync in opposite direction at end of sync"
}
ElseIf($SiteLink.Options -eq "4")
{
$tmp = "Disable compression of Change
Notification messages"
}
ElseIf($SiteLink.Options -eq "5")
{
$tmp = "Change Notification is Enabled without
Compression"
}
ElseIf($SiteLink.Options -eq "6")
{
$tmp = "Force sync in opposite direction at end
of sync and Disable compression of Change Notification messages"
}
ElseIf($SiteLink.Options -eq "7")
{
$tmp = "Change Notification is Enabled without
Compression and Force sync in opposite direction at end of sync"
}
Else
{
$tmp = "Unknown"
}
$rowdata += @(,('Options',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
$rowdata += @(,('Type',($htmlsilver -bor $htmlbold),
$SiteLinkType,$htmlwhite))
$msg = ""
$columnWidths = @("200","250")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "450"
WriteHTMLLine 0 0 " "
}
}
$AllSiteLinks = $Null

ForEach($Site in $Sites)
{
Write-Verbose "$(Get-Date): `tProcessing site $
($Site.Name)"
WriteHTMLLine 2 0 "///&nbsp;&nbsp;Site: $
($Site.Name)&nbsp;&nbsp;\\\"

WriteHTMLLine 3 0 "Subnets"
Write-Verbose "$(Get-Date): `t`tProcessing subnets"
$subnetArray = New-Object -Type string[] -ArgumentList
$Site.siteObjectBL.Count
$i = 0
$SitesiteObjectBL = $Site.siteObjectBL
ForEach($subnetDN in $SitesiteObjectBL)
{
$subnetName = $subnetDN.SubString(3,
$subnetDN.IndexOf(",CN=Subnets,CN=Sites,") - 3)
$subnetArray[$i] = $subnetName
$i++
}
$subnetArray = $subnetArray | Sort-Object
If($Null -eq $subnetArray)
{
WriteHTMLLine 0 0 "None"
}
Else
{
$rowdata = @()
ForEach($xSubnet in $subnetArray)
{
$rowdata += @(,($xSubnet,$htmlwhite))
}
$columnHeaders = @('Subnets',($htmlsilver -bor
$htmlbold))
$msg = ""
FormatHTMLTable $msg "auto" -rowArray $rowdata
-columnArray $columnHeaders
WriteHTMLLine 0 0 " "
}

Write-Verbose "$(Get-Date): `t`tProcessing servers"


WriteHTMLLine 3 0 "Servers"
$siteName = $Site.Name
#build array of connect objects
Write-Verbose "$(Get-Date): `t`t`tProcessing automatic
connection objects"
$Connections = New-Object System.Collections.ArrayList
$ConnnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and options -bor 1' `
-Searchbase $Script:ConfigNC -Property DistinguishedName,
fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value "<automatically generated>"
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections.Add($obj) > $Null
}
}

Write-Verbose "$(Get-Date): `t`t`tProcessing manual


connection objects"
$ConnectionObjects = $Null
$ConnectionObjects = Get-ADObject -Filter 'objectClass -eq
"nTDSConnection" -and -not options -bor 1' `
-Searchbase $Script:ConfigNC -Property Name,
DistinguishedName, fromServer -Server $ADForest -EA 0

If($? -and $Null -ne $ConnectionObjects)


{
ForEach($ConnectionObject in $ConnectionObjects)
{
$xArray =
$ConnectionObject.DistinguishedName.Split(",")
#server name is 3rd item in array (element 2)
$ToServer =
$xArray[2].SubString($xArray[2].IndexOf("=")+1) #get past the = sign
$xArray =
$ConnectionObject.FromServer.Split(",")
#server name is 2nd item in array (element 1)
$FromServer =
$xArray[1].SubString($xArray[1].IndexOf("=")+1) #get past the = sign
#site name is 4th item in array (element 3)
$FromServerSite =
$xArray[3].SubString($xArray[3].IndexOf("=")+1) #get past the = sign
$xArray = $Null
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty
-Name Name -Value $ConnectionObject.Name
$obj | Add-Member -MemberType NoteProperty
-Name ToServer -Value $ToServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServer -Value $FromServer
$obj | Add-Member -MemberType NoteProperty
-Name FromServerSite -Value $FromServerSite
$Connections += $obj
}
}

If($Null -ne $Connections)


{
$Connections = $Connections | Sort-Object Name,
ToServer, FromServer
}

#list each server


$serverContainerDN = "CN=Servers,CN=" + $siteName + "," +
$siteContainerDN
$SiteServers = $Null
$SiteServers = Get-ADObject -SearchBase $serverContainerDN
-SearchScope OneLevel `
-Filter { objectClass -eq "Server" } -Properties
"DNSHostName" -Server $ADForest -EA 0 | `
Select-Object DNSHostName, Name | Sort-Object DNSHostName

If($? -and $Null -ne $SiteServers)


{
ForEach($SiteServer in $SiteServers)
{
WriteHTMLLine 0 0 $SiteServer.DNSHostName
WriteHTMLLine 0 0 " "
#for each server list each connection object
If($Null -ne $Connections)
{
$Results = $Connections | Where-Object
{$_.ToServer -eq $SiteServer.Name}

If($? -and $Null -ne $Results)


{
$rowdata = @()
ForEach($Result in $Results)
{
#replace the <> characters
since HTML doesn't like those in data
$tmp = $Result.Name
$tmp = $tmp.Replace(">","")
$tmp = $tmp.Replace("<","")

$rowdata += @(,($tmp,
$htmlwhite,

$Result.FromServer,$htmlwhite,

$Result.FromServerSite,$htmlwhite))
}
$columnWidths =
@("175px","125px","150px")
$columnHeaders = @('Name',
($htmlsilver -bor $htmlbold),
'From
Server',($htmlsilver -bor $htmlbold),
'From
Site',($htmlsilver -bor $htmlbold))
$msg = "Connection Objects to
source server $($SiteServer.Name)"
FormatHTMLTable $msg -rowArray
$rowdata -columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "450"
WriteHTMLLine 0 0 " "
}
}
Else
{
WriteHTMLLine 0 3 "Connection Objects:
None"
}
}
}
ElseIf(!$?)
{
Write-Warning "No Site Servers were retrieved."
WriteHTMLLine 0 0 "Warning: No Site Servers were
retrieved" "" $Null 0 $False $True
}
Else
{
WriteHTMLLine 0 0 "No servers in this site"
}
}
}
}
ElseIf(!$?)
{
Write-Warning "No Sites were retrieved."
$txt = "Warning: No Sites were retrieved"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
WriteWordLine 0 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "There were no sites found to retrieve"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
WriteWordLine 0 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
}
#endregion

#region domains
Function ProcessDomains
{
Write-Verbose "$(Get-Date): Writing domain data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Domain Information"
}
ElseIf($Text)
{
Line 0 "/// Domain Information \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Domain Information&nbsp;&nbsp;\\\"
}

$Script:AllDomainControllers = New-Object System.Collections.ArrayList


$First = $True

#http://technet.microsoft.com/en-us/library/bb125224(v=exchg.150).aspx
#http://support.microsoft.com/kb/556086/he
#https://eightwone.com/references/ad-schema-versions/
#https://eightwone.com/references/schema-versions/

$SchemaVersionTable = @{
"13" = "Windows 2000";
"30" = "Windows 2003 RTM, SP1, SP2";
"31" = "Windows 2003 R2";
"44" = "Windows 2008";
"47" = "Windows 2008 R2";
"56" = "Windows Server 2012";
"69" = "Windows Server 2012 R2";
"72" = "Windows Server 2016 TP4";
"87" = "Windows Server 2016";
"88" = "Windows Server 2019"; #added V2.20, updated in 2.22
"4397" = "Exchange 2000 RTM";
"4406" = "Exchange 2000 SP3";
"6870" = "Exchange 2003 RTM, SP1, SP2";
"6936" = "Exchange 2003 SP3";
"10637" = "Exchange 2007 RTM";
"11116" = "Exchange 2007 SP1";
"14622" = "Exchange 2007 SP2, Exchange 2010 RTM";
"14625" = "Exchange 2007 SP3";
"14726" = "Exchange 2010 SP1";
"14732" = "Exchange 2010 SP2";
"14734" = "Exchange 2010 SP3";
"15137" = "Exchange 2013 RTM";
"15254" = "Exchange 2013 CU1";
"15281" = "Exchange 2013 CU2";
"15283" = "Exchange 2013 CU3";
"15292" = "Exchange 2013 SP1/CU4";
"15300" = "Exchange 2013 CU5";
"15303" = "Exchange 2013 CU6";
"15312" = "Exchange 2013 CU7 through CU 21"; #updated in 2.20
"15317" = "Exchange 2016";
"15323" = "Exchange 2016 CU1";
"15325" = "Exchange 2016 CU2";
"15326" = "Exchange 2016 CU3/CU4/CU5"; #added in 2.16
"15330" = "Exchange 2016 CU6"; #added in 2.16
"15332" = "Exchange 2016 CU7 through CU11"; #added in 2.16 and updated in
2.20, updated in 2.22
"17000" = "Exchange 2019 RTM"; #added in 2.22
}

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing domain $($Domain)"

$DomainInfo = Get-ADDomain -Identity $Domain -EA 0

If($? -and $Null -ne $DomainInfo)


{
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($Domain -eq $Script:ForestRootDomain)


{
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 "$($Domain) (Forest Root)"
}
ElseIf($Text)
{
Line 0 "/// $($Domain) (Forest Root) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($Domain) (Forest
Root)&nbsp;&nbsp;\\\"
}
}
Else
{
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $Domain
}
ElseIf($Text)
{
Line 0 "/// $($Domain) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$
($Domain)&nbsp;&nbsp;\\\"
}
}

Switch ($DomainInfo.DomainMode)
{
"0" {$DomainMode = "Windows 2000"; Break}
"1" {$DomainMode = "Windows Server 2003 mixed"; Break}
"2" {$DomainMode = "Windows Server 2003"; Break}
"3" {$DomainMode = "Windows Server 2008"; Break}
"4" {$DomainMode = "Windows Server 2008 R2"; Break}
"5" {$DomainMode = "Windows Server 2012"; Break}
"6" {$DomainMode = "Windows Server 2012 R2"; Break}
"7" {$DomainMode = "Windows Server 2016"; Break} #added
V2.20
"Windows2000Domain" {$DomainMode = "Windows
2000"; Break}
"Windows2003Mixed" {$DomainMode = "Windows
Server 2003 mixed"; Break}
"Windows2003Domain" {$DomainMode = "Windows
Server 2003"; Break}
"Windows2008Domain" {$DomainMode = "Windows
Server 2008"; Break}
"Windows2008R2Domain" {$DomainMode = "Windows
Server 2008 R2"; Break}
"Windows2012Domain" {$DomainMode = "Windows
Server 2012"; Break}
"Windows2012R2Domain" {$DomainMode = "Windows
Server 2012 R2"; Break}
"WindowsThresholdDomain" {$DomainMode = "Windows
Server 2016 TP"; Break}
"Windows2016Domain" {$DomainMode = "Windows
Server 2016"; Break}
"UnknownDomain" {$DomainMode = "Unknown
Domain Mode"; Break}
Default {$DomainMode = "Unable to
determine Domain Mode: $($DomainInfo.DomainMode)"; Break}
}

#http://blogs.technet.com/b/poshchap/archive/2014/03/07/ad-
schema-version.aspx
$ADSchemaInfo = $Null
$ExchangeSchemaInfo = $Null

$ADSchemaInfo = Get-ADObject (Get-ADRootDSE -Server $Domain -EA


0).schemaNamingContext `
-Property objectVersion -Server $Domain -EA 0

If($? -and $Null -ne $ADSchemaInfo)


{
$ADSchemaVersion = $ADSchemaInfo.objectversion
$ADSchemaVersionName =
$SchemaVersionTable.Get_Item("$ADSchemaVersion")
If($Null -eq $ADSchemaVersionName)
{
$ADSchemaVersionName = "Unknown"
}
}
Else
{
$ADSchemaVersion = "Unknown"
$ADSchemaVersionName = "Unknown"
}

If($Domain -eq $Script:ForestRootDomain)


{
$ExchangeSchemaInfo = Get-ADObject "cn=ms-exch-schema-
version-pt,cn=Schema,cn=Configuration,$($DomainInfo.DistinguishedName)" `
-properties rangeupper -Server $Domain -EA 0

If($? -and $Null -ne $ExchangeSchemaInfo)


{
$ExchangeSchemaVersion =
$ExchangeSchemaInfo.rangeupper
$ExchangeSchemaVersionName =
$SchemaVersionTable.Get_Item("$ExchangeSchemaVersion")
If($Null -eq $ExchangeSchemaVersionName)
{
$ExchangeSchemaVersionName = "Unknown"
}
}
Else
{
$ExchangeSchemaVersion = "Unknown"
$ExchangeSchemaVersionName = "Unknown"
}
}

If($Null -eq $DomainInfo.LastLogonReplicationInterval)


{
$LastLogonReplicationInterval = "Default 1 day"
}
Else
{
$LastLogonReplicationInterval =
$DomainInfo.LastLogonReplicationInterval.ToString()
}

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Domain mode"; Value =
$DomainMode; }
$ScriptInformation += @{ Data = "Domain name"; Value =
$DomainInfo.Name; }
$ScriptInformation += @{ Data = "NetBIOS name"; Value =
$DomainInfo.NetBIOSName; }
#V2.20 reorder the following properties in alpha order
$ScriptInformation += @{ Data = "AD Schema"; Value = "($
($ADSchemaVersion)) - $($ADSchemaVersionName)"; }
$DNSSuffixes = $DomainInfo.AllowedDNSSuffixes | Sort-Object

If($Null -eq $DNSSuffixes)


{
$ScriptInformation += @{ Data = "Allowed DNS
Suffixes"; Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($DNSSuffix in $DNSSuffixes)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Allowed
DNS Suffixes"; Value = "$($DNSSuffix.ToString())"; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= "$($DNSSuffix.ToString())"; }
}
}
}
$ChildDomains = $DomainInfo.ChildDomains | Sort-Object
If($Null -eq $ChildDomains)
{
$ScriptInformation += @{ Data = "Child domains";
Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($ChildDomain in $ChildDomains)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Child
domains"; Value = "$($ChildDomain.ToString())"; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= "$($ChildDomain.ToString())"; }
}
}
}
$ScriptInformation += @{ Data = "Default computers
container"; Value = $DomainInfo.ComputersContainer; }
$ScriptInformation += @{ Data = "Default users container";
Value = $DomainInfo.UsersContainer; }
$ScriptInformation += @{ Data = "Deleted objects
container"; Value = $DomainInfo.DeletedObjectsContainer; }
$ScriptInformation += @{ Data = "Distinguished name"; Value
= $DomainInfo.DistinguishedName; }
$ScriptInformation += @{ Data = "DNS root"; Value =
$DomainInfo.DNSRoot; }
$ScriptInformation += @{ Data = "Domain controllers
container"; Value = $DomainInfo.DomainControllersContainer; }
If(![String]::IsNullOrEmpty($ExchangeSchemaInfo))
{
$ScriptInformation += @{ Data = "Exchange Schema";
Value = "($($ExchangeSchemaVersion)) - $($ExchangeSchemaVersionName)"; }
}
$ScriptInformation += @{ Data = "Foreign security
principals container"; Value = $DomainInfo.ForeignSecurityPrincipalsContainer; }
$ScriptInformation += @{ Data = "Infrastructure master";
Value = $DomainInfo.InfrastructureMaster; }
#V2.20 added
$ScriptInformation += @{ Data = "Last logon replication
interval"; Value = $LastLogonReplicationInterval; }
$ScriptInformation += @{ Data = "Lost and Found container";
Value = $DomainInfo.LostAndFoundContainer; }
If(![String]::IsNullOrEmpty($DomainInfo.ManagedBy))
{
$ScriptInformation += @{ Data = "Managed by"; Value =
$DomainInfo.ManagedBy; }
}
$ScriptInformation += @{ Data = "PDC Emulator"; Value =
$DomainInfo.PDCEmulator; }
#V2.20 added
If(validObject $DomainInfo
PublicKeyRequiredPasswordRolling)
{
$ScriptInformation += @{ Data = "Public key required
password rolling"; Value = $DomainInfo.PublicKeyRequiredPasswordRolling.ToString();
}
}
$ScriptInformation += @{ Data = "Quotas container"; Value =
$DomainInfo.QuotasContainer; }
$ReadOnlyReplicas =
$DomainInfo.ReadOnlyReplicaDirectoryServers | Sort-Object
If($Null -eq $ReadOnlyReplicas)
{
$ScriptInformation += @{ Data = "Read-only replica
directory servers"; Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($ReadOnlyReplica in $ReadOnlyReplicas)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Read-
only replica directory servers"; Value = "$($ReadOnlyReplica.ToString())"; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= "$($ReadOnlyReplica.ToString())"; }
}
}
}
$Replicas = $DomainInfo.ReplicaDirectoryServers | Sort-
Object
If($Null -eq $Replicas)
{
$ScriptInformation += @{ Data = "Replica directory
servers"; Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($Replica in $Replicas)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Replica
directory servers"; Value = "$($Replica.ToString())"; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= "$($Replica.ToString())"; }
}
}
}
$ScriptInformation += @{ Data = "RID Master"; Value =
$DomainInfo.RIDMaster; }
$SubordinateReferences = $DomainInfo.SubordinateReferences
| Sort-Object
If($Null -eq $SubordinateReferences)
{
$ScriptInformation += @{ Data = "Subordinate
references"; Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($SubordinateReference in
$SubordinateReferences)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data =
"Subordinate references"; Value = "$($SubordinateReference.ToString())"; }
}
Else
{
$ScriptInformation += @{ Data = ""; Value
= "$($SubordinateReference.ToString())"; }
}
}
}
$ScriptInformation += @{ Data = "Systems container"; Value
= $DomainInfo.SystemsContainer; }

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells


-Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 175;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""

Write-Verbose "$(Get-Date): `t`tGetting domain trusts"


WriteWordLine 3 0 "Domain Trusts"

$ADDomainTrusts = $Null
$ADDomainTrusts = Get-ADObject -Filter {ObjectClass -eq
"trustedDomain"} `
-Server $Domain -Properties * -EA 0

If($? -and $Null -ne $ADDomainTrusts)


{

ForEach($Trust in $ADDomainTrusts)
{
[System.Collections.Hashtable[]]
$ScriptInformation = @()
$ScriptInformation += @{ Data = "Name"; Value =
$Trust.Name; }

If(!
[String]::IsNullOrEmpty($Trust.Description))
{
$ScriptInformation += @{ Data =
"Description"; Value = $Trust.Description; }
}

$ScriptInformation += @{ Data = "Created";


Value = $Trust.Created; }
$ScriptInformation += @{ Data = "Modified";
Value = $Trust.Modified; }

$TrustExtendedAttributes = Get-ADTrustInfo
$Trust

$ScriptInformation += @{ Data = "Type"; Value =


$TrustExtendedAttributes.TrustType; }

$cnt = 0
ForEach($attribute in
$TrustExtendedAttributes.TrustAttribute)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data =
"Attributes"; Value = $attribute.ToString(); }
}
Else
{
$ScriptInformation += @{ Data = "";
Value = "$($attribute.ToString())"; }
}
}

$ScriptInformation += @{ Data = "Direction";


Value = $TrustDirection; }

$Table = AddWordTable -Hashtable


$ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection
$Table.Columns.Item(1).Cells -Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 175;
$Table.Columns.Item(2).Width = 300;

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
}
ElseIf(!$?)
{
#error retrieving domain trusts
Write-Warning "Error retrieving domain trusts for $
($Domain)"
WriteWordLine 0 0 "Error retrieving domain trusts for
$($Domain)" "" $Null 0 $False $True
}
Else
{
#no domain trust data
WriteWordLine 0 0 "<None>"
}

Write-Verbose "$(Get-Date): `t`tProcessing domain


controllers"
$DomainControllers = $Null
$DomainControllers = Get-ADDomainController -Filter *
-Server $DomainInfo.DNSRoot -EA 0 | Sort-Object Name

If($? -and $Null -ne $DomainControllers)


{
$Script:AllDomainControllers.Add($DomainControllers)
> $Null
[System.Collections.Hashtable[]] $WordTable = @();
WriteWordLine 3 0 "Domain Controllers"
ForEach($DomainController in $DomainControllers)
{
$WordTableRowHash = @{
DCName = $DomainController.Name;
}
$WordTable += $WordTableRowHash;
}
#set column widths
$Table = AddWordTable -Hashtable $WordTable `
-Columns DCName `
-Headers "Name" `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection
$Table.Rows.Item(1).Cells -Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 105;
$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf(!$?)
{
Write-Warning "Error retrieving domain controller
data for domain $($Domain)"
WriteWordLine 0 0 "Error retrieving domain controller
data for domain $($Domain)" "" $Null 0 $False $True
}
Else
{
WriteWordLine 0 0 "No Domain controller data was
retrieved for domain $($Domain)" "" $Null 0 $False $True
}

Write-Verbose "$(Get-Date): `t`tProcessing Fine Grained


Password Policies"

#are FGPP cmdlets available


If(Get-Command -Name "Get-ADFineGrainedPasswordPolicy" -ea
0)
{
$FGPPs = $Null
$FGPPs = Get-ADFineGrainedPasswordPolicy -Searchbase
$DomainInfo.DistinguishedName -Filter * -Properties * -Server $DomainInfo.DNSRoot
-EA 0 | Sort-Object Precedence, ObjectGUID

If($? -and $Null -ne $FGPPs)


{
WriteWordLine 3 0 "Fine Grained Password
Policies"

ForEach($FGPP in $FGPPs)
{
[System.Collections.Hashtable[]]
$ScriptInformation = @()
$ScriptInformation += @{ Data = "Name";
Value = $FGPP.Name; }
$ScriptInformation += @{ Data =
"Precedence"; Value = $FGPP.Precedence.ToString(); }

If($FGPP.MinPasswordLength -eq 0)
{
$ScriptInformation += @{ Data =
"Enforce minimum password length"; Value = "Not enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Enforce minimum password length"; Value = "Enabled"; }
$ScriptInformation += @{ Data = "
Minimum password length (characters)"; Value =
$FGPP.MinPasswordLength.ToString(); }
}

If($FGPP.PasswordHistoryCount -eq 0)
{
$ScriptInformation += @{ Data =
"Enforce password history"; Value = "Not enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Enforce password history"; Value = "Enabled"; }
$ScriptInformation += @{ Data = "
Number of passwords remembered"; Value = $FGPP.PasswordHistoryCount.ToString(); }
}

If($FGPP.ComplexityEnabled -eq $True)


{
$ScriptInformation += @{ Data =
"Password must meet complexity requirements"; Value = "Enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Password must meet complexity requirements"; Value = "Not enabled"; }
}

If($FGPP.ReversibleEncryptionEnabled -eq
$True)
{
$ScriptInformation += @{ Data =
"Store password using reversible encryption"; Value = "Enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Store password using reversible encryption"; Value = "Not enabled"; }
}

If($FGPP.ProtectedFromAccidentalDeletion
-eq $True)
{
$ScriptInformation += @{ Data =
"Protect from accidental deletion"; Value = "Enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Protect from accidental deletion"; Value = "Not enabled"; }
}

$ScriptInformation += @{ Data = "Password


age options"; Value = ""; }
If($FGPP.MinPasswordAge.Days -eq 0)
{
$ScriptInformation += @{ Data = "
Enforce minimum password age"; Value = "Not enabled"; }
}
Else
{
$ScriptInformation += @{ Data = "
Enforce minimum password age"; Value = "Enabled"; }
$ScriptInformation += @{ Data = "
User cannot change the password within (days)"; Value =
$FGPP.MinPasswordAge.TotalDays.ToString(); }
}

If($FGPP.MaxPasswordAge -eq 0)
{
$ScriptInformation += @{ Data = "
Enforce maximum password age"; Value = "Not enabled"; }
}
Else
{
$ScriptInformation += @{ Data = "
Enforce maximum password age"; Value = "Enabled"; }
$ScriptInformation += @{ Data = "
User must change the password after (days)"; Value =
$FGPP.MaxPasswordAge.TotalDays.ToString(); }
}

If($FGPP.LockoutThreshold -eq 0)
{
$ScriptInformation += @{ Data =
"Enforce account lockout policy"; Value = "Not enabled"; }
}
Else
{
$ScriptInformation += @{ Data =
"Enforce account lockout policy"; Value = "Enabled"; }
$ScriptInformation += @{ Data = "
Number of failed logon attempts allowed"; Value =
$FGPP.LockoutThreshold.ToString(); }
$ScriptInformation += @{ Data = "
Reset failed logon attempts count after (mins)"; Value =
$FGPP.LockoutObservationWindow.TotalMinutes.ToString(); }
If($FGPP.LockoutDuration -eq 0)
{
$ScriptInformation += @{ Data
= " Account will be locked out"; Value = ""; }
$ScriptInformation += @{ Data
= " Until an administrator manually unlocks the account"; Value = ""; }
}
Else
{
$ScriptInformation += @{ Data
= " Account will be locked out for a duration of (mins)"; Value =
$FGPP.LockoutDuration.TotalMinutes.ToString(); }
}

$ScriptInformation += @{ Data =
"Description"; Value = $FGPP.Description; }

$results = Get-
ADFineGrainedPasswordPolicySubject -Identity $FGPP.Name -EA 0 | Sort-Object Name

If($? -and $Null -ne $results)


{
$cnt = 0
ForEach($Item in $results)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{
Data = "Directly Applies To"; Value = $Item.Name; }
}
Else
{
$ScriptInformation += @{
Data = ""; Value = $($Item.Name); }
}
}
}
Else
{
}

$Table = AddWordTable -Hashtable


$ScriptInformation `
-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection
$Table.Columns.Item(1).Cells -Bold -BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 275;
$Table.Columns.Item(2).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving Fine Grained
Password Policy data for domain $($Domain)"
WriteWordLine 0 0 "Error retrieving Fine
Grained Password Policy data for domain $($Domain)"
}
Else
{
WriteWordLine 0 0 "No Fine Grained Password
Policy data was retrieved for domain $($Domain)"
}
WriteWordLine 0 0 ""
}
Else
{
#FGPP cmdlets are not available
}
}
ElseIf($Text)
{
Line 1 "Domain mode`t`t`t`t: " $DomainMode
Line 1 "Domain name`t`t`t`t: " $DomainInfo.Name
Line 1 "NetBIOS name`t`t`t`t: " $DomainInfo.NetBIOSName
#V2.20 reorder the following properties in alpha order
Line 1 "AD Schema`t`t`t`t: ($($ADSchemaVersion)) - $
($ADSchemaVersionName)"
Line 1 "Allowed DNS Suffixes`t`t`t: " -NoNewLine
$DNSSuffixes = $DomainInfo.AllowedDNSSuffixes | Sort-Object

If($Null -eq $DNSSuffixes)


{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($DNSSuffix in $DNSSuffixes)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $DNSSuffix.ToString()
}
Else
{
Line 6 " $($DNSSuffix.ToString())"
}
}
}
Line 1 "Child domains`t`t`t`t: " -NoNewLine
$ChildDomains = $DomainInfo.ChildDomains | Sort-Object
If($Null -eq $ChildDomains)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($ChildDomain in $ChildDomains)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $ChildDomain.ToString()
}
Else
{
Line 6 " $($ChildDomain.ToString())"
}
}
}
Line 1 "Default computers container`t`t: "
$DomainInfo.ComputersContainer
Line 1 "Default users container`t`t`t: "
$DomainInfo.UsersContainer
Line 1 "Deleted objects container`t`t: "
$DomainInfo.DeletedObjectsContainer
Line 1 "Distinguished name`t`t`t: "
$DomainInfo.DistinguishedName
Line 1 "DNS root`t`t`t`t: " $DomainInfo.DNSRoot
Line 1 "Domain controllers container`t`t: "
$DomainInfo.DomainControllersContainer
If(![String]::IsNullOrEmpty($ExchangeSchemaInfo))
{
Line 1 "Exchange Schema`t`t`t`t: ($
($ExchangeSchemaVersion)) - $($ExchangeSchemaVersionName)"
}
Line 1 "Foreign security principals container`t: "
$DomainInfo.ForeignSecurityPrincipalsContainer
Line 1 "Infrastructure master`t`t`t: "
$DomainInfo.InfrastructureMaster
#V2.20 added
Line 1 "Last logon replication interval`t`t: "
$LastLogonReplicationInterval
Line 1 "Lost and Found container`t`t: "
$DomainInfo.LostAndFoundContainer
If(![String]::IsNullOrEmpty($DomainInfo.ManagedBy))
{
Line 1 "Managed by`t`t`t`t: " $DomainInfo.ManagedBy
}
Line 1 "PDC Emulator`t`t`t`t: " $DomainInfo.PDCEmulator
#V2.20 added
If(validObject $DomainInfo
PublicKeyRequiredPasswordRolling)
{
Line 1 "Public key required password rolling`t: "
$DomainInfo.PublicKeyRequiredPasswordRolling.ToString()
}
Line 1 "Quotas container`t`t`t: "
$DomainInfo.QuotasContainer
Line 1 "Read-only replica directory servers`t: " -NoNewLine
$ReadOnlyReplicas =
$DomainInfo.ReadOnlyReplicaDirectoryServers | Sort-Object
If($Null -eq $ReadOnlyReplicas)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($ReadOnlyReplica in $ReadOnlyReplicas)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $ReadOnlyReplica.ToString()
}
Else
{
Line 6 " $($ReadOnlyReplica.ToString())"
}
}
}
Line 1 "Replica directory servers`t`t: " -NoNewLine
$Replicas = $DomainInfo.ReplicaDirectoryServers | Sort-
Object
If($Null -eq $Replicas)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($Replica in $Replicas)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Replica.ToString()
}
Else
{
Line 6 " $($Replica.ToString())"
}
}
}
Line 1 "RID Master`t`t`t`t: " $DomainInfo.RIDMaster
Line 1 "Subordinate references`t`t`t: " -NoNewLine
$SubordinateReferences = $DomainInfo.SubordinateReferences
| Sort-Object
If($Null -eq $SubordinateReferences)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($SubordinateReference in
$SubordinateReferences)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $SubordinateReference.ToString()
}
Else
{
Line 6 " $
($SubordinateReference.ToString())"
}
}
}
Line 1 "Systems container`t`t`t: "
$DomainInfo.SystemsContainer

Write-Verbose "$(Get-Date): `t`tGetting domain trusts"


Line 0 "Domain Trusts: "

$ADDomainTrusts = $Null
$ADDomainTrusts = Get-ADObject -Filter {ObjectClass -eq
"trustedDomain"} -Server $Domain -Properties * -EA 0

If($? -and $Null -ne $ADDomainTrusts)


{

ForEach($Trust in $ADDomainTrusts)
{
Line 1 "Name`t`t: " $Trust.Name

If(!
[String]::IsNullOrEmpty($Trust.Description))
{
Line 1 "Description`t: "
$Trust.Description
}

Line 1 "Created`t`t: " $Trust.Created


Line 1 "Modified`t: " $Trust.Modified

$TrustExtendedAttributes = Get-ADTrustInfo
$Trust

Line 1 "Type`t`t: "


$TrustExtendedAttributes.TrustType
Line 1 "Attributes`t: " -NoNewLine
$cnt = 0
ForEach($attribute in
$TrustExtendedAttributes.Trustattribute)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $attribute.ToString()
}
Else
{
Line 3 " $($attribute.ToString())"
}
}

Line 1 "Direction`t: " $TrustDirection


Line 0 ""
}
}
ElseIf(!$?)
{
#error retrieving domain trusts
Write-Warning "Error retrieving domain trusts for $
($Domain)"
Line 0 "Error retrieving domain trusts for $
($Domain)"
}
Else
{
#no domain trust data
Line 1 "<None>"
}
Line 0 ""

Write-Verbose "$(Get-Date): `t`tProcessing domain


controllers"
$DomainControllers = $Null
$DomainControllers = Get-ADDomainController -Filter *
-Server $DomainInfo.DNSRoot -EA 0 | Sort-Object Name

If($? -and $Null -ne $DomainControllers)


{
$Script:AllDomainControllers.Add($DomainControllers)
> $Null
Line 0 "Domain Controllers: "
ForEach($DomainController in $DomainControllers)
{
Line 1 $DomainController.Name
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving domain controller
data for domain $($Domain)"
Line 0 "Error retrieving domain controller data for
domain $($Domain)"
}
Else
{
Line 0 "No Domain controller data was retrieved for
domain $($Domain)"
}
Line 0 ""

Write-Verbose "$(Get-Date): `t`tProcessing Fine Grained


Password Policies"

#are FGPP cmdlets available


If(Get-Command -Name "Get-ADFineGrainedPasswordPolicy" -ea
0)
{
$FGPPs = $Null
$FGPPs = Get-ADFineGrainedPasswordPolicy -Searchbase
$DomainInfo.DistinguishedName -Filter * -Properties * -Server $DomainInfo.DNSRoot
-EA 0 | Sort-Object Precedence, ObjectGUID

If($? -and $Null -ne $FGPPs)


{
Line 0 "Fine Grained Password Policies"

ForEach($FGPP in $FGPPs)
{
Line 1 "Name`t`t`t`t`t`t`t`t: "
$FGPP.Name
Line 1 "Precedence`t`t`t`t`t`t`t: "
$FGPP.Precedence.ToString()

Line 1 "Enforce minimum password


length`t`t`t`t`t: " -NoNewLine
If($FGPP.MinPasswordLength -eq 0)
{
Line 0 "Not enabled"
}
Else
{
Line 0 "Enabled"
Line 3 "Minimum password length
(characters)`t`t: " $FGPP.MinPasswordLength.ToString()
}

Line 1 "Enforce password


history`t`t`t`t`t: " -NoNewLine
If($FGPP.PasswordHistoryCount -eq 0)
{
Line 0 "Not enabled"
}
Else
{
Line 0 "Enabled"
Line 3 "Number of passwords
remembered`t`t`t: " $FGPP.PasswordHistoryCount.ToString()
}

Line 1 "Password must meet complexity


requirements`t`t`t: " -NoNewLine
If($FGPP.ComplexityEnabled -eq $True)
{
Line 0 "Enabled"
}
Else
{
Line 0 "Not enabled"
}

Line 1 "Store password using reversible


encryption`t`t`t: " -NoNewLine
If($FGPP.ReversibleEncryptionEnabled -eq
$True)
{
Line 0 "Enabled"
}
Else
{
Line 0 "Not enabled"
}

Line 1 "Protect from accidental


deletion`t`t`t`t: " -NoNewLine
If($FGPP.ProtectedFromAccidentalDeletion
-eq $True)
{
Line 0 "Enabled"
}
Else
{
Line 0 "Not enabled"
}

Line 1 "Password age options:"


If($FGPP.MinPasswordAge.Days -eq 0)
{
Line 2 "Enforce minimum password
age`t`t`t`t: Not enabled"
}
Else
{
Line 2 "Enforce minimum password
age`t`t`t`t: Enabled"
Line 3 "User cannot change the
password within (days)`t: " $FGPP.MinPasswordAge.TotalDays.ToString()
}

If($FGPP.MaxPasswordAge -eq 0)
{
Line 2 "Enforce maximum password
age`t`t`t`t: Not enabled"
}
Else
{
Line 2 "Enforce maximum password
age`t`t`t`t: Enabled"
Line 3 "User must change the
password after (days)`t: " $FGPP.MaxPasswordAge.TotalDays.ToString()
}

Line 1 "Enforce account lockout


policy`t`t`t`t`t: " -NoNewLine
If($FGPP.LockoutThreshold -eq 0)
{
Line 0 "Not enabled"
}
Else
{
Line 0 "Enabled"
Line 2 "Number of failed logon
attempts allowed`t`t`t: " $FGPP.LockoutThreshold.ToString()
Line 2 "Reset failed logon attempts
count after (mins)`t`t: " $FGPP.LockoutObservationWindow.TotalMinutes.ToString()
If($FGPP.LockoutDuration -eq 0)
{
Line 2 "Account will be
locked out"
Line 3 "Until an
administrator manually unlocks the account"
}
Else
{
Line 2 "Account will be
locked out for a duration of (mins)`t: "
$FGPP.LockoutDuration.TotalMinutes.ToString()
}

Line 1 "Description`t`t`t`t`t`t`t: "


$FGPP.Description

$results = Get-
ADFineGrainedPasswordPolicySubject -Identity $FGPP.Name -EA 0 | Sort-Object Name

If($? -and $Null -ne $results)


{
Line 1 "Directly Applies
To`t`t`t`t`t`t: " -NoNewLine
$cnt = 0
ForEach($Item in $results)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Item.Name
}
Else
{
Line 9 " $($Item.Name)"
}
}
}
Else
{
}

Line 0 ""
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving Fine Grained
Password Policy data for domain $($Domain)"
Line 0 "Error retrieving Fine Grained Password
Policy data for domain $($Domain)"
}
Else
{
Line 0 "No Fine Grained Password Policy data
was retrieved for domain $($Domain)"
}
Line 0 ""
}
Else
{
#FGPP cmdlets are not available
}
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Domain mode",($htmlsilver -bor
$htmlbold),$DomainMode,$htmlwhite)
$rowdata += @(,('Domain name',($htmlsilver -bor $htmlbold),
$DomainInfo.Name,$htmlwhite))
$rowdata += @(,('NetBIOS name',($htmlsilver -bor
$htmlbold),$DomainInfo.NetBIOSName,$htmlwhite))
#V2.20 reorder the following properties in alpha order
$rowdata += @(,('AD Schema',($htmlsilver -bor
$htmlbold),"($($ADSchemaVersion)) - $($ADSchemaVersionName)",$htmlwhite))
$DNSSuffixes = $DomainInfo.AllowedDNSSuffixes | Sort-Object

If($Null -eq $DNSSuffixes)


{
$rowdata += @(,('Allowed DNS Suffixes',($htmlsilver
-bor $htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($DNSSuffix in $DNSSuffixes)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Allowed DNS Suffixes',
($htmlsilver -bor $htmlbold),"$($DNSSuffix.ToString())",$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),"$($DNSSuffix.ToString())",$htmlwhite))
}
}
}
$ChildDomains = $DomainInfo.ChildDomains | Sort-Object
If($Null -eq $ChildDomains)
{
$rowdata += @(,('Child domains',($htmlsilver -bor
$htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($ChildDomain in $ChildDomains)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Child domains',
($htmlsilver -bor $htmlbold),"$($ChildDomain.ToString())",$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),"$($ChildDomain.ToString())",$htmlwhite))
}
}
}
$rowdata += @(,('Default computers container',($htmlsilver
-bor $htmlbold),$DomainInfo.ComputersContainer,$htmlwhite))
$rowdata += @(,('Default users container',($htmlsilver -bor
$htmlbold),$DomainInfo.UsersContainer,$htmlwhite))
$rowdata += @(,('Deleted objects container',($htmlsilver
-bor $htmlbold),$DomainInfo.DeletedObjectsContainer,$htmlwhite))
$rowdata += @(,('Distinguished name',($htmlsilver -bor
$htmlbold),$DomainInfo.DistinguishedName,$htmlwhite))
$rowdata += @(,('DNS root',($htmlsilver -bor $htmlbold),
$DomainInfo.DNSRoot,$htmlwhite))
$rowdata += @(,('Domain controllers container',($htmlsilver
-bor $htmlbold),$DomainInfo.DomainControllersContainer,$htmlwhite))
If(![String]::IsNullOrEmpty($ExchangeSchemaInfo))
{
$rowdata += @(,('Exchange Schema',($htmlsilver -bor
$htmlbold),"($($ExchangeSchemaVersion)) - $($ExchangeSchemaVersionName)",
$htmlwhite))
}
$rowdata += @(,('Foreign security principals container',
($htmlsilver -bor $htmlbold),$DomainInfo.ForeignSecurityPrincipalsContainer,
$htmlwhite))
$rowdata += @(,('Infrastructure master',($htmlsilver -bor
$htmlbold),$DomainInfo.InfrastructureMaster,$htmlwhite))
#V2.20 added
$rowdata += @(,("Last logon replication interval",
($htmlsilver -bor $htmlbold),$LastLogonReplicationInterval,$htmlwhite))
$rowdata += @(,('Lost and Found container',($htmlsilver
-bor $htmlbold),$DomainInfo.LostAndFoundContainer,$htmlwhite))
If(![String]::IsNullOrEmpty($DomainInfo.ManagedBy))
{
$rowdata += @(,('Managed by',($htmlsilver -bor
$htmlbold),$DomainInfo.ManagedBy,$htmlwhite))
}
$rowdata += @(,('PDC Emulator',($htmlsilver -bor
$htmlbold),$DomainInfo.PDCEmulator,$htmlwhite))
#V2.20 added
If(validObject $DomainInfo
PublicKeyRequiredPasswordRolling)
{
$rowdata += @(,("Public key required password
rolling",($htmlsilver -bor $htmlbold),
$DomainInfo.PublicKeyRequiredPasswordRolling.ToString(),$htmlwhite))
}
$rowdata += @(,('Quotas container',($htmlsilver -bor
$htmlbold),$DomainInfo.QuotasContainer,$htmlwhite))
$ReadOnlyReplicas =
$DomainInfo.ReadOnlyReplicaDirectoryServers | Sort-Object
If($Null -eq $ReadOnlyReplicas)
{
$rowdata += @(,('Read-only replica directory
servers',($htmlsilver -bor $htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($ReadOnlyReplica in $ReadOnlyReplicas)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Read-only replica
directory servers',($htmlsilver -bor $htmlbold),"$($ReadOnlyReplica.ToString())",
$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),"$($ReadOnlyReplica.ToString())",$htmlwhite))
}
}
}
$Replicas = $DomainInfo.ReplicaDirectoryServers | Sort-
Object
If($Null -eq $Replicas)
{
$rowdata += @(,('Replica directory servers',
($htmlsilver -bor $htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($Replica in $Replicas)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Replica directory
servers',($htmlsilver -bor $htmlbold),"$($Replica.ToString())",$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),"$($Replica.ToString())",$htmlwhite))
}
}
}
$rowdata += @(,('RID Master',($htmlsilver -bor $htmlbold),
$DomainInfo.RIDMaster,$htmlwhite))
$SubordinateReferences = $DomainInfo.SubordinateReferences
| Sort-Object
If($Null -eq $SubordinateReferences)
{
$rowdata += @(,('Subordinate references',($htmlsilver
-bor $htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($SubordinateReference in
$SubordinateReferences)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Subordinate references',
($htmlsilver -bor $htmlbold),"$($SubordinateReference.ToString())",$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),"$($SubordinateReference.ToString())",$htmlwhite))
}
}
}
$rowdata += @(,('Systems container',($htmlsilver -bor
$htmlbold),$DomainInfo.SystemsContainer,$htmlwhite))

$msg = ""
$columnWidths = @("175","300")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "475"
WriteHTMLLine 0 0 " "

Write-Verbose "$(Get-Date): `t`tGetting domain trusts"


WriteHTMLLine 3 0 "Domain Trusts"

$ADDomainTrusts = $Null
$ADDomainTrusts = Get-ADObject -Filter {ObjectClass -eq
"trustedDomain"} -Server $Domain -Properties * -EA 0

If($? -and $Null -ne $ADDomainTrusts)


{

ForEach($Trust in $ADDomainTrusts)
{
$rowdata = @()
$columnHeaders = @("Name",($htmlsilver -bor
$htmlbold),$Trust.Name,$htmlwhite)

If(!
[String]::IsNullOrEmpty($Trust.Description))
{
$rowdata += @(,('Description',
($htmlsilver -bor $htmlbold),$Trust.Description,$htmlwhite))
}

$rowdata += @(,('Created',($htmlsilver -bor


$htmlbold),$Trust.Created,$htmlwhite))
$rowdata += @(,('Modified',($htmlsilver -bor
$htmlbold),$Trust.Modified,$htmlwhite))

$TrustExtendedAttributes = Get-ADTrustInfo
$Trust

$rowdata += @(,('Type',($htmlsilver -bor


$htmlbold),$TrustExtendedAttributes.TrustType,$htmlwhite))

$cnt = 0
ForEach($attribute in
$TrustExtendedAttributes.Trustattribute)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Attributes',
($htmlsilver -bor $htmlbold),$attribute.ToString(),$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver
-bor $htmlbold),$attribute.ToString(),$htmlwhite))
}
}

$rowdata += @(,('Direction',($htmlsilver -bor


$htmlbold),$TrustDirection,$htmlwhite))

$msg = ""
$columnWidths = @("175","300")
FormatHTMLTable $msg -rowArray $rowdata
-columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "475"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
#error retrieving domain trusts
Write-Warning "Error retrieving domain trusts for $
($Domain)"
WriteHTMLLine 0 0 "Error retrieving domain trusts for
$($Domain)" "" $Null 0 $False $True
}
Else
{
#no domain trust data
WriteHTMLLine 0 0 "None"
}

Write-Verbose "$(Get-Date): `t`tProcessing domain


controllers"
$DomainControllers = $Null
$DomainControllers = Get-ADDomainController -Filter *
-Server $DomainInfo.DNSRoot -EA 0 | Sort-Object Name

If($? -and $Null -ne $DomainControllers)


{
$Script:AllDomainControllers.Add($DomainControllers)
> $Null
$rowdata = @()
WriteHTMLLine 3 0 "Domain Controllers"
ForEach($DomainController in $DomainControllers)
{
$rowdata += @(,($DomainController.Name,
$htmlwhite))
}
$msg = ""
$columnHeaders = @("Name",($htmlsilver -bor
$htmlbold))
$columnWidths = @("105")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "105"
WriteHTMLLine 0 0 " "
}
ElseIf(!$?)
{
Write-Warning "Error retrieving domain controller
data for domain $($Domain)"
WriteHTMLLine 0 0 "Error retrieving domain controller
data for domain $($Domain)" "" $Null 0 $False $True
}
Else
{
WriteHTMLLine 0 0 "No Domain controller data was
retrieved for domain $($Domain)" "" $Null 0 $False $True
}
WriteHTMLLine 0 0 " "

Write-Verbose "$(Get-Date): `t`tProcessing Fine Grained


Password Policies"

#are FGPP cmdlets available


If(Get-Command -Name "Get-ADFineGrainedPasswordPolicy" -ea
0)
{
$FGPPs = $Null
$FGPPs = Get-ADFineGrainedPasswordPolicy -Searchbase
$DomainInfo.DistinguishedName -Filter * -Properties * -Server $DomainInfo.DNSRoot
-EA 0 | Sort-Object Precedence, ObjectGUID

If($? -and $Null -ne $FGPPs)


{
WriteHTMLLine 3 0 "Fine Grained Password
Policies"

ForEach($FGPP in $FGPPs)
{
$rowdata = @()
$columnHeaders = @("Precedence",
($htmlsilver -bor $htmlbold),$FGPP.Precedence.ToString(),$htmlwhite)

If($FGPP.MinPasswordLength -eq 0)
{
$rowdata += @(,("Enforce minimum
password length",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Enforce minimum
password length",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
$rowdata += @(,(" Minimum
password length (characters)",($htmlsilver -bor $htmlbold),
$FGPP.MinPasswordLength.ToString(),$htmlwhite))
}

If($FGPP.PasswordHistoryCount -eq 0)
{
$rowdata += @(,("Enforce password
history",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Enforce password
history",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
$rowdata += @(,(" Number of
passwords remembered",($htmlsilver -bor $htmlbold),
$FGPP.PasswordHistoryCount.ToString(),$htmlwhite))
}

If($FGPP.ComplexityEnabled -eq $True)


{
$rowdata += @(,("Password must meet
complexity requirements",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Password must meet
complexity requirements",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}

If($FGPP.ReversibleEncryptionEnabled -eq
$True)
{
$rowdata += @(,("Store password
using reversible encryption",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Store password
using reversible encryption",($htmlsilver -bor $htmlbold),"Not enabled",
$htmlwhite))
}

If($FGPP.ProtectedFromAccidentalDeletion
-eq $True)
{
$rowdata += @(,("Protect from
accidental deletion",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Protect from
accidental deletion",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}

$rowdata += @(,("Password age options",


($htmlsilver -bor $htmlbold),"",$htmlwhite))
If($FGPP.MinPasswordAge.Days -eq 0)
{
$rowdata += @(,(" Enforce
minimum password age",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}
Else
{
$rowdata += @(,(" Enforce
minimum password age",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
$rowdata += @(,(" User
cannot change the password within (days)",($htmlsilver -bor $htmlbold),
$FGPP.MinPasswordAge.TotalDays.ToString(),$htmlwhite))
}

If($FGPP.MaxPasswordAge -eq 0)
{
$rowdata += @(,(" Enforce
maximum password age",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}
Else
{
$rowdata += @(,(" Enforce
maximum password age",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
$rowdata += @(,(" User
must change the password after (days)",($htmlsilver -bor $htmlbold),
$FGPP.MaxPasswordAge.TotalDays.ToString(),$htmlwhite))
}

If($FGPP.LockoutThreshold -eq 0)
{
$rowdata += @(,("Enforce account
lockout policy",($htmlsilver -bor $htmlbold),"Not enabled",$htmlwhite))
}
Else
{
$rowdata += @(,("Enforce account
lockout policy",($htmlsilver -bor $htmlbold),"Enabled",$htmlwhite))
$rowdata += @(,(" Number of
failed logon attempts allowed",($htmlsilver -bor $htmlbold),
$FGPP.LockoutThreshold.ToString(),$htmlwhite))
$rowdata += @(,(" Reset failed
logon attempts count after (mins)",($htmlsilver -bor $htmlbold),
$FGPP.LockoutObservationWindow.TotalMinutes.ToString(),$htmlwhite))
If($FGPP.LockoutDuration -eq 0)
{
$rowdata += @(,(" Account
will be locked out",($htmlsilver -bor $htmlbold),"",$htmlwhite))
$rowdata += @(,("
Until an administrator manually unlocks the account",($htmlsilver -bor
$htmlbold),"",$htmlwhite))
}
Else
{
$rowdata += @(,(" Account
will be locked out for a duration of (mins)",($htmlsilver -bor $htmlbold),
$FGPP.LockoutDuration.TotalMinutes.ToString(),$htmlwhite))
}

$rowdata += @(,("Description",
($htmlsilver -bor $htmlbold),$FGPP.Description,$htmlwhite))

$results = Get-
ADFineGrainedPasswordPolicySubject -Identity $FGPP.Name -EA 0 | Sort-Object Name

If($? -and $Null -ne $results)


{
$cnt = 0
ForEach($Item in $results)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,
("Directly Applies To",($htmlsilver -bor $htmlbold),$Item.Name,$htmlwhite))
}
Else
{
$rowdata += @(,("",
($htmlsilver -bor $htmlbold),$($Item.Name),$htmlwhite))
}
}
}
Else
{
}

$msg = "Name: $($FGPP.Name)"


$columnWidths = @("300","225")
FormatHTMLTable $msg -rowArray $rowdata
-columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "525"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving Fine Grained
Password Policy data for domain $($Domain)"
WriteHTMLLine 0 0 "Error retrieving Fine
Grained Password Policy data for domain $($Domain)"
}
Else
{
WriteHTMLLine 0 0 "No Fine Grained Password
Policy data was retrieved for domain $($Domain)"
}
WriteHTMLLine 0 0 " "
}
Else
{
#FGPP cmdlets are not available
}
}

$First = $False
}
ElseIf(!$?)
{
$txt = "Error retrieving domain data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No Domain data was retrieved for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
}
$ADDomainTrusts = $Null
$ADSchemaInfo = $Null
$ChildDomains = $Null
$DNSSuffixes = $Null
$DomainControllers = $Null
$ExchangeSchemaInfo = $Null
$FGPPs = $Null
$First = $Null
$ReadOnlyReplicas = $Null
$Replicas = $Null
$SubordinateReferences = $Null
$Table = $Null
}
#endregion
#region domain controllers
Function ProcessDomainControllers
{
Write-Verbose "$(Get-Date): Writing domain controller data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Domain Controllers in $($Script:ForestName)"
}
ElseIf($Text)
{
Line 0 "/// Domain Controllers in $($Script:ForestName) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Domain Controllers in $
($Script:ForestName)&nbsp;&nbsp;\\\"
}

$Script:DCDNSIPInfo = New-Object System.Collections.ArrayList


#V2.19 added
$Script:DCEventLogInfo = New-Object System.Collections.ArrayList
$Script:TimeServerInfo = New-Object System.Collections.ArrayList
$Script:AllDomainControllers = $Script:AllDomainControllers | Sort-Object
Name
$First = $True

ForEach($DC in $Script:AllDomainControllers)
{
Write-Verbose "$(Get-Date): `tProcessing domain controller $($DC.name)"
$FSMORoles = $DC.OperationMasterRoles | Sort-Object
$Partitions = $DC.Partitions | Sort-Object

If(($MSWORD -or $PDF) -and !$First)


{
#put each DC, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($MSWORD -or $PDF)


{
WriteWordLine 2 0 $DC.Name
[System.Collections.Hashtable[]] $ScriptInformation = @()
$ScriptInformation += @{ Data = "Default partition"; Value =
$DC.DefaultPartition; }
$ScriptInformation += @{ Data = "Domain"; Value = $DC.domain; }
If($DC.Enabled -eq $True)
{
$tmp = "True"
}
Else
{
$tmp = "False"
}
$ScriptInformation += @{ Data = "Enabled"; Value = $tmp; }
$ScriptInformation += @{ Data = "Hostname"; Value = $DC.HostName;
}
If($DC.IsGlobalCatalog -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
$ScriptInformation += @{ Data = "Global Catalog"; Value = $tmp; }
If($DC.IsReadOnly -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
$ScriptInformation += @{ Data = "Read-only"; Value = $tmp; }
$ScriptInformation += @{ Data = "LDAP port"; Value =
$DC.LdapPort.ToString(); }
$ScriptInformation += @{ Data = "SSL port"; Value =
$DC.SslPort.ToString(); }
If($Null -eq $FSMORoles)
{
$ScriptInformation += @{ Data = "Operation Master roles";
Value = "<None>"; }
}
Else
{
$cnt = 0
ForEach($FSMORole in $FSMORoles)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Operation
Master roles"; Value = $FSMORole.ToString(); }
}
Else
{
$ScriptInformation += @{ Data = ""; Value =
$FSMORole.ToString(); }
}
}
}
If($Null -eq $Partitions)
{
$ScriptInformation += @{ Data = "Partitions"; Value =
"<None>"; }
}
Else
{
$cnt = 0
ForEach($Partition in $Partitions)
{
$cnt++

If($cnt -eq 1)
{
$ScriptInformation += @{ Data = "Partitions";
Value = $Partition.ToString(); }
}
Else
{
$ScriptInformation += @{ Data = ""; Value =
$Partition.ToString(); }
}

}
}
$ScriptInformation += @{ Data = "Site"; Value = $DC.Site; }
$ScriptInformation += @{ Data = "Operating System"; Value =
$DC.OperatingSystem; }

If(![String]::IsNullOrEmpty($DC.OperatingSystemServicePack))
{
$ScriptInformation += @{ Data = "Service Pack"; Value =
$DC.OperatingSystemServicePack; }
}
$ScriptInformation += @{ Data = "Operating System version"; Value
= $DC.OperatingSystemVersion; }

If(!$Hardware)
{
If([String]::IsNullOrEmpty($DC.IPv4Address))
{
$tmp = "<None>"
}
Else
{
$tmp = $DC.IPv4Address
}
$ScriptInformation += @{ Data = "IPv4 Address"; Value =
$tmp; }

If([String]::IsNullOrEmpty($DC.IPv6Address))
{
$tmp = "<None>"
}
Else
{
$tmp = $DC.IPv6Address
}
$ScriptInformation += @{ Data = "IPv6 Address"; Value =
$tmp; }
}

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 140;
$Table.Columns.Item(2).Width = 300;
$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 "/// DC: $($DC.Name) \\\"
Line 1 "Default partition`t`t: " $DC.DefaultPartition
Line 1 "Domain`t`t`t`t: " $DC.domain
If($DC.Enabled -eq $True)
{
$tmp = "True"
}
Else
{
$tmp = "False"
}
Line 1 "Enabled`t`t`t`t: " $tmp
Line 1 "Hostname`t`t`t: " $DC.HostName
If($DC.IsGlobalCatalog -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
Line 1 "Global Catalog`t`t`t: " $tmp
If($DC.IsReadOnly -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
Line 1 "Read-only`t`t`t: " $tmp
Line 1 "LDAP port`t`t`t: " $DC.LdapPort.ToString()
Line 1 "SSL port`t`t`t: " $DC.SslPort.ToString()
Line 1 "Operation Master roles`t`t: " -NoNewLine
If($Null -eq $FSMORoles)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($FSMORole in $FSMORoles)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $FSMORole.ToString()
}
Else
{
Line 5 " $($FSMORole.ToString())"
}

}
}
Line 1 "Partitions`t`t`t: " -NoNewLine
If($Null -eq $Partitions)
{
Line 0 "<None>"
}
Else
{
$cnt = 0
ForEach($Partition in $Partitions)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Partition.ToString()
}
Else
{
Line 5 " $($Partition.ToString())"
}
}
}
Line 1 "Site`t`t`t`t: " $DC.Site
Line 1 "Operating System`t`t: " $DC.OperatingSystem
If(![String]::IsNullOrEmpty($DC.OperatingSystemServicePack))
{
Line 1 "Service Pack`t`t`t: "
$DC.OperatingSystemServicePack
}
Line 1 "Operating System version`t: " $DC.OperatingSystemVersion

If(!$Hardware)
{
Line 1 "IPv4 Address`t`t`t: " -NoNewLine
If([String]::IsNullOrEmpty($DC.IPv4Address))
{
Line 0 "<None>"
}
Else
{
Line 0 $DC.IPv4Address
}
Line 1 "IPv6 Address`t`t`t: " -NoNewLine
If([String]::IsNullOrEmpty($DC.IPv6Address))
{
Line 0 "<None>"
}
Else
{
Line 0 $DC.IPv6Address
}
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($DC.Name)&nbsp;&nbsp;\\\"
$rowdata = @()
$columnHeaders = @("Default partition",($htmlsilver -bor
$htmlbold),$DC.DefaultPartition,$htmlwhite)
$rowdata += @(,('Domain',($htmlsilver -bor $htmlbold),$DC.domain,
$htmlwhite))
If($DC.Enabled -eq $True)
{
$tmp = "True"
}
Else
{
$tmp = "False"
}
$rowdata += @(,('Enabled',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
$rowdata += @(,('Hostname',($htmlsilver -bor $htmlbold),
$DC.HostName,$htmlwhite))
If($DC.IsGlobalCatalog -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
$rowdata += @(,('Global Catalog',($htmlsilver -bor $htmlbold),
$tmp,$htmlwhite))
If($DC.IsReadOnly -eq $True)
{
$tmp = "Yes"
}
Else
{
$tmp = "No"
}
$rowdata += @(,('Read-only',($htmlsilver -bor $htmlbold),$tmp,
$htmlwhite))
$rowdata += @(,('LDAP port',($htmlsilver -bor $htmlbold),
$DC.LdapPort.ToString(),$htmlwhite))
$rowdata += @(,('SSL port',($htmlsilver -bor $htmlbold),
$DC.SslPort.ToString(),$htmlwhite))
If($Null -eq $FSMORoles)
{
$rowdata += @(,('Operation Master roles',($htmlsilver -bor
$htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($FSMORole in $FSMORoles)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Operation Master roles',
($htmlsilver -bor $htmlbold),$FSMORole.ToString(),$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),$FSMORole.ToString(),$htmlwhite))
}
}
}
If($Null -eq $Partitions)
{
$rowdata += @(,('Partitions',($htmlsilver -bor
$htmlbold),"None",$htmlwhite))
}
Else
{
$cnt = 0
ForEach($Partition in $Partitions)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,('Partitions',($htmlsilver -bor
$htmlbold),$Partition.ToString(),$htmlwhite))
}
Else
{
$rowdata += @(,('',($htmlsilver -bor
$htmlbold),$Partition.ToString(),$htmlwhite))
}
}
}
$rowdata += @(,('Site',($htmlsilver -bor $htmlbold),$DC.Site,
$htmlwhite))
$rowdata += @(,('Operating System',($htmlsilver -bor $htmlbold),
$DC.OperatingSystem,$htmlwhite))

If(![String]::IsNullOrEmpty($DC.OperatingSystemServicePack))
{
$rowdata += @(,('Service Pack',($htmlsilver -bor
$htmlbold),$DC.OperatingSystemServicePack,$htmlwhite))
}
$rowdata += @(,('Operating System version',($htmlsilver -bor
$htmlbold),$DC.OperatingSystemVersion,$htmlwhite))

If(!$Hardware)
{
If([String]::IsNullOrEmpty($DC.IPv4Address))
{
$tmp = "None"
}
Else
{
$tmp = $DC.IPv4Address
}
$rowdata += @(,('IPv4 Address',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
If([String]::IsNullOrEmpty($DC.IPv6Address))
{
$tmp = "None"
}
Else
{
$tmp = $DC.IPv6Address
}
$rowdata += @(,('IPv6 Address',($htmlsilver -bor
$htmlbold),$tmp,$htmlwhite))
}

$msg = ""
$columnWidths = @("140","300")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "440"
WriteHTMLLine 0 0 " "
}

If($Script:DARights -and $Script:Elevated)


{
OutputTimeServerRegistryKeys $DC.HostName

OutputADFileLocations $DC.HostName

OutputEventLogInfo $DC.HostName
}

If($Hardware -or $Services -or $DCDNSInfo)


{
If(Test-Connection -ComputerName $DC.HostName -quiet -EA 0)
{
If($Hardware)
{
GetComputerWMIInfo $DC.HostName
}

If($DCDNSInfo)
{
BuildDCDNSIPConfigTable $DC.HostName $DC.Site
}

If($Services)
{
GetComputerServices $DC.HostName
}

}
Else
{
$txt = "$(Get-Date): `t`t$($DC.Name) is offline or
unreachable. Hardware inventory is skipped."
Write-Verbose $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}

If($Hardware -and -not $Services)


{
$txt = "Hardware inventory was skipped."
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
ElseIf($Services -and -not $Hardware)
{
$txt = "Services was skipped."
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
ElseIf($Hardware -and $Services)
{
$txt = "Hardware inventory and Services were
skipped."
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
}
}
$First = $False
}
$Script:AllDomainControllers = $Null
}

Function OutputTimeServerRegistryKeys
{
Param( [string] $DCName )

Write-Verbose "$(Get-Date): `tTimeServer Registry Keys"


#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config
AnnounceFlags
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config
MaxNegPhaseCorrection
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config
MaxPosPhaseCorrection
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
NtpServer
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
Type

#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClie
nt SpecialPollInterval

#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTim
eProvider Enabled

$AnnounceFlags = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "AnnounceFlags" $DCName
$MaxNegPhaseCorrection = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxNegPhaseCorrection"
$DCName
$MaxPosPhaseCorrection = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxPosPhaseCorrection"
$DCName
$NtpServer = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "NtpServer" $DCName
$NtpType = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "Type" $DCName
$SpecialPollInterval = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient"
"SpecialPollInterval" $DCName
$VMICTimeProviderEnabled = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider"
"Enabled" $DCName
$NTPSource = w32tm /query /computer:$DCName /source

If($VMICTimeProviderEnabled -eq 0)
{
$VMICEnabled = "Disabled"
}
Else
{
$VMICEnabled = "Enabled"
}

#create time server info array


$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name DCName -Value
$DCName
$obj | Add-Member -MemberType NoteProperty -Name TimeSource -Value
$NTPSource
$obj | Add-Member -MemberType NoteProperty -Name AnnounceFlags -Value
$AnnounceFlags
$obj | Add-Member -MemberType NoteProperty -Name MaxNegPhaseCorrection -Value
$MaxNegPhaseCorrection
$obj | Add-Member -MemberType NoteProperty -Name MaxPosPhaseCorrection -Value
$MaxPosPhaseCorrection
$obj | Add-Member -MemberType NoteProperty -Name NtpServer -Value
$NtpServer
$obj | Add-Member -MemberType NoteProperty -Name NtpType -Value
$NtpType
$obj | Add-Member -MemberType NoteProperty -Name SpecialPollInterval -Value
$SpecialPollInterval
$obj | Add-Member -MemberType NoteProperty -Name VMICTimeProvider -Value
$VMICEnabled

[void]$Script:TimeServerInfo.Add($obj)

If($MSWORD -or $PDF)


{
WriteWordLine 3 0 "Time Server Information"
[System.Collections.Hashtable[]] $ScriptInformation = @()

$Scriptinformation += @{ Data = "Time source"; Value = $NTPSource; }


$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\Config\AnnounceFlags"; Value = $AnnounceFlags; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\Config\MaxNegPhaseCorrection"; Value =
$MaxNegPhaseCorrection; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\Config\MaxPosPhaseCorrection"; Value =
$MaxPosPhaseCorrection; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\Parameters\NtpServer"; Value = $NtpServer; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\Parameters\Type"; Value = $NtpType; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\TimeProviders\NtpClient\SpecialPollInterval";
Value = $SpecialPollInterval; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CCS\Services\W32Time\TimeProviders\VMICTimeProvider\Enabled"; Value =
$VMICEnabled; }

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;
SetWordCellFormat -Collection $Table -Size 9;

$Table.Columns.Item(1).Width = 335;
$Table.Columns.Item(2).Width = 130;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)
FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 "Time Server Information"
Line 0 ""
Line 1 "Time source: " $NTPSource
Line 1 "HKLM:\SYSTEM\CCS\Services\W32Time\"
Line 2 "Config\AnnounceFlags`t`t`t`t: " $AnnounceFlags
Line 2 "Config\MaxNegPhaseCorrection`t`t`t: " $MaxNegPhaseCorrection
Line 2 "Config\MaxPosPhaseCorrection`t`t`t: " $MaxPosPhaseCorrection
Line 2 "Parameters\NtpServer`t`t`t`t: " $NtpServer
Line 2 "Parameters\Type`t`t`t`t`t: " $NtpType
Line 2 "TimeProviders\NtpClient\SpecialPollInterval`t: "
$SpecialPollInterval
Line 2 "TimeProviders\VMICTimeProvider\Enabled`t`t: " $VMICEnabled
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Time Server Information"
$rowdata = @()
$columnHeaders = @("Time source",($htmlsilver -bor $htmlbold),
$NTPSource,$htmlwhite)
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\Config\AnnounceFlags',($htmlsilver -bor
$htmlbold),$AnnounceFlags,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\Config\MaxNegPhaseCorrection',($htmlsilver -bor
$htmlbold),$MaxNegPhaseCorrection,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\Config\MaxPosPhaseCorrection',($htmlsilver -bor
$htmlbold),$MaxPosPhaseCorrection,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\Parameters\NtpServer',($htmlsilver -bor
$htmlbold),$NtpServer,$htmlwhite))
$rowdata += @(,('HKLM:\SYSTEM\CCS\Services\W32Time\Parameters\Type',
($htmlsilver -bor $htmlbold),$NtpType,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\TimeProviders\NtpClient\SpecialPollInterval',
($htmlsilver -bor $htmlbold),$SpecialPollInterval,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CCS\Services\W32Time\TimeProviders\VMICTimeProvider\Enabled',
($htmlsilver -bor $htmlbold),$VMICEnabled,$htmlwhite))

$msg = ""
$columnWidths = @("335","130")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "465"
WriteHTMLLine 0 0 " "
}
}

Function OutputADFileLocations
{
Param( [string] $DCName )
Write-Verbose "$(Get-Date): `tAD Database, Logfile and SYSVOL locations"
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters 'DSA
Database file'
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters
'Database log files path'
#HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters
SysVol

$DSADatabaseFile = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" "DSA Database file"
$DCName
$DatabaseLogFilesPath = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" "Database log files path"
$DCName
$SysVol = Get-RegistryValue
"HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" "SysVol" $DCName

#calculation is taken from http://blogs.metcorpconsulting.com/tech/?p=177


$DITRemotePath = $DSADatabaseFile.Replace(":", "$")
$DITFile = "\\$DCName\$DITRemotePath"
$DITsize = ([System.IO.FileInfo]$DITFile).Length
$DITsize = ($DITsize/1GB)
$DSADatabaseFileSize = "{0:N3}" -f $DITsize

If($MSWORD -or $PDF)


{
WriteWordLine 3 0 "AD Database, Logfile and SYSVOL Locations"
[System.Collections.Hashtable[]] $ScriptInformation = @()

$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\DSA Database file"; Value
= $DSADatabaseFile; }
$Scriptinformation += @{ Data = "DSA Database file size "; Value = "$
($DSADatabaseFileSize) GB"; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\Database log files path";
Value = $DatabaseLogFilesPath; }
$Scriptinformation += @{ Data =
"HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters\SysVol"; Value =
$SysVol; }

$Table = AddWordTable -Hashtable $ScriptInformation `


-Columns Data,Value `
-List `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Columns.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;
SetWordCellFormat -Collection $Table -Size 9;

$Table.Columns.Item(1).Width = 335;
$Table.Columns.Item(2).Width = 130;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 "AD Database, Logfile and SYSVOL Locations"
Line 0 ""
Line 1 "HKLM:\SYSTEM\CCS\Services\"
Line 2 "NTDS\Parameters\DSA Database file`t: " $DSADatabaseFile
Line 2 "DSA Database file size`t`t`t: $($DSADatabaseFileSize) GB"
Line 2 "NTDS\Parameters\Database log files path`t: "
$DatabaseLogFilesPath
Line 2 "Netlogon\Parameters\SysVol`t`t: " $SysVol
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "AD Database, Logfile and SYSVOL Locations"
$rowdata = @()
$columnHeaders =
@("HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\DSA Database file",
($htmlsilver -bor $htmlbold),$DSADatabaseFile,$htmlwhite)
$rowdata += @(,('DSA Database file size',($htmlsilver -bor
$htmlbold),"$($DSADatabaseFileSize) GB",$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters\Database log files path',
($htmlsilver -bor $htmlbold),$DatabaseLogFilesPath,$htmlwhite))
$rowdata += @(,
('HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters\SysVol',($htmlsilver
-bor $htmlbold),$SysVol,$htmlwhite))

$msg = ""
$columnWidths = @("335","130")
FormatHTMLTable $msg -rowarray $rowdata -columnArray $columnheaders
-fixedWidth $columnWidths -tablewidth "465"
WriteHTMLLine 0 0 " "
}
}

Function OutputEventLogInfo
{
Param( [string] $DCName )
#V2.19 added

Write-Verbose "$(Get-Date): `tEvent Log Information"


$ELInfo = New-Object System.Collections.ArrayList

$EventLogs = Get-EventLog -List -ComputerName $DCName | Select-Object


MaximumKilobytes, Log | Sort-Object Log

If($? -and $Null -ne $EventLogs)


{
ForEach($EventLog in $EventLogs)
{
[string]$ELSize = "{0,10:N0}" -f $EventLog.MaximumKilobytes

$obj = New-Object -TypeName PSObject


$obj | Add-Member -MemberType NoteProperty -Name DCName
-Value $DCName
$obj | Add-Member -MemberType NoteProperty -Name EventLogName
-Value $EventLog.Log
$obj | Add-Member -MemberType NoteProperty -Name EventLogSize
-Value $ELSize

[void]$Script:DCEventLogInfo.Add($obj)
[void]$ELInfo.Add($obj)
}
}
Else
{
[string]$ELSize = "{0,10:N0}" -f 0

$obj = New-Object -TypeName PSObject


$obj | Add-Member -MemberType NoteProperty -Name DCName
-Value $DCName
$obj | Add-Member -MemberType NoteProperty -Name EventLogName
-Value "Unable to retrieve Event Log data"
$obj | Add-Member -MemberType NoteProperty -Name EventLogSize
-Value $ELSize

[void]$Script:DCEventLogInfo.Add($obj)
[void]$ELInfo.Add($obj)
}

#V2.20 changed to @()


$xEventLogInfo = @($ELInfo | Sort-Object EventLogName)

If($MSWord -or $PDF)


{
WriteWordLine 3 0 "Event Log Information"
$TableRange = $doc.Application.Selection.Range
[int]$Columns = 2
[int]$Rows = $xEventLogInfo.Count + 1
[int]$xRow = 1

$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)


$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat = $wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Event Log Name"

$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Event Log Size (KB)"
}
ElseIf($Text)
{
Line 0 "Event Log Information"
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Event Log Information"
$rowdata = @()
}

ForEach($Item in $xEventLogInfo)
{
If($MSWord -or $PDF)
{
$xRow++
$Table.Cell($xRow,1).Range.Text = $Item.EventLogName
$Table.Cell($xRow,2).Range.ParagraphFormat.Alignment =
$wdCellAlignVerticalTop
$Table.Cell($xRow,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell($xRow,2).Range.Text = $Item.EventLogSize
}
ElseIf($Text)
{
Line 1 "Event Log Name`t`t: " $Item.EventLogName
Line 1 "Event Log Size (KB)`t: " $Item.EventLogSize
Line 0 ""
}
ElseIf($HTML)
{
$rowdata += @(,(
$Item.EventLogName,$htmlwhite,
$Item.EventLogSize,$htmlwhite
))
}
}

If($MSWord -or $PDF)


{
#set column widths
$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 150; Break}
2 {$xcol.width = 100; Break}
}
}
$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$selection.EndKey($wdStory,$wdMove) | Out-Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
#nothing to do
}
ElseIf($HTML)
{
$columnHeaders = @(
'Event Log Name',($htmlsilver -bor $htmlbold),
'Event Log Size (KB)',($htmlsilver -bor $htmlbold)
)

$msg = ""
$columnWidths = @("175px","125px")
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "300"
WriteHTMLLine 0 0 " "
}
}
#endregion

#region organizational units


Function ProcessOrganizationalUnits
{
Write-Verbose "$(Get-Date): Writing OU data by Domain"
If($MSWORD -or $PDF)
{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Organizational Units"
}
ElseIf($Text)
{
Line 0 "/// Organizational Units \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Organizational Units&nbsp;&nbsp;\\\"
}
$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing domain $($Domain)"

If(($MSWORD -or $PDF) -and !$First)


{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($Domain -eq $Script:ForestRootDomain)


{
$txt = "OUs in Domain $($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
}
}
Else
{
$txt = "OUs in Domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
}
}

#get all OUs for the domain


$OUs = $Null
#V2.20 changed to @()
$OUs = @(Get-ADOrganizationalUnit -Filter * -Server $Domain `
-Properties CanonicalName, DistinguishedName, Name, Created,
ProtectedFromAccidentalDeletion -EA 0 | `
Select-Object CanonicalName, DistinguishedName, Name, Created,
ProtectedFromAccidentalDeletion | `
Sort-Object CanonicalName)

If($? -and $Null -ne $OUs)


{
[int]$OUCount = 0
If($MSWORD -or $PDF)
{
$TableRange = $doc.Application.Selection.Range
[int]$Columns = 6
[int]$Rows = $OUs.Count + 1
[int]$NumOUs = $OUs.Count
[int]$xRow = 1
[int]$UnprotectedOUs = 0 #added in V2.22

$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)


$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat = $wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Name"

$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Created"

$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Protected"

$Table.Cell($xRow,4).Range.Font.Bold = $True
$Table.Cell($xRow,4).Range.Text = "# Users"
$Table.Cell($xRow,5).Range.Font.Bold = $True
$Table.Cell($xRow,5).Range.Text = "# Computers"

$Table.Cell($xRow,6).Range.Font.Bold = $True
$Table.Cell($xRow,6).Range.Text = "# Groups"

ForEach($OU in $OUs)
{
$xRow++
$OUCount++
If($xRow % 2 -eq 0)
{

$Table.Cell($xRow,1).Shading.BackgroundPatternColor = $wdColorGray05

$Table.Cell($xRow,2).Shading.BackgroundPatternColor = $wdColorGray05

$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorGray05

$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorGray05

$Table.Cell($xRow,5).Shading.BackgroundPatternColor = $wdColorGray05

$Table.Cell($xRow,6).Shading.BackgroundPatternColor = $wdColorGray05
}
$OUDisplayName =
$OU.CanonicalName.SubString($OU.CanonicalName.IndexOf("/")+1)
Write-Verbose "$(Get-Date): `t`tProcessing OU $
($OU.CanonicalName) - OU # $OUCount of $NumOUs"

#get counts of users, computers and groups in the OU

[int]$UserCount = 0
[int]$ComputerCount = 0
[int]$GroupCount = 0

#V2.20 changed to @()


$Results = @(Get-ADUser -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$UserCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADComputer -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$ComputerCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADGroup -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$GroupCount = $Results.Count

$Table.Cell($xRow,1).Range.Text = $OUDisplayName
$Table.Cell($xRow,2).Range.Text =
$OU.Created.ToString()
If($OU.ProtectedFromAccidentalDeletion -eq $True)
{
$Table.Cell($xRow,3).Range.Text = "Yes"
}
Else
{
$Table.Cell($xRow,3).Range.Text = "No"
#not added in V2.22 now

##$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorYellow
$Table.Cell($xRow,3).Range.Font.Bold = $True
$UnprotectedOUs++
}

[string]$UserCountStr = "{0,7:N0}" -f $UserCount


[string]$ComputerCountStr = "{0,7:N0}" -f
$ComputerCount
[string]$GroupCountStr = "{0,7:N0}" -f $GroupCount

$Table.Cell($xRow,4).Range.ParagraphFormat.Alignment
= $wdCellAlignVerticalTop
$Table.Cell($xRow,4).Range.ParagraphFormat.Alignment
= $wdAlignParagraphRight
$Table.Cell($xRow,4).Range.Text = $UserCountStr
$Table.Cell($xRow,5).Range.ParagraphFormat.Alignment
= $wdCellAlignVerticalTop
$Table.Cell($xRow,5).Range.ParagraphFormat.Alignment
= $wdAlignParagraphRight
$Table.Cell($xRow,5).Range.Text = $ComputerCountStr
$Table.Cell($xRow,6).Range.ParagraphFormat.Alignment
= $wdCellAlignVerticalTop
$Table.Cell($xRow,6).Range.ParagraphFormat.Alignment
= $wdAlignParagraphRight
$Table.Cell($xRow,6).Range.Text = $GroupCountStr
$Results = $Null
$UserCountStr = $Null
$ComputerCountStr = $Null
$GroupCountStr = $Null
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 214; Break}
2 {$xcol.width = 68; Break}
3 {$xcol.width = 56; Break}
4 {$xcol.width = 56; Break}
5 {$xcol.width = 70; Break}
6 {$xcol.width = 56; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument
#move to the end of the current document
$selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
$Results = $Null
$UserCountStr = $Null
$ComputerCountStr = $Null
$GroupCountStr = $Null

#added in V2.22
If($UnprotectedOUs -gt 0)
{
WriteWordLine 0 0 "There are $($UnprotectedOUs)
unprotected OUs out of $($NumOUs) OUs"
}
}
ElseIf($Text)
{
[int]$NumOUs = $OUs.Count
[int]$UnprotectedOUs = 0 #added in V2.22
#V2.16 addition
[int]$MaxOUNameLength =
($OUs.CanonicalName.SubString($OUs[0].CanonicalName.IndexOf("/")+1) | measure-
object -maximum -property length).maximum

If($MaxOUNameLength -gt 4) #4 is length of "Name"


{
#2 is to allow for spacing between columns
Line 1 ("Name" + (' ' * ($MaxOUNameLength - 2)))
-NoNewLine
Line 0 "Created Protected # Users #
Computers # Groups"
Line 1 ('=' * $MaxOUNameLength) -NoNewLine
Line 0
"==============================================================="
}
Else
{
Line 1 "Name Created Protected #
Users # Computers # Groups"
Line 1
"==================================================================="
}

ForEach($OU in $OUs)
{
$OUCount++
$OUDisplayName =
$OU.CanonicalName.SubString($OU.CanonicalName.IndexOf("/")+1)
Write-Verbose "$(Get-Date): `t`tProcessing OU $
($OU.CanonicalName) - OU # $OUCount of $NumOUs"

#get counts of users, computers and groups in the OU

[int]$UserCount = 0
[int]$ComputerCount = 0
[int]$GroupCount = 0

#V2.20 changed to @()


$Results = @(Get-ADUser -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$UserCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADComputer -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$ComputerCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADGroup -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$GroupCount = $Results.Count

If($OU.ProtectedFromAccidentalDeletion -eq $True)


{
$tmp = "Yes"
}
Else
{
$tmp = "NO"
$UnprotectedOUs++
}
[string]$UserCountStr = "{0,7:N0}" -f $UserCount
[string]$ComputerCountStr = "{0,11:N0}" -f
$ComputerCount
[string]$GroupCountStr = "{0,7:N0}" -f $GroupCount

#V2.16 change
If(($OUDisplayName).Length -lt ($MaxOUNameLength))
{
[int]$NumOfSpaces = ($MaxOUNameLength * -1)
}
Else
{
[int]$NumOfSpaces = -4
}
Line 1 ( "{0,$NumOfSpaces} {1,-22} {2,-9} {3,-7}
{4,-12} {5,-7}" -f $OUDisplayName,$OU.Created.ToString(),$tmp,$UserCountStr,
$ComputerCountStr,$GroupCountStr)

$Results = $Null
$UserCountStr = $Null
$ComputerCountStr = $Null
$GroupCountStr = $Null
}
Line 0 ""
#added in V2.22
If($UnprotectedOUs -gt 0)
{
Line 0 "There are $($UnprotectedOUs) unprotected OUs
out of $($NumOUs) OUs"
}
$Results = $Null
$UserCountStr = $Null
$ComputerCountStr = $Null
$GroupCountStr = $Null
}
ElseIf($HTML)
{
[int]$NumOUs = $OUs.Count
[int]$UnprotectedOUs = 0 #added in V2.22
$rowdata = @()
ForEach($OU in $OUs)
{
$OUCount++
$OUDisplayName =
$OU.CanonicalName.SubString($OU.CanonicalName.IndexOf("/")+1)
Write-Verbose "$(Get-Date): `t`tProcessing OU $
($OU.CanonicalName) - OU # $OUCount of $NumOUs"

#get counts of users, computers and groups in the OU

[int]$UserCount = 0
[int]$ComputerCount = 0
[int]$GroupCount = 0

#V2.20 changed to @()


$Results = @(Get-ADUser -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$UserCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADComputer -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$ComputerCount = $Results.Count

#V2.20 changed to @()


$Results = @(Get-ADGroup -Filter * -SearchBase
$OU.DistinguishedName -Server $Domain -EA 0)
$GroupCount = $Results.Count

[string]$UserCountStr = "{0,7:N0}" -f $UserCount


[string]$ComputerCountStr = "{0,7:N0}" -f
$ComputerCount
[string]$GroupCountStr = "{0,7:N0}" -f $GroupCount

If($OU.ProtectedFromAccidentalDeletion -eq $True)


{
$Protected = "Yes"
$rowdata += @(,(
$OUDisplayName,$htmlwhite,
$OU.Created.ToString(),$htmlwhite,
$Protected,$htmlwhite,
$UserCountStr,$htmlwhite,
$ComputerCountStr,$htmlwhite,
$GroupCountStr,$htmlwhite))
}
Else
{
$Protected = "No"
$UnprotectedOUs++
$rowdata += @(,(
$OUDisplayName,$htmlwhite,
$OU.Created.ToString(),$htmlwhite,
$Protected,$htmlwhite,
$UserCountStr,$htmlwhite,
$ComputerCountStr,$htmlwhite,
$GroupCountStr,$htmlwhite))
}

$Results = $Null
$UserCountStr = $Null
$ComputerCountStr = $Null
$GroupCountStr = $Null
}
$columnHeaders = @('Name',($htmlsilver -bor $htmlbold),
'Created',($htmlsilver -bor
$htmlbold),
'Protected',($htmlsilver -bor
$htmlbold),
'# Users',($htmlsilver -bor
$htmlbold),
'# Computers',($htmlsilver
-bor $htmlbold),
'# Groups',($htmlsilver -bor
$htmlbold)
)
$msg = ""
$columnWidths = @("214","68","56","56","75","56")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "525"
#added in V2.22
If($UnprotectedOUs -gt 0)
{
WriteHTMLLine 0 0 "There are $($UnprotectedOUs)
unprotected OUs out of $($NumOUs) OUs"
}
}
}
ElseIf(!$?)
{
$txt = "Error retrieving OU data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No OU data was retrieved for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
$First = $False
}
}
#endregion

#region Group information


Function ProcessGroupInformation
{
Write-Verbose "$(Get-Date): Writing group data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Groups"
}
ElseIf($Text)
{
Line 0 "/// Groups \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Groups&nbsp;&nbsp;\\\"
}

$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing groups in domain $($Domain)"
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($Domain -eq $Script:ForestRootDomain)


{
$txt = "Domain $($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
}
}
Else
{
$txt = "Domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
}
}

#get all Groups for the domain


$Groups = $Null
$Groups = Get-ADGroup -Filter * -Server $Domain -Properties Name,
GroupCategory, GroupType -EA 0 | Sort-Object Name

If($? -and $Null -ne $Groups)


{
#get counts

Write-Verbose "$(Get-Date): `t`tGetting counts"

[int]$SecurityCount = 0
[int]$DistributionCount = 0
[int]$GlobalCount = 0
[int]$UniversalCount = 0
[int]$DomainLocalCount = 0
[int]$ContactsCount = 0
[int]$GroupsWithSIDHistory = 0

Write-Verbose "$(Get-Date): `t`t`tSecurity Groups"


#V2.20 changed to @()
$Results = @($groups | Where-Object {$_.groupcategory -eq
"Security"})

[int]$SecurityCount = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tDistribution Groups"


#V2.20 changed to @()
$Results = @($groups | Where-Object {$_.groupcategory -eq
"Distribution"})

[int]$DistributionCount = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tGlobal Groups"


#V2.20 changed to @()
$Results = @($groups | Where-Object {$_.groupscope -eq "Global"})

[int]$GlobalCount = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tUniversal Groups"


#V2.20 changed to @()
$Results = @($groups | Where-Object {$_.groupscope -eq
"Universal"})
[int]$UniversalCount = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tDomain Local Groups"


#V2.20 changed to @()
$Results = @($groups | Where-Object {$_.groupscope -eq
"DomainLocal"})

[int]$DomainLocalCount = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tGroups with SID History"


$Results = $Null
#V2.20 changed to @()
$Results = @(Get-ADObject -LDAPFilter "(sIDHistory=*)" -Server
$Domain -Property objectClass, sIDHistory -EA 0)

[int]$GroupsWithSIDHistory = ($Results | Where-Object


{$_.objectClass -eq 'group'}).Count

Write-Verbose "$(Get-Date): `t`t`tContacts"


$Results = $Null
#V2.20 changed to @()
$Results = @(Get-ADObject -LDAPFilter "objectClass=Contact"
-Server $Domain -EA 0)

[int]$ContactsCount = $Results.Count

[string]$TotalCountStr = "{0,7:N0}" -f ($SecurityCount +


$DistributionCount)
[string]$SecurityCountStr = "{0,7:N0}" -f $SecurityCount
[string]$DomainLocalCountStr = "{0,7:N0}" -f $DomainLocalCount
[string]$GlobalCountStr = "{0,7:N0}" -f $GlobalCount
[string]$UniversalCountStr = "{0,7:N0}" -f $UniversalCount
[string]$DistributionCountStr = "{0,7:N0}" -f $DistributionCount
[string]$GroupsWithSIDHistoryStr = "{0,7:N0}" -f
$GroupsWithSIDHistory
[string]$ContactsCountStr = "{0,7:N0}" -f $ContactsCount

Write-Verbose "$(Get-Date): `t`tBuild groups table"


If($MSWORD -or $PDF)
{
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 2
[int]$Rows = 8
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle
$Table.Cell(1,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(1,1).Range.Font.Bold = $True
$Table.Cell(1,1).Range.Text = "Total Groups"
$Table.Cell(1,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(1,2).Range.Text = $TotalCountStr
$Table.Cell(2,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(2,1).Range.Font.Bold = $True
$Table.Cell(2,1).Range.Text = "`tSecurity Groups"
$Table.Cell(2,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(2,2).Range.Text = $SecurityCountStr
$Table.Cell(3,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(3,1).Range.Font.Bold = $True
$Table.Cell(3,1).Range.Text = "`t`tDomain Local"
$Table.Cell(3,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(3,2).Range.Text = $DomainLocalCountStr
$Table.Cell(4,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(4,1).Range.Font.Bold = $True
$Table.Cell(4,1).Range.Text = "`t`tGlobal"
$Table.Cell(4,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(4,2).Range.Text = $GlobalCountStr
$Table.Cell(5,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(5,1).Range.Font.Bold = $True
$Table.Cell(5,1).Range.Text = "`t`tUniversal"
$Table.Cell(5,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(5,2).Range.Text = $UniversalCountStr
$Table.Cell(6,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(6,1).Range.Font.Bold = $True
$Table.Cell(6,1).Range.Text = "`tDistribution Groups"
$Table.Cell(6,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(6,2).Range.Text = $DistributionCountStr
$Table.Cell(7,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(7,1).Range.Font.Bold = $True
$Table.Cell(7,1).Range.Text = "Groups with SID History"
$Table.Cell(7,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(7,2).Range.Text = $GroupsWithSIDHistoryStr
$Table.Cell(8,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(8,1).Range.Font.Bold = $True
$Table.Cell(8,1).Range.Text = "Contacts"
$Table.Cell(8,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(8,2).Range.Text = $ContactsCountStr

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 1 "Total Groups`t`t`t: " $TotalCountStr
Line 1 "`tSecurity Groups`t`t: " $SecurityCountStr
Line 1 "`t`tDomain Local`t: " $DomainLocalCountStr
Line 1 "`t`tGlobal`t`t: " $GlobalCountStr
Line 1 "`t`tUniversal`t: " $UniversalCountStr
Line 1 "`tDistribution Groups`t: " $DistributionCountStr
Line 1 "Groups with SID History`t`t: "
$GroupsWithSIDHistoryStr
Line 1 "Contacts`t`t`t: " $ContactsCountStr
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
$columnHeaders = @("Total Groups",($htmlsilver -bor
$htmlbold),$TotalCountStr,$htmlwhite)
$rowdata += @(,(" Security Groups",($htmlsilver -bor
$htmlbold),$SecurityCountStr,$htmlwhite))
$rowdata += @(,(" Domain Local",($htmlsilver -bor
$htmlbold),$DomainLocalCountStr,$htmlwhite))
$rowdata += @(,(" Global",($htmlsilver -bor
$htmlbold),$GlobalCountStr,$htmlwhite))
$rowdata += @(,(" Universal",($htmlsilver -bor
$htmlbold),$UniversalCountStr,$htmlwhite))
$rowdata += @(,(" Distribution Groups",($htmlsilver
-bor $htmlbold),$DistributionCountStr,$htmlwhite))
$rowdata += @(,("Groups with SID History",($htmlsilver -bor
$htmlbold),$GroupsWithSIDHistoryStr,$htmlwhite))
$rowdata += @(,("Contacts",($htmlsilver -bor $htmlbold),
$ContactsCountStr,$htmlwhite))

$msg = ""
$columnWidths = @("150","75")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "225"
WriteHTMLLine 0 0 " "
}

#get members of privileged groups


$DomainInfo = $Null
$DomainInfo = Get-ADDomain -Identity $Domain -EA 0

If($? -and $Null -ne $DomainInfo)


{
$DomainAdminsSID = "$($DomainInfo.DomainSID)-512"
$EnterpriseAdminsSID = "$($DomainInfo.DomainSID)-519"
$SchemaAdminsSID = "$($DomainInfo.DomainSID)-518"
}
Else
{
$DomainAdminsSID = $Null
$EnterpriseAdminsSID = $Null
$SchemaAdminsSID = $Null
}

Write-Verbose "$(Get-Date): `t`tListing domain admins"


$Admins = $Null
#V2.20 changed to @()
$Admins = @(Get-ADGroupMember -Identity $DomainAdminsSID -Server
$Domain -EA 0)

If($? -and $Null -ne $Admins)


{
[int]$AdminsCount = $Admins.Count
$Admins = $Admins | Sort-Object Name
[string]$AdminsCountStr = "{0:N0}" -f $AdminsCount

If($MSWORD -or $PDF)


{
WriteWordLine 3 0 "Privileged Groups"
WriteWordLine 4 0 "Domain Admins ($($AdminsCountStr)
members):"
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 4
[int]$Rows = $AdminsCount + 1
[int]$xRow = 1
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Name"
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Password Last
Changed"
$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Password Never
Expires"
$Table.Cell($xRow,4).Range.Font.Bold = $True
$Table.Cell($xRow,4).Range.Text = "Account Enabled"
ForEach($Admin in $Admins)
{
$xRow++

$User = Get-ADUser -Identity $Admin.SID -Server


$Domain -Properties PasswordLastSet, Enabled, PasswordNeverExpires -EA 0

If($? -and $Null -ne $User)


{
$Table.Cell($xRow,1).Range.Text =
$User.Name
If($Null -eq $User.PasswordLastSet)
{

$Table.Cell($xRow,2).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Font.Color = $WDColorBlack
$Table.Cell($xRow,2).Range.Text =
"No Date Set"
}
Else
{
$Table.Cell($xRow,2).Range.Text =
(get-date $User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{

$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,3).Range.Font.Bold = $True

$Table.Cell($xRow,3).Range.Font.Color = $WDColorBlack
$Table.Cell($xRow,3).Range.Text =
"True"
}
Else
{
$Table.Cell($xRow,3).Range.Text =
"False"
}
If($User.Enabled -eq $True)
{

$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,4).Range.Font.Bold = $True

$Table.Cell($xRow,4).Range.Font.Color = $WDColorBlack
$Table.Cell($xRow,4).Range.Text =
"True"
}
Else
{
$Table.Cell($xRow,4).Range.Text =
"False"
}
}
Else
{
$Table.Cell($xRow,1).Range.Text =
$Admin.SID
$Table.Cell($xRow,2).Range.Text =
"Unknown"
$Table.Cell($xRow,3).Range.Text =
"Unknown"
$Table.Cell($xRow,4).Range.Text =
"Unknown"
}
}

#set column widths


$xcols = $table.columns
ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 200; Break}
2 {$xcol.width = 66; Break}
3 {$xcol.width = 56; Break}
4 {$xcol.width = 56; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 0 "Privileged Groups"
Line 1 "Domain Admins ($AdminsCountStr members):"
#V2.16 addition
Line 2 "
Password Password "
Line 2 "
Last Never Account"
Line 2 "Name
Changed Expires Enabled"
Line 2
"================================================================================="
ForEach($Admin in $Admins)
{
$User = Get-ADUser -Identity $Admin.SID -Server
$Domain -Properties PasswordLastSet, Enabled, PasswordNeverExpires -EA 0

If($? -and $Null -ne $User)


{
If($Null -eq $User.PasswordLastSet)
{
$PasswordLastSet = "No Date Set"
}
Else
{
$PasswordLastSet = (get-date
$User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{
$PasswordNeverExpires = "True"
}
Else
{
$PasswordNeverExpires = "False"
}
If($User.Enabled -eq $True)
{
$UserEnabled = "True"
}
Else
{
$UserEnabled = "False"
}
#V2.16 change
Line 2 ( "{0,-50} {1,-11} {2,-10} {3,-5}"
-f $User.Name,$PasswordLastSet,$PasswordNeverExpires,$UserEnabled)
}
Else
{
#V2.16 change
Line 2 ( "{0,-50} {1,-11} {2,-10} {3,-5}"
-f $Admin.SID,"Unknown","Unknown","Unknown")
}
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Privileged Groups"
WriteHTMLLine 4 0 "Domain Admins ($($AdminsCountStr)
members):"
$rowdata = @()
ForEach($Admin in $Admins)
{
$User = Get-ADUser -Identity $Admin.SID -Server
$Domain -Properties PasswordLastSet, Enabled, PasswordNeverExpires -EA 0

If($? -and $Null -ne $User)


{
$UserName = $User.Name
If($Null -eq $User.PasswordLastSet)
{
$PasswordLastSet = "No Date Set"
}
Else
{
$PasswordLastSet = (get-date
$User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{
$PasswordNeverExpires = "True"
}
Else
{
$PasswordNeverExpires = "False"
}
If($User.Enabled -eq $True)
{
$Enabled = "True"
}
Else
{
$Enabled = "False"
}
}
Else
{
$UserName = $Admin.SID
$PasswordLastSet = "Unknown"
$PasswordNeverExpires = "Unknown"
$Enabled = "Unknown"
}
$rowdata += @(,(
$UserName,$htmlwhite,
$PasswordLastSet,$htmlwhite,
$PasswordNeverExpires,$htmlwhite,
$Enabled,$htmlwhite))
}

$columnHeaders = @(
'Name',($htmlsilver -bor $htmlbold),
'Password Last Changed',($htmlsilver -bor $htmlbold),
'Password Never Expires',($htmlsilver -bor
$htmlbold),
'Account Enabled',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("200","66","56","56")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "378"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
$txt = "Unable to retrieve Domain Admins group membership"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "Domain Admins: <None>"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Domain Admins: None"
}
}

If($Domain -eq $Script:ForestRootDomain)


{
Write-Verbose "$(Get-Date): `t`tListing enterprise admins"

#V2.20 changed to @()


$Admins = @(Get-ADGroupMember -Identity
$EnterpriseAdminsSID -Server $Domain -EA 0)

If($? -and $Null -ne $Admins)


{
[int]$AdminsCount = $Admins.Count
$Admins = $Admins | Sort-Object Name
[string]$AdminsCountStr = "{0:N0}" -f $AdminsCount

If($MSWORD -or $PDF)


{
WriteWordLine 4 0 "Enterprise Admins ($
($AdminsCountStr) members):"
$TableRange =
$Script:doc.Application.Selection.Range
[int]$Columns = 5
[int]$Rows = $AdminsCount + 1
[int]$xRow = 1
$Table = $Script:doc.Tables.Add($TableRange,
$Rows, $Columns)
$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle =
$wdLineStyleSingle
$Table.Borders.OutsideLineStyle =
$wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Name"
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Domain"
$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Password
Last Changed"
$Table.Cell($xRow,4).Range.Font.Bold = $True
$Table.Cell($xRow,4).Range.Text = "Password
Never Expires"
$Table.Cell($xRow,5).Range.Font.Bold = $True
$Table.Cell($xRow,5).Range.Text = "Account
Enabled"
ForEach($Admin in $Admins)
{
$xRow++
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}

If($Admin.ObjectClass -eq 'user')


{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{

$Table.Cell($xRow,1).Range.Text = $User.Name

$Table.Cell($xRow,2).Range.Text = $xServer
If($Null -eq
$User.PasswordLastSet)
{

$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,3).Range.Font.Bold = $True

$Table.Cell($xRow,3).Range.Font.Color = $WDColorBlack

$Table.Cell($xRow,3).Range.Text = "No Date Set"


}
Else
{

$Table.Cell($xRow,3).Range.Text = (get-date $User.PasswordLastSet -f d)


}
If($User.PasswordNeverExpires
-eq $True)
{

$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,4).Range.Font.Bold = $True

$Table.Cell($xRow,4).Range.Font.Color = $WDColorBlack
}

$Table.Cell($xRow,4).Range.Text = $User.PasswordNeverExpires.ToString()
If($User.Enabled -eq $False)
{

$Table.Cell($xRow,5).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,5).Range.Font.Bold = $True

$Table.Cell($xRow,5).Range.Font.Color = $WDColorBlack
}

$Table.Cell($xRow,5).Range.Text = $User.Enabled.ToString()
}
ElseIf($Admin.ObjectClass -eq
'group')
{

$Table.Cell($xRow,1).Range.Text = "$($User.Name) (group)"

$Table.Cell($xRow,2).Range.Text = $xServer

$Table.Cell($xRow,3).Range.Text = "N/A"

$Table.Cell($xRow,4).Range.Text = "N/A"

$Table.Cell($xRow,5).Range.Text = "N/A"
}

}
Else
{
$Table.Cell($xRow,1).Range.Text =
$Admin.SID.Value
$Table.Cell($xRow,2).Range.Text =
$xServer
$Table.Cell($xRow,3).Range.Text =
"Unknown"
$Table.Cell($xRow,4).Range.Text =
"Unknown"
$Table.Cell($xRow,5).Range.Text =
"Unknown"
}
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 100; Break}
2 {$xcol.width = 108; Break}
3 {$xcol.width = 66; Break}
4 {$xcol.width = 56; Break}
5 {$xcol.width = 56; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document

$Script:doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) |
Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 1 "Enterprise Admins ($AdminsCountStr
members):"
#V2.16 addition
Line 2 "
Password Password "
Line 2 "
Last Never Account"
Line 2 "Name
Domain Changed Expires Enabled"
Line 2
"==================================================================================
========================="
ForEach($Admin in $Admins)
{
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}

If($Admin.ObjectClass -eq 'user')


{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{
If($Null -eq
$User.PasswordLastSet)
{
$PasswordLastSet = "No
Date Set"
}
Else
{
$PasswordLastSet = (get-
date $User.PasswordLastSet -f d)
}
#V2.16 change
Line 2 ( "{0,-50} {1,-25}
{2,-10} {3,-10} {4,-5}" -f $User.Name,$xServer,$PasswordLastSet,
$User.PasswordNeverExpires.ToString(),$User.Enabled.ToString())
}
ElseIf($Admin.ObjectClass -eq
'group')
{
#V2.16 change
Line 2 ( "{0,-43} (group)
{1,-25} {2,-10} {3,-10} {4,-5}" -f $User.Name,$xServer,"N/A","N/A","N/A")
}
}
Else
{
#v2.16 change
Line 2 ( "{0,-50} {1,-25} {2,-10}
{3,-10} {4,-5}" -f $Admin.SID.Value,$xServer,"Unknown","Unknown","Unknown")
}
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Enterprise Admins ($
($AdminsCountStr) members):"
$rowdata = @()
ForEach($Admin in $Admins)
{
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}

If($Admin.ObjectClass -eq 'user')


{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{
$UserName = $User.Name
$Domain = $xServer
If($Null -eq
$User.PasswordLastSet)
{
$PasswordLastSet = "No
Date Set"
}
Else
{
$PasswordLastSet = (get-
date $User.PasswordLastSet -f d)
}
$PasswordNeverExpires =
$User.PasswordNeverExpires.ToString()
$Enabled =
$User.Enabled.ToString()
}
ElseIf($Admin.ObjectClass -eq
'group')
{
$UserName = "$($User.Name)
(group)"
$Domain = $xServer
$PasswordLastSet = "N/A"
$PasswordNeverExpires = "N/A"
$Enabled = "N/A"
}

}
Else
{
$UserName = $Admin.SID.Value
$Domain = $xServer
$PasswordLastSet = "Unknown"
$PasswordNeverExpires = "Unknown"
$Enabled = "Unknown"
}
$rowdata += @(,(
$UserName,$htmlwhite,
$Domain,$htmlwhite,
$PasswordLastSet,$htmlwhite,
$PasswordNeverExpires,$htmlwhite,
$Enabled,$htmlwhite))
}

$columnHeaders = @(
'Name',($htmlsilver -bor $htmlbold),
'Domain',($htmlsilver -bor $htmlbold),
'Password Last Changed',($htmlsilver -bor
$htmlbold),
'Password Never Expires',($htmlsilver -bor
$htmlbold),
'Account Enabled',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("100","108","66","56","56")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata
-columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "386"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
$txt1 = "Enterprise Admins:"
$txt2 = "Unable to retrieve Enterprise Admins group
membership"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLine 0 0 $txt2 "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLine 0 0 $txt2
}
}
Else
{
$txt1 = "Enterprise Admins:"
$txt2 = "<None>"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLine 0 0 $txt2
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLine 0 0 "None"
}
}
}

If($Domain -eq $Script:ForestRootDomain)


{
Write-Verbose "$(Get-Date): `t`tListing schema admins"

#V2.20 changed to @()


$Admins = @(Get-ADGroupMember -Identity $SchemaAdminsSID
-Server $Domain -EA 0)

If($? -and $Null -ne $Admins)


{
[int]$AdminsCount = $Admins.Count
$Admins = $Admins | Sort-Object Name
[string]$AdminsCountStr = "{0:N0}" -f $AdminsCount

If($MSWORD -or $PDF)


{
WriteWordLine 4 0 "Schema Admins ($
($AdminsCountStr) members): "
$TableRange =
$Script:doc.Application.Selection.Range
[int]$Columns = 5
[int]$Rows = $AdminsCount + 1
[int]$xRow = 1
$Table = $Script:doc.Tables.Add($TableRange,
$Rows, $Columns)
$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle =
$wdLineStyleSingle
$Table.Borders.OutsideLineStyle =
$wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Name"
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Domain"
$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Password
Last Changed"
$Table.Cell($xRow,4).Range.Font.Bold = $True
$Table.Cell($xRow,4).Range.Text = "Password
Never Expires"
$Table.Cell($xRow,5).Range.Font.Bold = $True
$Table.Cell($xRow,5).Range.Text = "Account
Enabled"
ForEach($Admin in $Admins)
{
$xRow++
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}
If($Admin.ObjectClass -eq 'user')
{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{

$Table.Cell($xRow,1).Range.Text = $User.Name

$Table.Cell($xRow,2).Range.Text = $xServer
If($Null -eq
$User.PasswordLastSet)
{

$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,3).Range.Font.Bold = $True

$Table.Cell($xRow,3).Range.Font.Color = $WDColorBlack

$Table.Cell($xRow,3).Range.Text = "No Date Set"


}
Else
{

$Table.Cell($xRow,3).Range.Text = (get-date $User.PasswordLastSet -f d)


}
If($User.PasswordNeverExpires
-eq $True)
{

$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,4).Range.Font.Bold = $True

$Table.Cell($xRow,4).Range.Font.Color = $WDColorBlack
}

$Table.Cell($xRow,4).Range.Text = $User.PasswordNeverExpires.ToString()
If($User.Enabled -eq $False)
{

$Table.Cell($xRow,5).Shading.BackgroundPatternColor = $wdColorRed
$Table.Cell($xRow,5).Range.Font.Bold = $True

$Table.Cell($xRow,5).Range.Font.Color = $WDColorBlack
}

$Table.Cell($xRow,5).Range.Text = $User.Enabled.ToString()

#$Table.Cell($xRow,6).Range.Text = ""
}
ElseIf($Admin.ObjectClass -eq
'group')
{

$Table.Cell($xRow,1).Range.Text = "$($User.Name) (group)"

$Table.Cell($xRow,2).Range.Text = $xServer

$Table.Cell($xRow,3).Range.Text = "N/A"

$Table.Cell($xRow,4).Range.Text = "N/A"

$Table.Cell($xRow,5).Range.Text = "N/A"
}

}
Else
{
$Table.Cell($xRow,1).Range.Text =
$Admin.SID.Value
$Table.Cell($xRow,2).Range.Text =
$xServer
$Table.Cell($xRow,3).Range.Text =
"Unknown"
$Table.Cell($xRow,4).Range.Text =
"Unknown"
$Table.Cell($xRow,5).Range.Text =
"Unknown"
}
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 100; Break}
2 {$xcol.width = 108; Break}
3 {$xcol.width = 66; Break}
4 {$xcol.width = 56; Break}
5 {$xcol.width = 56; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)
#return focus back to document

$Script:doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) |
Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 1 "Schema Admins ($($AdminsCountStr)
members): "
#V2.16 addition
Line 2 "
Password Password "
Line 2 "
Last Never Account"
Line 2 "Name
Domain Changed Expires Enabled"
Line 2
"==================================================================================
========================="
ForEach($Admin in $Admins)
{
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}

If($Admin.ObjectClass -eq 'user')


{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{
If($Null -eq
$User.PasswordLastSet)
{
$PasswordLastSet = "No
Date Set"
}
Else
{
$PasswordLastSet = (get-
date $User.PasswordLastSet -f d)
}
#V2.16 change
Line 2 ( "{0,-50} {1,-25}
{2,-10} {3,-10} {4,-5}" -f $User.Name,$xServer,$PasswordLastSet,
$User.PasswordNeverExpires.ToString(),$User.Enabled.ToString())
}
ElseIf($Admin.ObjectClass -eq
'group')
{
#V2.16 change
Line 2 ( "{0,-43} (group)
{1,-25} {2,-10} {3,-10} {4,-5}" -f $User.Name,$xServer,"N/A","N/A","N/A")
}
}
Else
{
#v2.16 change
Line 2 ( "{0,-50} {1,-25} {2,-10}
{3,-10} {4,-5}" -f $Admin.SID.Value,$xServer,"Unknown","Unknown","Unknown")
}
}
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
WriteHTMLLine 4 0 "Schema Admins ($
($AdminsCountStr) members): "
ForEach($Admin in $Admins)
{
$xArray =
$Admin.DistinguishedName.Split(",")
$xServer = ""
$xCnt = 0
ForEach($xItem in $xArray)
{
$xCnt++
If($xItem.StartsWith("DC="))
{
$xtmp =
$xItem.Substring($xItem.IndexOf("=")+1)
If($xCnt -eq $xArray.Count)
{
$xServer += $xTmp
}
Else
{
$xServer += "$($xTmp)."
}
}
}

If($Admin.ObjectClass -eq 'user')


{
$User = Get-ADUser -Identity
$Admin.SID.value -Server $xServer `
-Properties PasswordLastSet,
Enabled, PasswordNeverExpires -EA 0
}
ElseIf($Admin.ObjectClass -eq 'group')
{
$User = Get-ADGroup -Identity
$Admin.SID.value -Server $xServer -EA 0
}
Else
{
$User = $Null
}

If($? -and $Null -ne $User)


{
If($Admin.ObjectClass -eq 'user')
{
$UserName = $User.Name
$Domain = $xServer
If($Null -eq
$User.PasswordLastSet)
{
$PasswordLastSet = "No
Date Set"
}
Else
{
$PasswordLastSet = (get-
date $User.PasswordLastSet -f d)
}
$PasswordNeverExpires =
$User.PasswordNeverExpires.ToString()
$Enabled =
$User.Enabled.ToString()
}
ElseIf($Admin.ObjectClass -eq
'group')
{
$UserName = "$($User.Name)
(group)"
$Domain = $xServer
$PasswordLastSet = "N/A"
$PasswordNeverExpires = "N/A"
$Enabled = "N/A"
}

}
Else
{
$UserName = $Admin.SID.Value
$Domain = $xServer
$PasswordLastSet = "Unknown"
$PasswordNeverExpires = "Unknown"
$Enabled = "Unknown"
}
$rowdata += @(,(
$UserName,$htmlwhite,
$Domain,$htmlwhite,
$PasswordLastSet,$htmlwhite,
$PasswordNeverExpires,$htmlwhite,
$Enabled,$htmlwhite))
}

$columnHeaders = @(
'Name',($htmlsilver -bor $htmlbold),
'Domain',($htmlsilver -bor $htmlbold),
'Password Last Changed',($htmlsilver -bor
$htmlbold),
'Password Never Expires',($htmlsilver -bor
$htmlbold),
'Account Enabled',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("100","108","66","56","56")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata
-columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "386"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
$txt1 = "Schema Admins: "
$txt2 = "Unable to retrieve Schema Admins group
membership"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLine 0 0 $txt2 "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLine 0 0 $txt2
}
}
Else
{
$txt1 = "Schema Admins: "
$txt2 = "<None>"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLine 0 0 $txt2
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLine 0 0 "None"
}
}
}

#http://www.shariqsheikh.com/blog/index.php/200908/use-
powershell-to-look-up-admincount-from-adminsdholder-and-sdprop/
Write-Verbose "$(Get-Date): `t`tListing users with AdminCount=1"
#V2.20 changed to @()
$AdminCounts = @(Get-ADUser -LDAPFilter "(admincount=1)" -Server
$Domain -EA 0)

If($? -and $Null -ne $AdminCounts)


{
$AdminCounts = $AdminCounts | Sort-Object Name
[int]$AdminsCount = $AdminCounts.Count
[string]$AdminsCountStr = "{0:N0}" -f $AdminsCount

If($MSWORD -or $PDF)


{
WriteWordLine 4 0 "Users with AdminCount=1
($AdminsCountStr users):"
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 4
[int]$Rows = $AdminCounts.Count + 1
[int]$xRow = 1
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Name"
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Password Last
Changed"
$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Password Never
Expires"
$Table.Cell($xRow,4).Range.Font.Bold = $True
$Table.Cell($xRow,4).Range.Text = "Account Enabled"
ForEach($Admin in $AdminCounts)
{
$User = Get-ADUser -Identity $Admin.SID -Server
$Domain `
-Properties PasswordLastSet, Enabled,
PasswordNeverExpires -EA 0

$xRow++

If($? -and $Null -ne $User)


{
$Table.Cell($xRow,1).Range.Text =
$User.Name
If($Null -eq $User.PasswordLastSet)
{

$Table.Cell($xRow,2).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,2).Range.Font.Bold = $True

$Table.Cell($xRow,2).Range.Font.Color = $WDColorBlack
$Table.Cell($xRow,2).Range.Text =
"No Date Set"
}
Else
{
$Table.Cell($xRow,2).Range.Text =
(get-date $User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{

$Table.Cell($xRow,3).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,3).Range.Font.Bold = $True

$Table.Cell($xRow,3).Range.Font.Color = $WDColorBlack
}
$Table.Cell($xRow,3).Range.Text =
$User.PasswordNeverExpires.ToString()
If($User.Enabled -eq $False)
{

$Table.Cell($xRow,4).Shading.BackgroundPatternColor = $wdColorRed

$Table.Cell($xRow,4).Range.Font.Bold = $True

$Table.Cell($xRow,4).Range.Font.Color = $WDColorBlack
}
$Table.Cell($xRow,4).Range.Text =
$User.Enabled.ToString()
}
Else
{
$Table.Cell($xRow,2).Range.Text =
$Admin.SID
$Table.Cell($xRow,3).Range.Text =
"Unknown"
$Table.Cell($xRow,4).Range.Text =
"Unknown"
}
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 200; Break}
2 {$xcol.width = 66; Break}
3 {$xcol.width = 56; Break}
4 {$xcol.width = 56; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 1 "Users with AdminCount=1 ($AdminsCountStr
users):"
#V2.16 addition
Line 2 "
Password Password "
Line 2 "
Last Never Account"
Line 2 "Name
Changed Expires Enabled"
Line 2
"================================================================================"
ForEach($Admin in $AdminCounts)
{
$User = Get-ADUser -Identity $Admin.SID -Server
$Domain `
-Properties PasswordLastSet, Enabled,
PasswordNeverExpires -EA 0

If($? -and $Null -ne $User)


{
If($Null -eq $User.PasswordLastSet)
{
$PasswordLastSet = "No Date Set"
}
Else
{
$PasswordLastSet = (get-date
$User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{
$PasswordNeverExpires = "True"
}
Else
{
$PasswordNeverExpires = "False"
}
If($User.Enabled -eq $False)
{
$UserEnabled = "True"
}
Else
{
$UserEnabled = "False"
}
#V2.16 change
Line 2 ( "{0,-50} {1,-10} {2,-10} {3,-5}"
-f $User.Name,$PasswordLastSet,$PasswordNeverExpires,$UserEnabled)
}
Else
{
#V2.16 change
Line 2 ( "{0,-50} {1,-10} {2,-10} {3,-5}"
-f $Admin.SID,"Unknown","Unknown","Unknown")
}
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Users with AdminCount=1 ($
($AdminsCountStr) users):"
$rowdata = @()
ForEach($Admin in $AdminCounts)
{
$User = Get-ADUser -Identity $Admin.SID -Server
$Domain `
-Properties PasswordLastSet, Enabled,
PasswordNeverExpires -EA 0

If($? -and $Null -ne $User)


{
$UserName = $User.Name
If($Null -eq $User.PasswordLastSet)
{
$PasswordLastSet = "No Date Set"
}
Else
{
$PasswordLastSet = (get-date
$User.PasswordLastSet -f d)
}
If($User.PasswordNeverExpires -eq $True)
{
$PasswordNeverExpires = "True"
}
Else
{
$PasswordNeverExpires = "False"
}
If($User.Enabled -eq $True)
{
$Enabled = "True"
}
Else
{
$Enabled = "False"
}
}
Else
{
$UserName = $Admin.SID
$PasswordLastSet = "Unknown"
$PasswordNeverExpires = "Unknown"
$Enabled = "Unknown"
}
$rowdata += @(,(
$UserName,$htmlwhite,
$PasswordLastSet,$htmlwhite,
$PasswordNeverExpires,$htmlwhite,
$Enabled,$htmlwhite))
}
$columnHeaders = @(
'Name',($htmlsilver -bor $htmlbold),
'Password Last Changed',($htmlsilver -bor $htmlbold),
'Password Never Expires',($htmlsilver -bor
$htmlbold),
'Account Enabled',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("200","66","56","56")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "378"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
$txt = "Unable to retrieve users with AdminCount=1"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt1 = "Users with AdminCount=1: "
$txt2 = "<None>"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLIne 0 0 $txt2
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLIne 0 0 "None"
}
}

Write-Verbose "$(Get-Date): `t`tListing groups with AdminCount =


1"
#V2.20 changed to @()
$AdminCounts = @(Get-ADGroup -LDAPFilter "(admincount=1)" -Server
$Domain -EA 0 | Select-Object Name)

If($? -and $Null -ne $AdminCounts)


{
$AdminCounts = $AdminCounts | Sort-Object Name
[int]$AdminsCount = $AdminCounts.Count
[string]$AdminsCountStr = "{0:N0}" -f $AdminsCount

If($MSWORD -or $PDF)


{
WriteWordLine 4 0 "Groups with AdminCount=1 ($
($AdminsCountStr) members):"
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 2
[int]$Rows = $AdminCounts.Count + 1
[int]$xRow = 1
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle
$Table.Rows.First.Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Group Name"
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Members"
ForEach($Admin in $AdminCounts)
{
Write-Verbose "$(Get-Date): `t`t`t$
($Admin.Name)"
$xRow++
#V2.20 changed to @()
$Members = @(Get-ADGroupMember -Identity
$Admin.Name -Server $Domain -EA 0 | Sort-Object Name)

If($? -and $Null -ne $Members)


{
[int]$MembersCount = $Members.Count
}
Else
{
[int]$MembersCount = 0
}

[string]$MembersCountStr = "{0:N0}" -f
$MembersCount
$Table.Cell($xRow,1).Range.Text = "$
($Admin.Name) ($($MembersCountStr) members)"
$MbrStr = ""
If($MembersCount -gt 0)
{
ForEach($Member in $Members)
{
$MbrStr += "$($Member.Name)`r"
}
$Table.Cell($xRow,2).Range.Text = $MbrStr
}
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 200; Break}
2 {$xcol.width = 172; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 1 "Groups with AdminCount=1 ($($AdminsCountStr)
members):"
ForEach($Admin in $AdminCounts)
{
Write-Verbose "$(Get-Date): `t`t`t$
($Admin.Name)"
#V2.20 changed to @()
$Members = @(Get-ADGroupMember -Identity
$Admin.Name -Server $Domain -EA 0 | Sort-Object Name)

If($? -and $Null -ne $Members)


{
[int]$MembersCount = $Members.Count
}
Else
{
[int]$MembersCount = 0
}

[string]$MembersCountStr = "{0:N0}" -f
$MembersCount
Line 2 "Group Name`t: $($Admin.Name) ($
($MembersCountStr) members)"
$MbrStr = ""
If($MembersCount -gt 0)
{
Line 2 "Members`t`t: " -NoNewLine
$cnt = 0
ForEach($Member in $Members)
{
$cnt++

If($cnt -eq 1)
{
Line 0 $Member.Name
}
Else
{
Line 4 " $($Member.Name)"
}
}
}
Else
{
Line 2 "Members`t`t: None"
}
Line 0 ""
}
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 "Groups with AdminCount=1 ($
($AdminsCountStr) members):"
$rowdata = @()
ForEach($Admin in $AdminCounts)
{
Write-Verbose "$(Get-Date): `t`t`t$
($Admin.Name)"
#V2.20 changed to @()
$Members = @(Get-ADGroupMember -Identity
$Admin.Name -Server $Domain -EA 0 | Sort-Object Name)

If($? -and $Null -ne $Members)


{
$MembersCount = $Members.Count
}
Else
{
[int]$MembersCount = 0
}

[string]$MembersCountStr = "{0:N0}" -f
$MembersCount
$GroupName = "$($Admin.Name) ($
($MembersCountStr) members)"
$MbrStr = ""
If($MembersCount -gt 0)
{
$cnt = 0
ForEach($Member in $Members)
{
$cnt++

If($cnt -eq 1)
{
$rowdata += @(,(
$GroupName,$htmlwhite,
$Member.Name,$htmlwhite))
}
Else
{
$rowdata += @(,(
"",$htmlwhite,
$Member.Name,$htmlwhite))
}
}
}
Else
{
$rowdata += @(,(
$GroupName,$htmlwhite,
"",$htmlwhite))
}
}
$columnHeaders = @(
'Group Name',($htmlsilver -bor $htmlbold),
'Members',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("200","172")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "372"
WriteHTMLLine 0 0 " "
}
}
ElseIf(!$?)
{
$txt = "Unable to retrieve Groups with AdminCount=1"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt1 = "Groups with AdminCount=1: "
$txt2 = "<None>"
If($MSWORD -or $PDF)
{
WriteWordLine 4 0 $txt1
WriteWordLine 0 0 $txt2
}
ElseIf($Text)
{
Line 0 $txt1
Line 0 $txt2
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $txt1
WriteHTMLLine 0 0 "None"
}
}
}
ElseIf(!$?)
{
$txt = "Error retrieving Group data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No Group data was retrieved for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
$First = $False
}
}
#endregion

#region GPOs by domain


Function ProcessGPOsByDomain
{
Write-Verbose "$(Get-Date): Writing domain group policy data"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Group Policies by Domain"
}
ElseIf($Text)
{
Line 0 "/// Group Policies by Domain \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Group Policies by
Domain&nbsp;&nbsp;\\\"
}
$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing group policies for domain $
($Domain)"

$DomainInfo = Get-ADDomain -Identity $Domain -EA 0

If($? -and $Null -ne $DomainInfo)


{
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($Domain -eq $Script:ForestRootDomain)


{
$txt = "$($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 1 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$
($txt)&nbsp;&nbsp;\\\"
}
}
Else
{
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $Domain
}
ElseIf($Text)
{
Line 1 "/// $($Domain) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$
($Domain)&nbsp;&nbsp;\\\"
}
}

Write-Verbose "$(Get-Date): `t`tGetting linked GPOs"


If($MSWORD -or $PDF)
{
WriteWordLine 3 0 "Linked Group Policy Objects"
}
ElseIf($Text)
{
Line 0 "Linked Group Policy Objects"
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "Linked Group Policy Objects"
}

#V2.20 changed to @()


$LinkedGPOs = @($DomainInfo.LinkedGroupPolicyObjects | Sort-
Object)
If($Null -eq $LinkedGpos)
{
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "<None>"
}
ElseIf($Text)
{
Line 2 "<None>"
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "None"
}
}
Else
{
$GPOArray = New-Object System.Collections.ArrayList
ForEach($LinkedGpo in $LinkedGpos)
{
#taken from Michael B. Smith's work on the XenApp 6.x
scripts
#this way we don't need the GroupPolicy module
$gpObject = [ADSI]( "LDAP://" + $LinkedGPO )
If($Null -eq $gpObject.DisplayName)
{
$p1 = $LinkedGPO.IndexOf("{")
#38 is length of guid (32) plus the four "-"s
plus the beginning "{" plus the ending "}"
$GUID = $LinkedGPO.SubString($p1,38)
$tmp = "GPO with GUID $($GUID) was not found in
this domain"
}
Else
{
$tmp = $gpObject.DisplayName ### name of the
group policy object
}
$GPOArray.Add($tmp) > $Null
}

$GPOArray = $GPOArray | Sort-Object

If($MSWORD -or $PDF)


{
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 1
[int]$Rows = $LinkedGpos.Count
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle = $wdLineStyleNone
$Table.Borders.OutsideLineStyle = $wdLineStyleNone

[int]$xRow = 0
ForEach($Item in $GPOArray)
{
$xRow++
$Table.Cell($xRow,1).Range.Text = $Item
}

$Table.Rows.SetLeftIndent($Indent0TabStops,
$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
ForEach($Item in $GPOArray)
{
Line 2 $Item
}
Line 0 ""
}
ElseIf($HTML)
{
$rowdata = @()
ForEach($Item in $GPOArray)
{
$rowdata += @(,($Item,$htmlwhite))
}
$columnHeaders = @('Name',($htmlsilver -bor
$htmlbold))
$msg = ""
FormatHTMLTable $msg "auto" -rowArray $rowdata
-columnArray $columnHeaders
WriteHTMLLine 0 0 " "
}
$GPOArray = $Null
}
$LinkedGPOs = $Null
$First = $False
}
ElseIf(!$?)
{
$txt = "Error retrieving domain data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No Domain data was retrieved for domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
}
}
#endregion

#region group policies by organizational units


Function ProcessgGPOsByOUOld
{
Write-Verbose "$(Get-Date): Writing Group Policy data by Domain by OU"
If($MSWORD -or $PDF)
{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Group Policies by Organizational Unit"
}
ElseIf($Text)
{
Line 0 "/// Group Policies by Organizational Unit \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Group Policies by Organizational
Unit&nbsp;&nbsp;\\\"
}
$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing domain $($Domain)"
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

$Disclaimer = "(Contains only OUs with linked Group Policies)"


If($Domain -eq $Script:ForestRootDomain)
{
$txt = "Group Policies by OUs in Domain $($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
#print disclaimer line in 8 point bold italics
WriteWordLine 0 0 $Disclaimer "" $Null 8 $True $True
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
Line 0 $Disclaimer
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
WriteHTMLLine 0 0 $Disclaimer "" $Null 1 $True $True
}
}
Else
{
$txt = "Group Policies by OUs in Domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
WriteWordLine 0 0 $Disclaimer "" $Null 8 $True $True
}
ElseIf($Text)
{
Line 1 "/// $($txt) \\\"
Line 1 $Disclaimer
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
WriteHTMLLine 0 0 $Disclaimer "" $Null 1 $True $True
}
}

#get all OUs for the domain


#V2.20 changed to @()
$OUs = @(Get-ADOrganizationalUnit -Filter * -Server $Domain `
-Properties CanonicalName, DistinguishedName, Name -EA 0 | `
Select-Object CanonicalName, DistinguishedName, Name | Sort-Object
CanonicalName)

If($? -and $Null -ne $OUs)


{
[int]$NumOUs = $OUs.Count
[int]$OUCount = 0

ForEach($OU in $OUs)
{
$OUCount++
$OUDisplayName =
$OU.CanonicalName.SubString($OU.CanonicalName.IndexOf("/")+1)
Write-Verbose "$(Get-Date): `t`tProcessing OU $
($OU.CanonicalName) - OU # $OUCount of $NumOUs"

#get data for the individual OU


$OUInfo = Get-ADOrganizationalUnit -Identity
$OU.DistinguishedName -Server $Domain -Properties * -EA 0

If($? -and $Null -ne $OUInfo)


{
Write-Verbose "$(Get-Date): `t`t`tGetting linked
GPOs"
$LinkedGPOs = $OUInfo.LinkedGroupPolicyObjects |
Sort-Object
If($Null -eq $LinkedGpos)
{
# do nothing
}
Else
{
$GPOArray = New-Object
System.Collections.ArrayList
ForEach($LinkedGpo in $LinkedGpos)
{
#taken from Michael B. Smith's work on
the XenApp 6.x scripts
#this way we don't need the GroupPolicy
module
$gpObject = [ADSI]( "LDAP://" +
$LinkedGPO )
If($Null -eq $gpObject.DisplayName)
{
$p1 = $LinkedGPO.IndexOf("{")
#38 is length of guid (32) plus the
four "-"s plus the beginning "{" plus the ending "}"
$GUID =
$LinkedGPO.SubString($p1,38)
$tmp = "GPO with GUID $($GUID) was
not found in this domain"
}
Else
{
$tmp = $gpObject.DisplayName ###
name of the group policy object
}
$GPOArray.Add($tmp) > $Null
}

$GPOArray = $GPOArray | Sort-Object

[int]$Rows = $LinkedGpos.Count

If($MSWORD -or $PDF)


{
[int]$Columns = 1
WriteWordLine 3 0 "$($OUDisplayName) ($
($Rows))"
$TableRange =
$Script:doc.Application.Selection.Range
$Table =
$Script:doc.Tables.Add($TableRange, $Rows, $Columns)
$Table.Style =
$Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle =
$wdLineStyleNone
$Table.Borders.OutsideLineStyle =
$wdLineStyleNone

[int]$xRow = 0
ForEach($Item in $GPOArray)
{
$xRow++
$Table.Cell($xRow,1).Range.Text =
$Item
}

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,
$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
}
ElseIf($Text)
{
Line 2 "$($OUDisplayName) ($($Rows))"
ForEach($Item in $GPOArray)
{
Line 3 $Item
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "$($OUDisplayName) ($
($Rows))"
$rowdata = @()
ForEach($Item in $GPOArray)
{
$rowdata += @(,($Item,$htmlwhite))
}
$columnHeaders = @('Name',($htmlsilver
-bor $htmlbold))
$msg = ""
FormatHTMLTable $msg "auto" -rowArray
$rowdata -columnArray $columnHeaders
WriteHTMLLine 0 0 " "
}
$GPOArray = $Null
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving OU data for OU $
($OU.CanonicalName)"
}
Else
{
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "<None>"
}
ElseIf($Text)
{
Line 0 "<None>"
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "None"
}
}
}
}
ElseIf(!$?)
{
$txt = "Error retrieving OU data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No OU data was retrieved for domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
$First = $False
}
}

Function ProcessgGPOsByOUNew
{
Write-Verbose "$(Get-Date): Writing Group Policy data by Domain by OU"
If($MSWORD -or $PDF)
{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Group Policies by Organizational Unit"
}
ElseIf($Text)
{
Line 0 "/// Group Policies by Organizational Unit \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Group Policies by Organizational
Unit&nbsp;&nbsp;\\\"
}
$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing domain $($Domain)"
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

$Disclaimer = "(Contains only OUs with linked or inherited Group


Policies)"
If($Domain -eq $Script:ForestRootDomain)
{
$txt = "Group Policies by OUs in Domain $($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
#print disclaimer line in 8 point bold italics
WriteWordLine 0 0 $Disclaimer "" $Null 8 $True $True
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
Line 0 $Disclaimer
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
WriteHTMLLine 0 0 $Disclaimer "" $Null 1 $True $True
}
}
Else
{
$txt = "Group Policies by OUs in Domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
WriteWordLine 0 0 $Disclaimer "" $Null 8 $True $True
}
ElseIf($Text)
{
Line 1 "/// $($txt) \\\"
Line 1 $Disclaimer
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$($txt)&nbsp;&nbsp;\\\"
WriteHTMLLine 0 0 $Disclaimer "" $Null 1 $True $True
}
}

#get all OUs for the domain


#V2.20 changed to @()
$OUs = @(Get-ADOrganizationalUnit -Filter * -Server $Domain `
-Properties CanonicalName, DistinguishedName, Name -EA 0 | `
Select-Object CanonicalName, DistinguishedName, Name | Sort-Object
CanonicalName)

If($? -and $Null -ne $OUs)


{
[int]$NumOUs = $OUs.Count
[int]$OUCount = 0
ForEach($OU in $OUs)
{
$OUCount++
$OUDisplayName =
$OU.CanonicalName.SubString($OU.CanonicalName.IndexOf("/")+1)
Write-Verbose "$(Get-Date): `t`tProcessing OU $
($OU.CanonicalName) - OU # $OUCount of $NumOUs"

#get data for the individual OU


$OUInfo = Get-ADOrganizationalUnit -Identity
$OU.DistinguishedName -Server $Domain -Properties * -EA 0

If($? -and $Null -ne $OUInfo)


{
Write-Verbose "$(Get-Date): `t`t`tGetting linked and
inherited GPOs"

#change for 2.16


#work around invalid property DisplayName when the
gpolinks and inheritedgpolinks collections are empty

$Results = Get-GPInheritance -target


$OU.DistinguishedName -EA 0

If(($Results.gpoLinks).Count -gt 0)
{
$LinkedGPOs = $Results.gpolinks.DisplayName
}
Else
{
$LinkedGPOs = $Null
}

If(($Results.inheritedgpoLinks).Count -gt 0)
{
$InheritedGPOs =
$Results.inheritedgpolinks.DisplayName
}
Else
{
$InheritedGPOs = $Null
}

If($Null -eq $LinkedGPOs -and $Null -eq


$InheritedGPOs)
{
# do nothing
}
Else
{
$AllGPOs = New-Object
System.Collections.ArrayList

ForEach($item in $InheritedGPOs)
{
$obj = New-Object -TypeName PSObject
$GPOType = ""
if(!($LinkedGPOs -contains $item))
{
$GPOType = "Inherited"
}
else
{
$GPOType = "Linked"
}
$obj | Add-Member -MemberType
NoteProperty -Name "GPOName" -value $Item
$obj | Add-Member -MemberType
NoteProperty -Name "GPOType" -value $GPOType

$AllGPOs.Add($obj) > $Null


}

$AllGPOS = $AllGPOs | Sort-Object GPOName

[int]$Rows = 0
$Rows = $AllGPOS.Count

If($MSWORD -or $PDF)


{
WriteWordLine 3 0 "$($OUDisplayName) ($
($Rows))"
[int]$Columns = 2
$TableRange =
$Script:doc.Application.Selection.Range
#increment $Rows to account for the last
row
$Table =
$Script:doc.Tables.Add($TableRange, ($Rows+1), $Columns)
$Table.Style =
$Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat =
$wdHeadingFormatTrue
$Table.Borders.InsideLineStyle =
$wdLineStyleSingle
$Table.Borders.OutsideLineStyle =
$wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
[int]$xRow = 1
$Table.Cell($xRow,1).Range.Font.Bold =
$True
$Table.Cell($xRow,1).Range.Text = "GPO
Name"
$Table.Cell($xRow,2).Range.Font.Bold =
$True
$Table.Cell($xRow,2).Range.Text = "GPO
Type"
ForEach($Item in $AllGPOS)
{
$xRow++
$Table.Cell($xRow,1).Range.Text =
$Item.GPOName
$Table.Cell($xRow,2).Range.Text =
$Item.GPOType
}

#set column widths


$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 300; Break}
2 {$xcol.width = 65; Break}
}
}

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document

$Script:doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,
$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 2 "$($OUDisplayName) ($($Rows))"
ForEach($Item in $AllGPOS)
{
Line 3 "Name: " $Item.GPOName
Line 3 "Type: " $Item.GPOType
Line 0 ""
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 3 0 "$($OUDisplayName) ($
($Rows))"
$rowdata = @()
ForEach($Item in $AllGPOS)
{
$rowdata += @(,(
$Item.GPOName,$htmlwhite,
$Item.GPOType,$htmlwhite))
}
$columnHeaders = @(
'GPO Name',($htmlsilver -bor $htmlbold),
'GPO Type',($htmlsilver -bor $htmlbold)
)

$columnWidths = @("350","65")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata
-columnArray $columnHeaders -fixedWidth $columnWidths -tablewidth "415"
WriteHTMLLine 0 0 " "
}
}
}
ElseIf(!$?)
{
Write-Warning "Error retrieving OU data for OU $
($OU.CanonicalName)"
}
Else
{
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 "<None>"
}
ElseIf($Text)
{
Line 0 "<None>"
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 "None"
}
}
}
}
ElseIf(!$?)
{
$txt = "Error retrieving OU data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No OU data was retrieved for domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
$First = $False
}
}
#endregion

#region misc info by domain


#From Jeff Hicks
#modified from his original
#https://www.petri.com/powershell-problem-solver-active-directory-remote-desktop-
settings
#added for 2.16
Function Get-RDUserSetting
{
[cmdletbinding(DefaultParameterSetName="SAM")]

Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter a user's sAMAccountName",
ValueFromPipeline,ParameterSetName="SAM")]
[ValidateNotNullorEmpty()]
[Alias("Name")]
[string]$SAMAccountname,
[Parameter(ParameterSetName="SAM")]
[string]$SearchRoot,

[Parameter(Mandatory,HelpMessage="Enter a user's distingished name",


ValueFromPipelineByPropertyName,ParameterSetName="DN")]
[ValidateNotNullorEmpty()]
[Alias("DN")]
[string]$DistinguishedName,

[string]$Server

Begin
{
#remote desktop properties
$TSSettings =
@("TerminalServicesProfilePath","TerminalServicesHomeDirectory","TerminalServicesHo
meDrive")
}

Process
{
Switch ($PSCmdlet.ParameterSetName)
{
"SAM"
{
$searcher = New-Object DirectoryServices.DirectorySearcher
$searcher.Filter = "(&(objectcategory=person)
(objectclass=user)(samAccountname=$sAMAccountname))"
If($SearchRoot)
{
If($Server)
{
$searchPath = "LDAP://$server/$SearchRoot"
}
Else
{
$searchPath = "LDAP://$SearchRoot"
}
$r = New-Object
System.DirectoryServices.DirectoryEntry $SearchPath

$searcher.SearchRoot = $r
}
$user = $searcher.FindOne().GetDirectoryEntry()
}
"DN"
{
If($server)
{
[ADSI]$User = "LDAP://$Server/$DistinguishedName"
}
Else
{
[ADSI]$User = "LDAP://$DistinguishedName"
}
}
} #close Switch

If($user.path)
{
#initialize a hashtable
Try
{
$hash=[ordered]@{
DistinguishedName = $User.DistinguishedName.Value
Name = $user.name.Value
samAccountName = $user.samAccountName.value
AllowLogon = $user.psbase.InvokeGet("AllowLogon") -as
[Boolean]
}

ForEach($property in $TSSettings)
{
$hash.Add($property,
$user.psbase.invokeGet($property))
} #ForEach

#create an object
New-Object -TypeName PSObject -Property $hash
}
Catch
{
#nothing
}
} #if user found
Else
{
#nothing
}

} #Process

End
{
#nothing
} #End

} #end function

Function ProcessMiscDataByDomain
{
Write-Verbose "$(Get-Date): Writing miscellaneous data by domain"

If($MSWORD -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Miscellaneous Data by Domain"
}
ElseIf($Text)
{
Line 0 "/// Miscellaneous Data by Domain \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Miscellaneous Data by
Domain&nbsp;&nbsp;\\\"
}

$First = $True

ForEach($Domain in $Script:Domains)
{
Write-Verbose "$(Get-Date): `tProcessing misc data for domain $
($Domain)"

$DomainInfo = Get-ADDomain -Identity $Domain -EA 0

If($? -and $Null -ne $DomainInfo)


{
If(($MSWORD -or $PDF) -and !$First)
{
#put each domain, starting with the second, on a new page
$Script:selection.InsertNewPage()
}

If($Domain -eq $Script:ForestRootDomain)


{
$txt = "$($Domain) (Forest Root)"
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $txt
}
ElseIf($Text)
{
Line 0 "/// $($txt) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$
($txt)&nbsp;&nbsp;\\\"
}
}
Else
{
If($MSWORD -or $PDF)
{
WriteWordLine 2 0 $Domain
}
ElseIf($Text)
{
Line 0 "/// $($Domain) \\\"
}
ElseIf($HTML)
{
WriteHTMLLine 2 0 "///&nbsp;&nbsp;$
($Domain)&nbsp;&nbsp;\\\"
}
}

Write-Verbose "$(Get-Date): `t`tGathering user misc data"

#added for 2.16 HomeDrive, HomeDirectory, ProfilePath,


ScriptPath, PrimaryGroup
#V2.20 changed to @()
$Users = @(Get-ADUser -Filter * -Server $Domain `
-Properties CannotChangePassword, Enabled, LockedOut,
PasswordExpired, PasswordNeverExpires, `
PasswordNotRequired, lastLogonTimestamp, DistinguishedName,
SamAccountName, UserPrincipalName, `
HomeDrive, HomeDirectory, ProfilePath, ScriptPath, PrimaryGroup
-EA 0)

If($? -and $Null -ne $Users)


{
[int]$UsersCount = $Users.Count

#added in V2.22
If($UsersCount -gt 100000)
{
Write-Verbose "$(Get-Date):
`t`t`t*****************************************************************************
*************************"
Write-Verbose "$(Get-Date): `t`t`tThere are $
($UsersCount) user accounts to process. The following 17 actions will take a long
time. Be patient."
Write-Verbose "$(Get-Date):
`t`t`t*****************************************************************************
*************************"
}

Write-Verbose "$(Get-Date): `t`t`tDisabled users"


#V2.20 changed to @()
$DisabledUsers = @($Users | Where-Object {$_.Enabled -eq
$False})

[int]$UsersDisabledcnt = $DisabledUsers.Count

Write-Verbose "$(Get-Date): `t`t`tUnknown users"


#V2.20 changed to @()
$UnknownUsers = @($Users | Where-Object {$Null -eq
$_.Enabled})
[int]$UsersUnknowncnt = $UnknownUsers.Count

Write-Verbose "$(Get-Date): `t`t`tLocked out users"


#V2.20 changed to @()
$LockedOutUsers = @($Users | Where-Object {$_.LockedOut -eq
$True})

[int]$UsersLockedOutcnt = $LockedOutUsers.Count

Write-Verbose "$(Get-Date): `t`t`tAll users with password


expired"
#V2.20 changed to @()
$AllUsersWithPasswordExpired = @($Users | Where-Object
{$_.PasswordExpired -eq $True})

[int]$UsersPasswordExpiredcnt =
$AllUsersWithPasswordExpired.Count

Write-Verbose "$(Get-Date): `t`t`tAll users whose password


never expires"
#V2.20 changed to @()
$AllUsersWhosePasswordNeverExpires = @($Users | Where-
Object {$_.PasswordNeverExpires -eq $True})

[int]$UsersPasswordNeverExpirescnt =
$AllUsersWhosePasswordNeverExpires.Count

Write-Verbose "$(Get-Date): `t`t`tAll users with password


not required"
#V2.20 changed to @()
$AllUsersWithPasswordNotRequired = @($Users | Where-Object
{$_.PasswordNotRequired -eq $True})

[int]$UsersPasswordNotRequiredcnt =
$AllUsersWithPasswordNotRequired.Count

Write-Verbose "$(Get-Date): `t`t`tAll users who cannot


change password"
#V2.20 changed to @()
$AllUsersWhoCannotChangePassword = @($Users | Where-Object
{$_.CannotChangePassword -eq $True})

[int]$UsersCannotChangePasswordcnt =
$AllUsersWhoCannotChangePassword.Count

Write-Verbose "$(Get-Date): `t`t`tAll users with SID


History"
#V2.20 changed to @()
$AllUsersWithSIDHistory = @(Get-ADObject -LDAPFilter
"(sIDHistory=*)" -Server $Domain `
-Property objectClass, sIDHistory -EA 0)

[int]$UsersWithSIDHistorycnt = ($AllUsersWithSIDHistory |
Where-Object {$_.objectClass -eq 'user'}).Count

#2.16
Write-Verbose "$(Get-Date): `t`t`tAll users with Homedrive
set in ADUC"
#V2.20 changed to @()
$HomeDriveUsers = @($Users | Where-Object {$_.HomeDrive -ne
$Null})

[int]$UsersHomeDrivecnt = $HomeDriveUsers.Count

#2.16
Write-Verbose "$(Get-Date): `t`t`tAll users whose Primary
Group is not Domain Users"
#V2.20 changed to @()
$PrimaryGroupUsers = @($Users | Where-Object
{$_.SamAccountName -ne 'Guest' -and $_.PrimaryGroup -notmatch 'Domain Users'})

[int]$UsersPrimaryGroupcnt = $PrimaryGroupUsers.Count

#2.16
Write-Verbose "$(Get-Date): `t`t`tAll users with RDS
HomeDrive set in ADUC"
#V2.20 changed to @()
$RDSHomeDriveUsers = @($users | Get-RDUserSetting | Where-
Object {$_.TerminalServicesHomeDrive -gt 0})

[int]$UsersRDSHomeDrivecnt = $RDSHomeDriveUsers.Count

#active users now


Write-Verbose "$(Get-Date): `t`t`tActive users"
#V2.20 changed to @()
$EnabledUsers = @($Users | Where-Object {$_.Enabled -eq
$True})

[int]$ActiveUsersCount = $EnabledUsers.Count

Write-Verbose "$(Get-Date): `t`t`tActive users password


expired"
#V2.20 changed to @()
$Results = @($EnabledUsers | Where-Object
{$_.PasswordExpired -eq $True})

[int]$ActiveUsersPasswordExpired = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tActive users password


never expires"
#V2.20 changed to @()
$Results = @($EnabledUsers | Where-Object
{$_.PasswordNeverExpires -eq $True})

[int]$ActiveUsersPasswordNeverExpires = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tActive users password not


required"
#V2.20 changed to @()
$Results = @($EnabledUsers | Where-Object
{$_.PasswordNotRequired -eq $True})

[int]$ActiveUsersPasswordNotRequired = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tActive Users cannot


change password"
#V2.20 changed to @()
$Results = @($EnabledUsers | Where-Object
{$_.CannotChangePassword -eq $True})

[int]$ActiveUsersCannotChangePassword = $Results.Count

Write-Verbose "$(Get-Date): `t`t`tActive Users no


lastLogonTimestamp"
#V2.20 changed to @()
$Results = @($EnabledUsers | Where-Object {$Null -eq
$_.lastLogonTimestamp})

[int]$ActiveUserslastLogonTimestamp = $Results.Count
}
Else
{
[int]$UsersCount = 0
[int]$UsersDisabledcnt = 0
[int]$UsersLockedOutcnt = 0
[int]$UsersPasswordExpiredcnt = 0
[int]$UsersPasswordNeverExpirescnt = 0
[int]$UsersPasswordNotRequiredcnt = 0
[int]$UsersCannotChangePasswordcnt = 0
[int]$UsersWithSIDHistorycnt = 0
[int]$UsersHomeDrivecnt = 0
[int]$UsersPrimaryGroupcnt = 0
[int]$UsersRDSHomeDrivecnt = 0
[int]$ActiveUsersCountcnt = 0
[int]$ActiveUsersPasswordExpiredcnt = 0
[int]$ActiveUsersPasswordNeverExpirescnt = 0
[int]$ActiveUsersPasswordNotRequiredcnt = 0
[int]$ActiveUsersCannotChangePasswordcnt = 0
[int]$ActiveUserslastLogonTimestampcnt = 0
}

Write-Verbose "$(Get-Date): `t`tFormat numbers into strings"


[string]$UsersCountStr = "{0,7:N0}" -f $UsersCount
[string]$UsersDisabledStr = "{0,7:N0}" -f $UsersDisabledcnt
[string]$UsersUnknownStr = "{0,7:N0}" -f $UsersUnknowncnt
[string]$UsersLockedOutStr = "{0,7:N0}" -f $UsersLockedOutcnt
[string]$UsersPasswordExpiredStr = "{0,7:N0}" -f
$UsersPasswordExpiredcnt
[string]$UsersPasswordNeverExpiresStr = "{0,7:N0}" -f
$UsersPasswordNeverExpirescnt
[string]$UsersPasswordNotRequiredStr = "{0,7:N0}" -f
$UsersPasswordNotRequiredcnt
[string]$UsersCannotChangePasswordStr = "{0,7:N0}" -f
$UsersCannotChangePasswordcnt
[string]$UsersWithSIDHistoryStr = "{0,7:N0}" -f
$UsersWithSIDHistorycnt
[string]$UsersHomeDriveStr = "{0,7:N0}" -f $UsersHomeDrivecnt
[string]$UsersPrimaryGroupStr = "{0,7:N0}" -f
$UsersPrimaryGroupcnt
[string]$UsersRDSHomeDriveStr = "{0,7:N0}" -f
$UsersRDSHomeDrivecnt
[string]$ActiveUsersCountStr = "{0,7:N0}" -f $ActiveUsersCount
[string]$ActiveUsersPasswordExpiredStr = "{0,7:N0}" -f
$ActiveUsersPasswordExpired
[string]$ActiveUsersPasswordNeverExpiresStr = "{0,7:N0}" -f
$ActiveUsersPasswordNeverExpires
[string]$ActiveUsersPasswordNotRequiredStr = "{0,7:N0}" -f
$ActiveUsersPasswordNotRequired
[string]$ActiveUsersCannotChangePasswordStr = "{0,7:N0}" -f
$ActiveUsersCannotChangePassword
[string]$ActiveUserslastLogonTimestampStr = "{0,7:N0}" -f
$ActiveUserslastLogonTimestamp

If($MSWORD -or $PDF)


{
Write-Verbose "$(Get-Date): `t`tBuild table for All Users"
WriteWordLine 3 0 "All Users"
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 3
[int]$Rows = 12
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle
$Table.Cell(1,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(1,1).Range.Font.Bold = $True
$Table.Cell(1,1).Range.Text = "Total Users"
$Table.Cell(1,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(1,2).Range.Text = $UsersCountStr
$Table.Cell(2,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(2,1).Range.Font.Bold = $True
$Table.Cell(2,1).Range.Text = "Disabled users"
$Table.Cell(2,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(2,2).Range.Text = $UsersDisabledStr
[single]$pct = (($UsersDisabledcnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(2,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(2,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(3,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(3,1).Range.Font.Bold = $True
$Table.Cell(3,1).Range.Text = "Unknown users*"
$Table.Cell(3,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(3,2).Range.Text = $UsersUnknownStr
[single]$pct = (($UsersUnknowncnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(3,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(3,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(4,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(4,1).Range.Font.Bold = $True
$Table.Cell(4,1).Range.Text = "Locked out users"
$Table.Cell(4,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(4,2).Range.Text = $UsersLockedOutStr
[single]$pct = (($UsersLockedOutcnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(4,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(4,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(5,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(5,1).Range.Font.Bold = $True
$Table.Cell(5,1).Range.Text = "Password expired"
$Table.Cell(5,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(5,2).Range.Text = $UsersPasswordExpiredStr
[single]$pct = (($UsersPasswordExpiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(5,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(5,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(6,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(6,1).Range.Font.Bold = $True
$Table.Cell(6,1).Range.Text = "Password never expires"
$Table.Cell(6,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(6,2).Range.Text = $UsersPasswordNeverExpiresStr
[single]$pct = (($UsersPasswordNeverExpirescnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(6,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(6,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(7,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(7,1).Range.Font.Bold = $True
$Table.Cell(7,1).Range.Text = "Password not required"
$Table.Cell(7,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(7,2).Range.Text = $UsersPasswordNotRequiredStr
[single]$pct = (($UsersPasswordNotRequiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(7,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(7,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(8,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(8,1).Range.Font.Bold = $True
$Table.Cell(8,1).Range.Text = "Can't change password"
$Table.Cell(8,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(8,2).Range.Text = $UsersCannotChangePasswordStr
[single]$pct = (($UsersCannotChangePasswordcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(8,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(8,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(9,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(9,1).Range.Font.Bold = $True
$Table.Cell(9,1).Range.Text = "With SID History"
$Table.Cell(9,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(9,2).Range.Text = $UsersWithSIDHistoryStr
[single]$pct = (($UsersWithSIDHistorycnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(9,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(9,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(10,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(10,1).Range.Font.Bold = $True
$Table.Cell(10,1).Range.Text = "HomeDrive users"
$Table.Cell(10,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(10,2).Range.Text = $UsersHomeDriveStr
[single]$pct = (($UsersHomeDrivecnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(10,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(10,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(11,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(11,1).Range.Font.Bold = $True
$Table.Cell(11,1).Range.Text = "PrimaryGroup users"
$Table.Cell(11,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(11,2).Range.Text = $UsersPrimaryGroupStr
[single]$pct = (($UsersPrimaryGroupcnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(11,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(11,3).Range.Text = "$($pctstr)% of Total Users"
$Table.Cell(12,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(12,1).Range.Font.Bold = $True
$Table.Cell(12,1).Range.Text = "RDS HomeDrive users"
$Table.Cell(12,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(12,2).Range.Text = $UsersRDSHomeDriveStr
[single]$pct = (($UsersRDSHomeDrivecnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(12,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(12,3).Range.Text = "$($pctstr)% of Total Users"

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null

WriteWordLine 0 0 "*Unknown users are user accounts with no


Enabled property." "" $Null 8 $False $True
If($Script:DARights -eq $False)
{
WriteWordLine 0 0 "*Rerun the script with Domain
Admin rights in $($ADForest)." "" $Null 8 $False $True
}
Else
{
WriteWordLine 0 0 "*This may be a permissions issue
if this is a Trusted Forest." "" $Null 8 $False $True
}

Write-Verbose "$(Get-Date): `t`tBuild table for Active


Users"
WriteWordLine 3 0 "Active Users"
$TableRange = $Script:doc.Application.Selection.Range
[int]$Columns = 3
[int]$Rows = 6
$Table = $Script:doc.Tables.Add($TableRange, $Rows,
$Columns)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle
$Table.Cell(1,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(1,1).Range.Font.Bold = $True
$Table.Cell(1,1).Range.Text = "Total Active Users"
$Table.Cell(1,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(1,2).Range.Text = $ActiveUsersCountStr
$Table.Cell(2,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(2,1).Range.Font.Bold = $True
$Table.Cell(2,1).Range.Text = "Password expired"
$Table.Cell(2,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(2,2).Range.Text =
$ActiveUsersPasswordExpiredStr
[single]$pct = (($ActiveUsersPasswordExpired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(2,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(2,3).Range.Text = "$($pctstr)% of Active Users"
$Table.Cell(3,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(3,1).Range.Font.Bold = $True
$Table.Cell(3,1).Range.Text = "Password never expires"
$Table.Cell(3,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(3,2).Range.Text =
$ActiveUsersPasswordNeverExpiresStr
[single]$pct = (($ActiveUsersPasswordNeverExpires /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(3,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(3,3).Range.Text = "$($pctstr)% of Active Users"
$Table.Cell(4,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(4,1).Range.Font.Bold = $True
$Table.Cell(4,1).Range.Text = "Password not required"
$Table.Cell(4,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(4,2).Range.Text =
$ActiveUsersPasswordNotRequiredStr
[single]$pct = (($ActiveUsersPasswordNotRequired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(4,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(4,3).Range.Text = "$($pctstr)% of Active Users"
$Table.Cell(5,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(5,1).Range.Font.Bold = $True
$Table.Cell(5,1).Range.Text = "Can't change password"
$Table.Cell(5,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(5,2).Range.Text =
$ActiveUsersCannotChangePasswordStr
[single]$pct = (($ActiveUsersCannotChangePassword /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(5,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(5,3).Range.Text = "$($pctstr)% of Active Users"
$Table.Cell(6,1).Shading.BackgroundPatternColor =
$wdColorGray15
$Table.Cell(6,1).Range.Font.Bold = $True
$Table.Cell(6,1).Range.Text = "No lastLogonTimestamp"
$Table.Cell(6,2).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(6,2).Range.Text =
$ActiveUserslastLogonTimestampStr
[single]$pct = (($ActiveUserslastLogonTimestamp /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$Table.Cell(6,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell(6,3).Range.Text = "$($pctstr)% of Active Users"

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitContent)

#return focus back to document


$Script:doc.ActiveWindow.ActivePane.view.SeekView =
$wdSeekMainDocument

#move to the end of the current document


$Script:selection.EndKey($wdStory,$wdMove) | Out-Null
$TableRange = $Null
$Table = $Null

#put computer info on a separate page


$Script:selection.InsertNewPage()
}
ElseIf($Text)
{
Write-Verbose "$(Get-Date): `t`tBuild table for All Users"
Line 0 "All Users"
Line 1 "Total Users`t`t: " $UsersCountStr

[single]$pct = (($UsersDisabledcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
Line 1 "Disabled users`t`t: $($UsersDisabledStr)`t$
($pctstr)% of Total Users"

[single]$pct = (($UsersUnknowncnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
Line 1 "Unknown users*`t`t: $($UsersUnknownStr)`t$($pctstr)
% of Total Users"

[single]$pct = (($UsersLockedOutcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
Line 1 "Locked out users`t: $($UsersLockedOutStr)`t$
($pctstr)% of Total Users"

[single]$pct = (($UsersPasswordExpiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password expired`t: $($UsersPasswordExpiredStr)`t$
($pctstr)% of Total Users"

[single]$pct = (($UsersPasswordNeverExpirescnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password never expires`t: $
($UsersPasswordNeverExpiresStr)`t$($pctstr)% of Total Users"

[single]$pct = (($UsersPasswordNotRequiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password not required`t: $
($UsersPasswordNotRequiredStr)`t$($pctstr)% of Total Users"

[single]$pct = (($UsersCannotChangePasswordcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Can't change password`t: $
($UsersCannotChangePasswordStr)`t$($pctstr)% of Total Users"

[single]$pct = (($UsersWithSIDHistorycnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "With SID History`t: $($UsersWithSIDHistoryStr)`t$
($pctstr)% of Total Users"

[single]$pct = (($UsersHomeDrivecnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
Line 1 "With HomeDrive`t`t: $($UsersHomeDriveStr)`t$
($pctstr)% of Total Users"

[single]$pct = (($UsersPrimaryGroupcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
Line 1 "With Primary Group`t: $($UsersPrimaryGroupStr)`t$
($pctstr)% of Total Users"
[single]$pct = (($UsersRDSHomeDrivecnt / $UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "With RDS HomeDrive`t: $($UsersRDSHomeDriveStr)`t$
($pctstr)% of Total Users"

Line 1 "*Unknown users are user accounts with no Enabled


property"
If($Script:DARights -eq $False)
{
Line 1 "*Rerun the script with Domain Admin rights in
$($ADForest)"
}
Else
{
Line 1 "*This may be a permissions issue if this is a
Trusted Forest"
}
Line 0 ""

Write-Verbose "$(Get-Date): `t`tBuild table for Active


Users"
Line 0 "Active Users"

Line 1 "Total Active Users`t: " $ActiveUsersCountStr

[single]$pct = (($ActiveUsersPasswordExpired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password expired`t: $
($ActiveUsersPasswordExpiredStr)`t$($pctstr)% of Active Users"

[single]$pct = (($ActiveUsersPasswordNeverExpires /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password never expires`t: $
($ActiveUsersPasswordNeverExpiresStr)`t$($pctstr)% of Active Users"

[single]$pct = (($ActiveUsersPasswordNotRequired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Password not required`t: $
($ActiveUsersPasswordNotRequiredStr)`t$($pctstr)% of Active Users"

[single]$pct = (($ActiveUsersCannotChangePassword /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "Can't change password`t: $
($ActiveUsersCannotChangePasswordStr)`t$($pctstr)% of Active Users"

[single]$pct = (($ActiveUserslastLogonTimestamp /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
Line 1 "No lastLogonTimestamp`t: $
($ActiveUserslastLogonTimestampStr)`t$($pctstr)% of Active Users"
Line 0 ""
}
ElseIf($HTML)
{
Write-Verbose "$(Get-Date): `t`tBuild table for All Users"
WriteHTMLLine 3 0 "All Users"
$rowdata = @()
$columnHeaders = @("Total Users",($htmlsilver -bor
$htmlbold),
$UsersCountStr,($htmlwhite),
"",($htmlwhite))

[single]$pct = (($UsersDisabledcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Disabled users',($htmlsilver -bor
$htmlbold),
$UsersDisabledStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersUnknowncnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Unknown users*',($htmlsilver -bor
$htmlbold),
$UsersUnknownStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersLockedOutcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Locked out users',($htmlsilver -bor
$htmlbold),
$UsersLockedOutStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersPasswordExpiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password expired',($htmlsilver -bor
$htmlbold),
$UsersPasswordExpiredStr,($htmlwhite),
"$($pctstr)% of Total User",($htmlwhite)))

[single]$pct = (($UsersPasswordNeverExpirescnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password never expires',($htmlsilver -bor
$htmlbold),
$UsersPasswordNeverExpiresStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersPasswordNotRequiredcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password not required',($htmlsilver -bor
$htmlbold),
$UsersPasswordNotRequiredStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersCannotChangePasswordcnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,("Can't change password",($htmlsilver -bor
$htmlbold),
$UsersCannotChangePasswordStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersWithSIDHistorycnt /
$UsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('With SID History',($htmlsilver -bor
$htmlbold),
$UsersWithSIDHistoryStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersHomeDrivecnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('With HomeDrive',($htmlsilver -bor
$htmlbold),
$UsersHomeDriveStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersPrimaryGroupcnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('With PrimaryGroup',($htmlsilver -bor
$htmlbold),
$UsersPrimaryGroupStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

[single]$pct = (($UsersRDSHomeDrivecnt / $UsersCount)*100)


$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('With RDS HomeDrive',($htmlsilver -bor
$htmlbold),
$UsersRDSHomeDriveStr,($htmlwhite),
"$($pctstr)% of Total Users",($htmlwhite)))

$msg = ""
$columnWidths = @("150","50","150")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "350"

WriteHTMLLine 0 0 "*Unknown users are user accounts with no


Enabled property." "" $Null 1 $False $True
If($Script:DARights -eq $False)
{
WriteHTMLLine 0 0 "*Rerun the script with Domain
Admin rights in $($ADForest)." "" $Null 1 $False $True
}
Else
{
WriteHTMLLine 0 0 "*This may be a permissions issue
if this is a Trusted Forest." "" $Null 1 $False $True
}

Write-Verbose "$(Get-Date): `t`tBuild table for Active


Users"
WriteHTMLLine 3 0 "Active Users"

$rowdata = @()
$rowdata += @(,('Total Active Users',($htmlsilver -bor
$htmlbold),
$ActiveUsersCountStr,($htmlwhite),
"",($htmlwhite)))
[single]$pct = (($ActiveUsersPasswordExpired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password expired',($htmlsilver -bor
$htmlbold),
$ActiveUsersPasswordExpiredStr,($htmlwhite),
"$($pctstr)% of Active Users",($htmlwhite)))

[single]$pct = (($ActiveUsersPasswordNeverExpires /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password never expires',($htmlsilver -bor
$htmlbold),
$ActiveUsersPasswordNeverExpiresStr,($htmlwhite),
"$($pctstr)% of Active Users",($htmlwhite)))

[single]$pct = (($ActiveUsersPasswordNotRequired /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('Password not required',($htmlsilver -bor
$htmlbold),
$ActiveUsersPasswordNotRequiredStr,($htmlwhite),
"$($pctstr)% of Active Users",($htmlwhite)))

[single]$pct = (($ActiveUsersCannotChangePassword /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,("Can't change password",($htmlsilver -bor
$htmlbold),
$ActiveUsersCannotChangePasswordStr,($htmlwhite),
"$($pctstr)% of Active Users",($htmlwhite)))

[single]$pct = (($ActiveUserslastLogonTimestamp /
$ActiveUsersCount)*100)
$pctstr = "{0,5:N2}" -f $pct
$rowdata += @(,('No lastLogonTimestamp',($htmlsilver -bor
$htmlbold),
$ActiveUserslastLogonTimestampStr,($htmlwhite),
"$($pctstr)% of Active Users",($htmlwhite)))

$msg = ""
$columnWidths = @("150","50","150")
FormatHTMLTable $msg -rowArray $rowdata -columnArray
$columnHeaders -fixedWidth $columnWidths -tablewidth "350"
WriteHTMLLine 0 0 " "
}

If($UsersDisabledcnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputUserInfo $DisabledUsers "Disabled users"
}

If($UsersUnknowncnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputUserInfo $UnknownUsers "Unknown users"
}

If($UsersLockedOutcnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputUserInfo $LockedOutUsers "Locked out users"
}

If($UsersPasswordExpiredcnt -gt 0 -and $IncludeUserInfo -eq


$True)
{
OutputUserInfo $AllUsersWithPasswordExpired "All users with
password expired"
}

If($UsersPasswordNeverExpirescnt -gt 0 -and $IncludeUserInfo -eq


$True)
{
OutputUserInfo $AllUsersWhosePasswordNeverExpires "All
users whose password never expires"
}

If($UsersPasswordNotRequiredcnt -gt 0 -and $IncludeUserInfo -eq


$True)
{
OutputUserInfo $AllUsersWithPasswordNotRequired "All users
with password not required"
}

If($UsersCannotChangePasswordcnt -gt 0 -and $IncludeUserInfo -eq


$True)
{
OutputUserInfo $AllUsersWhoCannotChangePassword "All users
who cannot change password"
}

If($UsersWithSIDHistorycnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputUserInfo $AllUsersWithSIDHistory "All users with SID
History"
}

If($UsersHomeDrivecnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputHDUserInfo $HomeDriveUsers "All users with HomeDrive
set in ADUC"
}

If($UsersPrimaryGroupcnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputPGUserInfo $PrimaryGroupUsers "All users whose
Primary Group is not Domain Users"
}

If($UsersRDSHomeDrivecnt -gt 0 -and $IncludeUserInfo -eq $True)


{
OutputRDSHDUserInfo $RDSHomeDriveUsers "All users with RDS
HomeDrive set in ADUC"
}

Get-ComputerCountByOS $Domain
}
ElseIf(!$?)
{
$txt = "Error retrieving domain data for domain $($Domain)"
Write-Warning $txt
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
Else
{
$txt = "No Domain data was retrieved for domain $($Domain)"
If($MSWORD -or $PDF)
{
WriteWordLine 0 0 $txt "" $Null 0 $False $True
}
ElseIf($Text)
{
Line 0 $txt
}
ElseIf($HTML)
{
WriteHTMLLine 0 0 $txt
}
}
$First = $False
}
$Script:Domains = $Null
}

Function OutputUserInfo
{
Param([object] $Users, [string] $title)

Write-Verbose "$(Get-Date): `t`t`t`tOutput $($title)"


$Users = $Users | Sort-Object samAccountName

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $UsersWordTable = @();
[int] $CurrentServiceIndex = 2;

WriteWordLine 4 0 $title

ForEach($User in $Users)
{
$WordTableRowHash = @{
SamAccountName = $User.SamAccountName;
DN = $User.DistinguishedName
}

## Add the hash to the array


$UsersWordTable += $WordTableRowHash;
$CurrentServiceIndex++;
}

## Add the table to the document, using the hashtable (-Alt is short
for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $UsersWordTable `
-Columns SamAccountName, DN `
-Headers "SamAccountName", "DistinguishedName" `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 150;
$Table.Columns.Item(2).Width = 350;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $title
Line 0 ""
Line 1 "samAccountName DistinguishedName"
Line 1
"==================================================================================
============================================================"
###### "1234512345123451234512345

ForEach($User in $Users)
{
Line 1 ( "{0,-25} {1,-116}" -f $User.samAccountName,
$User.DistinguishedName )
}
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $title
$rowdata = @()

ForEach($User in $Users)
{
$rowdata += @(,($User.SamAccountName,$htmlwhite,
$User.DistinguishedName,$htmlwhite))
}

$columnHeaders = @('SamAccountName',($htmlsilver -bor


$htmlbold),'DistinguishedName',($htmlsilver -bor $htmlbold))
$columnWidths = @("150px","350px")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "500"
WriteHTMLLine 0 0 " "
}
}

Function OutputHDUserInfo
{
#new for 2.16
Param([object] $Users, [string] $title)

Write-Verbose "$(Get-Date): `t`t`t`tOutput $($title)"


$Users = $Users | Sort-Object samAccountName

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $UsersWordTable = @();
[int] $CurrentServiceIndex = 2;

WriteWordLine 4 0 $title

ForEach($User in $Users)
{
$WordTableRowHash = @{
SamAccountName = $User.SamAccountName;
DN = $User.DistinguishedName;
HomeDrive = $User.HomeDrive;
HomeDir = $User.HomeDirectory;
ProfilePath = $User.ProfilePath;
ScriptPath = $User.ScriptPath
}

## Add the hash to the array


$UsersWordTable += $WordTableRowHash;

$CurrentServiceIndex++;
}

## Add the table to the document, using the hashtable (-Alt is short
for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $UsersWordTable `
-Columns SamAccountName, DN, HomeDrive, HomeDir, ProfilePath,
ScriptPath `
-Headers "SamAccountName", "DistinguishedName", "Home drive", "Home
folder", "Profile path", "Login script" `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table -Size 9


SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 100;
$Table.Columns.Item(2).Width = 110;
$Table.Columns.Item(3).Width = 35;
$Table.Columns.Item(4).Width = 85;
$Table.Columns.Item(5).Width = 85;
$Table.Columns.Item(6).Width = 85;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $title
Line 0 ""

ForEach($User in $Users)
{
Line 1 "SamAccountName`t`t: " $User.samAccountName
Line 1 "DistinguishedName`t: " $User.DistinguishedName
Line 1 "Home drive`t`t: " $User.HomeDrive
Line 1 "Home folder`t`t: " $User.HomeDirectory
Line 1 "Profile path`t`t: " $User.ProfilePath
Line 1 "Login script`t`t: " $User.ScriptPath
Line 0 ""
}
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $title
$rowdata = @()

ForEach($User in $Users)
{
$rowdata += @(,($User.SamAccountName,$htmlwhite,
$User.DistinguishedName,$htmlwhite,
$User.HomeDrive,$htmlwhite,
$User.HomeDirectory,$htmlwhite,
$User.ProfilePath,$htmlwhite,
$User.ScriptPath,$htmlwhite))
}

$columnHeaders = @(
'SamAccountName',($htmlsilver -bor $htmlbold),
'DistinguishedName',($htmlsilver -bor $htmlbold),
'Home Drive',($htmlsilver -bor $htmlbold),
'Home folder',($htmlsilver -bor $htmlbold),
'Profile path',($htmlsilver -bor $htmlbold),
'Login script',($htmlsilver -bor $htmlbold))
$columnWidths = @("100px","100px","75px","75px","75px","75px")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "500"
WriteHTMLLine 0 0 " "
}
}

Function OutputPGUserInfo
{
#new for 2.16
Param([object] $Users, [string] $title)

Write-Verbose "$(Get-Date): `t`t`t`tOutput $($title)"


$Users = $Users | Sort-Object samAccountName

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $UsersWordTable = @();
[int] $CurrentServiceIndex = 2;

WriteWordLine 4 0 $title

ForEach($User in $Users)
{
$WordTableRowHash = @{
SamAccountName = $User.SamAccountName;
DN = $User.DistinguishedName;
PG = $User.PrimaryGroup
}

## Add the hash to the array


$UsersWordTable += $WordTableRowHash;

$CurrentServiceIndex++;
}

## Add the table to the document, using the hashtable (-Alt is short
for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $UsersWordTable `
-Columns SamAccountName, DN, PG `
-Headers "SamAccountName", "DistinguishedName", "Primary Group" `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table -Size 9


SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 100;
$Table.Columns.Item(2).Width = 200;
$Table.Columns.Item(3).Width = 200;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $title
Line 0 ""

ForEach($User in $Users)
{
Line 1 "samAccountName`t`t: " $User.samAccountName
Line 1 "DistinguishedName`t: " $User.DistinguishedName
Line 1 "Primary Group`t`t: " $User.PrimaryGroup
Line 0 ""
}
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $title
$rowdata = @()
ForEach($User in $Users)
{
$rowdata += @(,($User.SamAccountName,$htmlwhite,
$User.DistinguishedName,$htmlwhite,
$User.PrimaryGroup,$htmlwhite))
}

$columnHeaders = @(
'SamAccountName',($htmlsilver -bor $htmlbold),
'DistinguishedName',($htmlsilver -bor $htmlbold),
'Primary Group',($htmlsilver -bor $htmlbold))
$columnWidths = @("100px","200px","200px")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "500"
WriteHTMLLine 0 0 " "
}
}

Function OutputRDSHDUserInfo
{
#new for 2.16
Param([object] $Users, [string] $title)

Write-Verbose "$(Get-Date): `t`t`t`tOutput $($title)"


$Users = $Users | Sort-Object samAccountName

If($MSWORD -or $PDF)


{
[System.Collections.Hashtable[]] $UsersWordTable = @();
[int] $CurrentServiceIndex = 2;

WriteWordLine 4 0 $title

ForEach($User in $Users)
{
$WordTableRowHash = @{
SamAccountName = $User.SamAccountName;
DN = $User.DistinguishedName;
HomeDrive = $User.TerminalServicesHomeDrive;
HomeDir = $User.TerminalServicesHomeDirectory;
ProfilePath = $User.TerminalServicesProfilePath;
AllowLogon = $User.AllowLogon
}

## Add the hash to the array


$UsersWordTable += $WordTableRowHash;

$CurrentServiceIndex++;
}

## Add the table to the document, using the hashtable (-Alt is short
for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $UsersWordTable `
-Columns SamAccountName, DN, HOmeDrive, HomeDir, ProfilePath,
ALlowLogon `
-Headers "SamAccountName", "DistinguishedName", "RDS Home drive", "RDS
Home folder", "RDS Profile path", "Allow Logon" `
-Format $wdTableGrid `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table -Size 9


SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold
-BackgroundColor $wdColorGray15;

$Table.Columns.Item(1).Width = 100;
$Table.Columns.Item(2).Width = 140;
$Table.Columns.Item(3).Width = 35;
$Table.Columns.Item(4).Width = 75;
$Table.Columns.Item(5).Width = 90;
$Table.Columns.Item(6).Width = 60;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)

FindWordDocumentEnd
$Table = $Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
Line 0 $title
Line 0 ""

ForEach($User in $Users)
{
Line 1 "SamAccountName`t`t: " $User.samAccountName
Line 1 "DistinguishedName`t: " $User.DistinguishedName
Line 1 "RDS Home drive`t`t: " $User.TerminalServicesHomeDrive
Line 1 "RDS Home folder`t`t: "
$User.TerminalServicesHomeDirectory
Line 1 "RDS Profile path`t: " $User.TerminalServicesProfilePath
Line 1 "Allow Logon`t`t: " $User.AllowLogon
Line 0 ""
}
}
ElseIf($HTML)
{
WriteHTMLLine 4 0 $title
$rowdata = @()

ForEach($User in $Users)
{
$rowdata += @(,($User.SamAccountName,$htmlwhite,
$User.DistinguishedName,$htmlwhite,
$User.TerminalServicesHomeDrive,
$htmlwhite,
$User.TerminalServicesHomeDirectory,
$htmlwhite,
$User.TerminalServicesProfilePath,
$htmlwhite,
$User.AllowLogon,$htmlwhite))
}

$columnHeaders = @(
'SamAccountName',($htmlsilver -bor $htmlbold),
'DistinguishedName',($htmlsilver -bor $htmlbold),
'RDS Home Drive',($htmlsilver -bor $htmlbold),
'RDS Home folder',($htmlsilver -bor $htmlbold),
'RDS Profile path',($htmlsilver -bor $htmlbold),
'Allow Logon',($htmlsilver -bor $htmlbold))
$columnWidths = @("100px","100px","75px","75px","90px","60px")
$msg = ""
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "500"
WriteHTMLLine 0 0 " "
}
}
#endregion

#region DCDNSInfo
Function ProcessDCDNSInfo
{
If($DCDNSInfo)
{
#Domain Controller DNS IP Configuration
Write-Verbose "$(Get-Date): Create Domain Controller DNS IP
Configuration"
Write-Verbose "$(Get-Date): `tAdd Domain Controller DNS IP
Configuration table to doc"

#sort by site then by DC


$xDCDNSIPInfo = $Script:DCDNSIPInfo | Sort-Object DCSite, DCName

If($MSWord -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Domain Controller DNS IP Configuration"
[System.Collections.Hashtable[]] $ItemsWordTable = @();
[int] $CurrentServiceIndex = 2;
}
ElseIf($Text)
{
Line 0 "/// Domain Controller DNS IP Configuration \\\"
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Domain Controller DNS IP
Configuration&nbsp;&nbsp;\\\"
$rowdata = @()
}

ForEach($Item in $xDCDNSIPInfo)
{
If($MSWord -or $PDF)
{
## Add the required key/values to the hashtable
$WordTableRowHash = @{
DCName = $Item.DCName;
DCSite = $Item.DCSite;
DCIpAddress1 = $Item.DCIpAddress1;
DCIpAddress2 = $Item.DCIpAddress2;
DCDNS1 = $Item.DCDNS1;
DCDNS2 = $Item.DCDNS2;
DCDNS3 = $Item.DCDNS3;
DCDNS4 = $Item.DCDNS4
}
## Add the hash to the array
$ItemsWordTable += $WordTableRowHash;

$CurrentServiceIndex++;
}
ElseIf($Text)
{
Line 1 "DC Name`t`t: " $Item.DCName
Line 1 "Site Name`t: " $Item.DCSite
Line 1 "IP Address1`t: " $Item.DCIpAddress1
Line 1 "IP Address2`t: " $Item.DCIpAddress2
Line 1 "Preferred DNS`t: " $Item.DCDNS1
Line 1 "Alternate DNS`t: " $Item.DCDNS2
Line 1 "DNS 3`t`t: " $Item.DCDNS3
Line 1 "DNS 4`t`t: " $Item.DCDNS4
Line 0 ""
}
ElseIf($HTML)
{
$rowdata += @(,(
$Item.DCName,$htmlwhite,
$Item.DCSite,$htmlwhite,
$Item.DCIpAddress1,$htmlwhite,
$Item.DCIpAddress2,$htmlwhite,
$Item.DCDNS1,$htmlwhite,
$Item.DCDNS2,$htmlwhite,
$Item.DCDNS3,$htmlwhite,
$Item.DCDNS4,$htmlwhite))
}
}

If($MSWord -or $PDF)


{
## Add the table to the document, using the hashtable (-Alt is
short for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $ItemsWordTable `
-Columns DCName, DCSite, DCIpAddress1, DCIpAddress2, DCDNS1,
DCDNS2, DCDNS3, DCDNS4 `
-Headers "DC Name", "Site", "IP Address 1", "IP Address 2", "DNS
1", "DNS 2", "DNS 3", "DNS 4" `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;
SetWordCellFormat -Collection $Table -Size 8

$Table.Columns.Item(1).Width = 100;
$Table.Columns.Item(2).Width = 60;
$Table.Columns.Item(3).Width = 67;
$Table.Columns.Item(4).Width = 67;
$Table.Columns.Item(5).Width = 40;
$Table.Columns.Item(6).Width = 40;
$Table.Columns.Item(7).Width = 40;
$Table.Columns.Item(8).Width = 40;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
}
ElseIf($Text)
{
#nothing to do
}
ElseIf($HTML)
{
$columnHeaders = @(
'DC Name',($htmlsilver -bor $htmlbold),
'Site',($htmlsilver -bor $htmlbold),
'IP Address 1',($htmlsilver -bor $htmlbold),
'IP Address 2',($htmlsilver -bor $htmlbold),
'Preferred DNS',($htmlsilver -bor $htmlbold),
'Alternate DNS',($htmlsilver -bor $htmlbold),
'DNS 3',($htmlsilver -bor $htmlbold),
'DNS 4',($htmlsilver -bor $htmlbold)
)

$msg = ""
FormatHTMLTable $msg "auto" -rowArray $rowdata -columnArray
$columnHeaders
WriteHTMLLine 0 0 " "
}

Write-Verbose "$(Get-Date): Finished Create Domain Controller DNS IP


Configuration"
Write-Verbose "$(Get-Date): "
}
}
#endregion

#region TimeServerInfo
Function ProcessTimeServerInfo
{
#Domain Controller Time Server Configuration
Write-Verbose "$(Get-Date): Create Domain Controller Time Server
Configuration"
Write-Verbose "$(Get-Date): `tAdd Domain Controller Time Server Configuration
table to doc"

#sort by DC
$xTimeServerInfo = $Script:TimeServerInfo | Sort-Object DCName

If($MSWord -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Domain Controller Time Server Configuration"
[System.Collections.Hashtable[]] $ItemsWordTable = @();
[int] $CurrentServiceIndex = 2;
}
ElseIf($Text)
{
Line 0 "/// Domain Controller Time Server Configuration \\\"
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Domain Controller Time Server
Configuration&nbsp;&nbsp;\\\"
$rowdata = @()
}

ForEach($Item in $xTimeServerInfo)
{
If($MSWord -or $PDF)
{
## Add the required key/values to the hashtable
$WordTableRowHash = @{
DCName = $Item.DCName;
DCTimeSource = $Item.TimeSource;
DCAnnounceFlags = $Item.AnnounceFlags;
DCMaxNegPhaseCorrection = $Item.MaxNegPhaseCorrection;
DCMaxPosPhaseCorrection = $Item.MaxPosPhaseCorrection;
DCNtpServer = $Item.NtpServer;
DCNtpType = $Item.NtpType;
DCSpecialPollInterval = $Item.SpecialPollInterval;
DCVMICTimeProvider = $Item.VMICTimeProvider
}

## Add the hash to the array


$ItemsWordTable += $WordTableRowHash;

$CurrentServiceIndex++;
}
ElseIf($Text)
{
Line 1 "DC Name`t`t`t: " $Item.DCName
Line 1 "Time source`t`t: " $Item.TimeSource
Line 1 "Announce flags`t`t: " $Item.AnnounceFlags
Line 1 "Max Neg Phase Correction: " $Item.MaxNegPhaseCorrection
Line 1 "Max Pos Phase Correction: " $Item.MaxPosPhaseCorrection
Line 1 "NTP Server`t`t: " $Item.NtpServer
Line 1 "Type`t`t`t: " $Item.NtpType
Line 1 "Special Poll Interval`t: " $Item.SpecialPollInterval
Line 1 "VMIC Time Provider`t: " $Item.VMICTimeProvider
Line 0 ""
}
ElseIf($HTML)
{
$rowdata += @(,(
$Item.DCName,$htmlwhite,
$Item.TimeSource,$htmlwhite,
$Item.AnnounceFlags,$htmlwhite,
$Item.MaxNegPhaseCorrection,$htmlwhite,
$Item.MaxPosPhaseCorrection,$htmlwhite,
$Item.NtpServer,$htmlwhite,
$Item.NtpType,$htmlwhite,
$Item.SpecialPollInterval,$htmlwhite,
$Item.VMICTimeProvider,$htmlwhite
))
}
}

If($MSWord -or $PDF)


{
## Add the table to the document, using the hashtable (-Alt is short
for -AlternateBackgroundColor!)
$Table = AddWordTable -Hashtable $ItemsWordTable `
-Columns DCName, DCTimeSource, DCAnnounceFlags,
DCMaxNegPhaseCorrection, DCMaxPosPhaseCorrection, DCNtpServer, DCNtpType,
DCSpecialPollInterval, DCVMICTimeProvider `
-Headers "DC Name", "Time Source", "Announce Flags", "Max Neg Phase
Correction", "Max Pos Phase Correction", "NTP Server", "Type", "Special Poll
Interval", "VMIC Time Provider" `
-AutoFit $wdAutoFitFixed;

SetWordCellFormat -Collection $Table.Rows.Item(1).Cells -Bold


-BackgroundColor $wdColorGray15;
SetWordCellFormat -Collection $Table -Size 8

$Table.Columns.Item(1).Width = 105;
$Table.Columns.Item(2).Width = 73;
$Table.Columns.Item(3).Width = 45;
$Table.Columns.Item(4).Width = 47;
$Table.Columns.Item(5).Width = 47;
$Table.Columns.Item(6).Width = 73;
$Table.Columns.Item(7).Width = 33;
$Table.Columns.Item(8).Width = 37;
$Table.Columns.Item(9).Width = 40;

$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustProportional)

FindWordDocumentEnd
$Table = $Null
}
ElseIf($Text)
{
#nothing to do
}
ElseIf($HTML)
{
$columnHeaders = @(
'DC Name',($htmlsilver -bor $htmlbold),
'Time Source',($htmlsilver -bor $htmlbold),
'Announce Flags',($htmlsilver -bor $htmlbold),
'Max Neg Phase Correction',($htmlsilver -bor $htmlbold),
'Max Pos Phase Correction',($htmlsilver -bor $htmlbold),
'NTP Server',($htmlsilver -bor $htmlbold),
'Type',($htmlsilver -bor $htmlbold),
'Special Poll Interval',($htmlsilver -bor $htmlbold),
'VMIC Time Provider',($htmlsilver -bor $htmlbold)
)

$msg = ""
$columnWidths =
@("100px","70px","45px","45px","45px","75px","40px","40px","40px")
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "500"
WriteHTMLLine 0 0 " "
}

Write-Verbose "$(Get-Date): Finished Create Domain Controller Time Server


Configuration"
Write-Verbose "$(Get-Date): "
}
#endregion
#region EventLogInfo
Function ProcessEventLogInfo
{
#Domain Controller Event Log Data
Write-Verbose "$(Get-Date): Create Domain Controller Event Log Data"
Write-Verbose "$(Get-Date): `tAdd Domain Controller Event Log Data table to
doc"

#sort by DC and then event log name


#V2.20 changed to @()
$xEventLogInfo = @($Script:DCEventLogInfo | Sort-Object EventLogName, DCName)

If($MSWord -or $PDF)


{
$Script:selection.InsertNewPage()
WriteWordLine 1 0 "Domain Controller Event Log Data"
$TableRange = $doc.Application.Selection.Range
[int]$Columns = 3
[int]$Rows = $xEventLogInfo.Count + 1
[int]$xRow = 1

$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)


$Table.AutoFitBehavior($wdAutoFitFixed)
$Table.Style = $Script:MyHash.Word_TableGrid

$Table.rows.first.headingformat = $wdHeadingFormatTrue
$Table.Borders.InsideLineStyle = $wdLineStyleSingle
$Table.Borders.OutsideLineStyle = $wdLineStyleSingle

$Table.Rows.First.Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Event Log Name"

$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "DC Name"

$Table.Cell($xRow,3).Range.Font.Bold = $True
$Table.Cell($xRow,3).Range.Text = "Event Log Size (KB)"
}
ElseIf($Text)
{
Line 0 "/// Domain Controller Event Log Data \\\"
Line 0 ""
}
ElseIf($HTML)
{
WriteHTMLLine 1 0 "///&nbsp;&nbsp;Domain Controller Event Log
Data&nbsp;&nbsp;\\\"
$rowdata = @()
}

ForEach($Item in $xEventLogInfo)
{
If($MSWord -or $PDF)
{
$xRow++
$Table.Cell($xRow,1).Range.Text = $Item.EventLogName
$Table.Cell($xRow,2).Range.Text = $Item.DCName
$Table.Cell($xRow,3).Range.ParagraphFormat.Alignment =
$wdCellAlignVerticalTop
$Table.Cell($xRow,3).Range.ParagraphFormat.Alignment =
$wdAlignParagraphRight
$Table.Cell($xRow,3).Range.Text = $Item.EventLogSize
}
ElseIf($Text)
{
Line 1 "Event Log Name`t`t: " $Item.EventLogName
Line 1 "DC Name`t`t`t: " $Item.DCName
Line 1 "Event Log Size (KB)`t: " $Item.EventLogSize
Line 0 ""
}
ElseIf($HTML)
{
$rowdata += @(,(
$Item.EventLogName,$htmlwhite,
$Item.DCName,$htmlwhite,
$Item.EventLogSize,$htmlwhite
))
}
}

If($MSWord -or $PDF)


{
#set column widths
$xcols = $table.columns

ForEach($xcol in $xcols)
{
switch ($xcol.Index)
{
1 {$xcol.width = 150; Break}
2 {$xcol.width = 150; Break}
3 {$xcol.width = 100; Break}
}
}
$Table.Rows.SetLeftIndent($Indent0TabStops,$wdAdjustNone)
$Table.AutoFitBehavior($wdAutoFitFixed)

#return focus back to document


$doc.ActiveWindow.ActivePane.view.SeekView = $wdSeekMainDocument

#move to the end of the current document


$selection.EndKey($wdStory,$wdMove) | Out-Null
WriteWordLine 0 0 ""
}
ElseIf($Text)
{
#nothing to do
}
ElseIf($HTML)
{
$columnHeaders = @(
'Event Log Name',($htmlsilver -bor $htmlbold),
'DC Name',($htmlsilver -bor $htmlbold),
'Event Log Size (KB)',($htmlsilver -bor $htmlbold)
)
$msg = ""
$columnWidths = @("150px","150px","100px")
FormatHTMLTable $msg -rowArray $rowdata -columnArray $columnHeaders
-fixedWidth $columnWidths -tablewidth "400"
WriteHTMLLine 0 0 " "
}

Write-Verbose "$(Get-Date): Finished Create Domain Controller Event Log Data"


Write-Verbose "$(Get-Date): "
}
#endregion

#region general script functions


Function ProcessDocumentOutput
{
If($MSWORD -or $PDF)
{
SaveandCloseDocumentandShutdownWord
}
ElseIf($Text)
{
SaveandCloseTextDocument
}
ElseIf($HTML)
{
SaveandCloseHTMLDocument
}

$GotFile = $False

If($PDF)
{
If(Test-Path "$($Script:FileName2)")
{
Write-Verbose "$(Get-Date): $($Script:FileName2) is ready for
use"
$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"
$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
}
}
#endregion

#region script start function


Function ProcessScriptStart
{
$script:startTime = Get-Date
}
#endregion

#region script end


Function ProcessScriptEnd
{
Write-Verbose "$(Get-Date): Script has completed"
Write-Verbose "$(Get-Date): "

#http://poshtips.com/measuring-elapsed-time-in-powershell/
Write-Verbose "$(Get-Date): Script started: $($Script:StartTime)"
Write-Verbose "$(Get-Date): Script ended: $(Get-Date)"
$runtime = $(Get-Date) - $Script:StartTime
$Str = [string]::format("{0} days, {1} hours, {2} minutes, {3}.{4} seconds",
$runtime.Days,
$runtime.Hours,
$runtime.Minutes,
$runtime.Seconds,
$runtime.Milliseconds)
Write-Verbose "$(Get-Date): Elapsed time: $($Str)"

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)
{
$SIFile = "$($pwd.Path)\ADInventoryScriptInfo_$(Get-Date -f yyyy-MM-
dd_HHmm).txt"
Out-File -FilePath $SIFile -InputObject "" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Add DateTime :
$AddDateTime" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $SIFile -Append -InputObject "Company Name :
$Script:CoName" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "ComputerName :
$ComputerName" 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 Phone :
$CompanyPhone" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Cover Page :
$CoverPage" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "DCDNSInfo :
$DCDNSInfo" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Dev : $Dev"
4>$Null
If($Dev)
{
Out-File -FilePath $SIFile -Append -InputObject "DevErrorFile :
$Script:DevErrorFile" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "Domain name :
$ADDomain" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Elevated :
$Script:Elevated" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Filename1 :
$Script:FileName1" 4>$Null
If($PDF)
{
Out-File -FilePath $SIFile -Append -InputObject "Filename2 :
$Script:FileName2" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "Folder :
$Folder" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Forest name :
$ADForest" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "From :
$From" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "GPOInheritance :
$GPOInheritance" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "HW Inventory :
$Hardware" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "IncludeUserInfo:
$IncludeUserInfo" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Log : $
($Log)" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "MaxDetails :
$MaxDetails" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Save As HTML :
$HTML" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Save As PDF : $PDF"
4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Save As TEXT :
$TEXT" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Save As WORD :
$MSWORD" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Script Info :
$ScriptInfo" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Services :
$Services" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Smtp Port :
$SmtpPort" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Smtp Server :
$SmtpServer" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Title :
$Script:Title" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "To : $To"
4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Use SSL :
$UseSSL" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $SIFile -Append -InputObject "User Name :
$UserName" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "OS Detected :
$Script:RunningOS" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "PoSH version : $
($Host.Version)" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "PSCulture :
$PSCulture" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "PSUICulture :
$PSUICulture" 4>$Null
If($MSWORD -or $PDF)
{
Out-File -FilePath $SIFile -Append -InputObject "Word language :
$Script:WordLanguageValue" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Word version :
$Script:WordProduct" 4>$Null
}
Out-File -FilePath $SIFile -Append -InputObject "" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Script start :
$Script:StartTime" 4>$Null
Out-File -FilePath $SIFile -Append -InputObject "Elapsed time : $Str"
4>$Null
}

#V2.18 added
#stop transcript logging
If($Log -eq $True)
{
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"
}
}
}
$ErrorActionPreference = $SaveEAPreference
}
#endregion

#region script core


#Script begins

ProcessScriptStart

ProcessScriptSetup

If($ADDomain -ne "")


{
SetFilename1andFilename2 "$Script:DomainDNSRoot"
}
Else
{
SetFilename1andFilename2 "$Script:ForestRootDomain"
}

If($Section -eq "All" -or $Section -eq "Forest")


{
ProcessForestInformation

ProcessAllDCsInTheForest

ProcessCAInformation

ProcessADOptionalFeatures

ProcessADSchemaItems
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "Sites")


{
ProcessSiteInformation
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "Domains")


{
ProcessDomains
ProcessDomainControllers
[gc]::collect()
}
If($Section -eq "All" -or $Section -eq "OUs")
{
ProcessOrganizationalUnits
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "Groups")


{
ProcessGroupInformation
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "GPOs")


{
ProcessGPOsByDomain

If($GPOInheritance -eq $True)


{
ProcessgGPOsByOUNew
}
Else
{
ProcessgGPOsByOUOld
}
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "Misc")


{
ProcessMiscDataByDomain
[gc]::collect()
}

If($Section -eq "All" -or $Section -eq "Domains")


{
ProcessDCDNSInfo
[gc]::collect()
}

If($Script:Elevated -and ($Section -eq "All" -or $Section -eq "Domains"))


{
ProcessTimeServerInfo
[gc]::collect()
}

If($Script:Elevated -and ($Section -eq "All" -or $Section -eq "Domains"))


{
ProcessEventLogInfo
[gc]::collect()
}

#endregion

#region finish script


Write-Verbose "$(Get-Date): Finishing up document"
#end of document processing

$AbstractTitle = "Microsoft Active Directory Inventory Report V2.16"


$SubjectTitle = "Active Directory Inventory Report V2.16"
UpdateDocumentProperties $AbstractTitle $SubjectTitle

ProcessDocumentOutput

ProcessScriptEnd
[gc]::collect()

#endregion

You might also like