From 084272675e07b37a82add6a4e29ce9163e2f17ba Mon Sep 17 00:00:00 2001 From: lework Date: Thu, 24 Dec 2020 18:20:25 +0800 Subject: [PATCH] add --- bat/vmware-batch.bat | 680 +- powershell/exchange/ADDS_Inventory_V2.ps1 | 16523 ++++++++++++++++ .../Get-ExchangeEnvironmentReport.ps1 | 1454 ++ powershell/exchange/Get-ExchangeTracking.ps1 | 4086 ++++ powershell/exchange/HealthChecker.ps1 | 5839 ++++++ powershell/exchange/MessageTrackingLogGUI.ps1 | 507 + powershell/exchange/Set-Webserver.ps1 | 252 + .../exchange/enable-ad-user-mailbox.ps1 | 31 + shell/get_proc_mem.sh | 176 +- shell/k8s/k8s-app-info.sh | 2 +- 10 files changed, 29216 insertions(+), 334 deletions(-) create mode 100644 powershell/exchange/ADDS_Inventory_V2.ps1 create mode 100644 powershell/exchange/Get-ExchangeEnvironmentReport.ps1 create mode 100644 powershell/exchange/Get-ExchangeTracking.ps1 create mode 100644 powershell/exchange/HealthChecker.ps1 create mode 100644 powershell/exchange/MessageTrackingLogGUI.ps1 create mode 100644 powershell/exchange/Set-Webserver.ps1 create mode 100644 powershell/exchange/enable-ad-user-mailbox.ps1 diff --git a/bat/vmware-batch.bat b/bat/vmware-batch.bat index 8096a8b..6c5d814 100644 --- a/bat/vmware-batch.bat +++ b/bat/vmware-batch.bat @@ -1,333 +1,349 @@ -@echo off -Setlocal enabledelayedexpansion -::CODER BY lework - -title VMware Workstation 虚拟机批量管理 - -IF EXIST "%PROGRAMFILES%\VMWare\VMWare Workstation\vmrun.exe" SET VMwarePath=%PROGRAMFILES%\VMWare\VMWare Workstation -IF EXIST "%PROGRAMFILES(X86)%\VMWare\VMWare Workstation\vmrun.exe" SET VMwarePath=%PROGRAMFILES(X86)%\VMWare\VMWare Workstation -IF EXIST "%PROGRAMFILES%\VMware\VMware VIX\vmrun.exe" SET VMwarePath=%PROGRAMFILES%\VMware\VMware VIX -IF EXIST "%PROGRAMFILES(X86)%\VMware\VMware VIX\vmrun.exe" SET VMRUN=%PROGRAMFILES(X86)%\VMware\VMware VIX - -:: VMware安装地址 -# set VMwarePath="C:\Program Files (x86)\VMware\VMware Workstation" -:: 虚拟机存放目录 -set VMpath="D:\Virtual Machines" -:: 虚拟机名称 -set VMname=CentOS_7.8_x64_node -:: 虚拟机快照名称 -set VMSnapshot=init -:: 新建虚拟机数目 -set VMcount=5 -:: 虚拟机owa模板位置 -set VMowa="D:\vmware owa\CentOS_7.8_x64_base.ova" -:: 模板系统用户名 -set VMuser=root -:: 模板系统密码 -set VMpass=123456 -:: 虚拟机网络 -set VMnetwork=192.168.77 -:: 虚拟机ip开始地址 -set VMipStart=130 - - - -:init -cls -echo. -echo. VMware Workstation 虚拟机批量管理 -echo. -echo ============================== -echo. -echo. 输入 0 一键初始化(包含1,2,3步骤) -echo. 输入 1 创建虚拟机 -echo. 输入 2 设置ip地址 -echo. 输入 3 创建快照 -echo. 输入 4 查看启动的虚拟机 -echo. 输入 5 启动虚拟机 -echo. 输入 6 关闭虚拟机 -echo. 输入 7 重启虚拟机 -echo. 输入 8 恢复虚拟机快照 -echo. 输入 9 删除虚拟机 -echo. 输入 10 挂起虚拟机 -echo. 输入 11 暂停虚拟机 -echo. 输入 12 恢复虚拟机 -echo. 输入 q 退出 -echo. -echo ============================== -echo. - -cd /d "%VMwarePath%" - -set "input=" -set /p input=请输入您的选择: -echo. -if "%input%"=="q" goto exit -if "%input%"=="0" goto oneKey -if "%input%"=="1" goto create -if "%input%"=="2" goto setip -if "%input%"=="3" goto snapshot -if "%input%"=="4" goto list -if "%input%"=="5" goto start -if "%input%"=="6" goto stop -if "%input%"=="7" goto restart -if "%input%"=="8" goto revertToSnapshot -if "%input%"=="9" goto delete -if "%input%"=="10" goto suspend -if "%input%"=="11" goto pausevm -if "%input%"=="12" goto unpausevm - -:wait -echo. -echo 执行完毕, 等待中... -for /l %%a in (1,1,5) do ( -ping /n 2 127.1>nul -set /p a=^> /etc/hostname; sudo sed -i 's/IPADDR=.*$/IPADDR="!ip!"/g' /etc/sysconfig/network-scripts/ifcfg-e*;/etc/init.d/network restart || sudo sed -i 's/address .*$/address !ip!/g' /etc/network/interfaces;/etc/init.d/network restart" nogui -) - -echo. -echo 创建快照: -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui -vmrun -T ws snapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui -vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx nogui -) - -goto wait - - -:start -echo [启动虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws start !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:stop -echo [关闭虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:restart -echo [重启虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui -vmrun -T ws start !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:suspend -echo [挂起虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws suspend !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:pausevm -echo [暂停虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws pause !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:unpausevm -echo [恢复虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws unpause !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:revertToSnapshot -echo [恢复虚拟机快照...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -set /p VMSnapshot=请输入快照名称(默认:%VMSnapshot%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws revertToSnapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui -) -goto wait - -:list -echo [虚拟机启动列表...] -vmrun list -echo. -pause -goto wait - - -:create -echo [创建虚拟机...] -set "cname=" -set "ccount=" -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): - -echo. -echo ============= -echo. -echo. 虚拟机模板: !VMowa! -echo. 虚拟机存放目录: !VMpath! -echo. 虚拟机名称: !VMname! -echo. 虚拟机数量: !VMcount! -echo. -echo ============= - -for /l %%a in (1,1,!VMcount!) do ( -echo. -echo 创建虚拟机: !VMname!%%a -cd OVFTool -ovftool --name=!VMname!%%a !VMowa! !VMpath! -cd .. -echo 启动虚拟机: !VMname!%%a -vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx -) -goto wait - - -:delete -echo [删除虚拟机...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -set is=no -set /p is=确定删除么?(yes/no, 默认:%is%): - -if "%is%" NEQ "yes" ( -echo 已取消 -goto wait -) - -echo 关闭vmware -taskkill /f /t /im vmware.exe - -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo 删除: !name! -vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui -vmrun deleteVM !VMpath!\!name!\!name!.vmx nogui -) -goto wait - - -:snapshot -echo [创建快照...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -set /p VMSnapshot=请输入快照名称(默认:%VMSnapshot%): -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -echo !name! -vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui -vmrun -T ws snapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui -vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx nogui -) -goto wait - - -:setip -echo [设置ip地址...] -set /p VMname=请输入虚拟机名称(默认:%VMname%): -set /p VMcount=请输入虚拟机数量(默认:%VMcount%): -set /p VMuser=请输入用户名(默认:%VMuser%): -set /p VMpass=请输入密码(默认:%VMpass%): -set /p VMipStart=请输入ip开始地址(默认:%VMipStart%): - -for /l %%a in (1,1,%VMcount%) do ( -set name=!VMname!%%a -set /a num=%VMipStart%+%%a-1 -set ip=!VMnetwork!.!num! -echo !name!:!ip! -vmrun -T ws -gu !VMuser! -gp !VMpass! runProgramInGuest !VMpath!\!name!\!name!.vmx /bin/bash -c "sudo sed -i 's/IPADDR=.*$/IPADDR="!ip!"/g' /etc/sysconfig/network-scripts/ifcfg-e*;/etc/init.d/network restart || sudo sed -i 's/address .*$/address !ip!/g' /etc/network/interfaces;/etc/init.d/network restart" nogui -) -goto wait - - -:exit -echo 退出... -ping /n 5 127.1>nul +@echo off +Setlocal enabledelayedexpansion +::CODER BY lework +:: 虚拟机需安装vmtool + +title VMware Workstation 虚拟机批量管理 + +IF EXIST "%PROGRAMFILES%\VMWare\VMWare Workstation\vmrun.exe" SET VMwarePath=%PROGRAMFILES%\VMWare\VMWare Workstation +IF EXIST "%PROGRAMFILES(X86)%\VMWare\VMWare Workstation\vmrun.exe" SET VMwarePath=%PROGRAMFILES(X86)%\VMWare\VMWare Workstation +IF EXIST "%PROGRAMFILES%\VMware\VMware VIX\vmrun.exe" SET VMwarePath=%PROGRAMFILES%\VMware\VMware VIX +IF EXIST "%PROGRAMFILES(X86)%\VMware\VMware VIX\vmrun.exe" SET VMRUN=%PROGRAMFILES(X86)%\VMware\VMware VIX + +:: VMware安装地址 +# set VMwarePath="C:\Program Files (x86)\VMware\VMware Workstation" +:: 虚拟机存放目录 +set VMpath="D:\Virtual Machines" +:: 虚拟机名称 +set VMname=Debian_10.2_x64_node +:: 虚拟机快照名称 +set VMSnapshot=init +:: 新建虚拟机数目 +set VMcount=5 +:: 虚拟机owa模板位置 +set VMowa="D:\vmware owa\Debian_10.2_x64_base.ova" +:: 模板系统用户名 +set VMuser=root +:: 模板系统密码 +set VMpass=123456 +:: 虚拟机网络 +set VMnetwork=192.168.77 +:: 虚拟机ip开始地址 +set VMipStart=180 + + + +:init +cls +echo. +echo. VMware Workstation 虚拟机批量管理 +echo. +echo ============================== +echo. +echo. 输入 0 一键初始化(包含1,2,3步骤) +echo. 输入 1 创建虚拟机 +echo. 输入 2 设置ip地址 +echo. 输入 3 创建快照 +echo. 输入 4 查看启动的虚拟机 +echo. 输入 5 启动虚拟机 +echo. 输入 6 关闭虚拟机 +echo. 输入 7 重启虚拟机 +echo. 输入 8 恢复虚拟机快照 +echo. 输入 9 删除虚拟机 +echo. 输入 10 挂起虚拟机 +echo. 输入 11 暂停虚拟机 +echo. 输入 12 恢复虚拟机 +echo. 输入 13 删除快照 +echo. 输入 q 退出 +echo. +echo ============================== +echo. + +cd /d "%VMwarePath%" + +set "input=" +set /p input=请输入您的选择: +echo. +if "%input%"=="q" goto exit +if "%input%"=="0" goto oneKey +if "%input%"=="1" goto create +if "%input%"=="2" goto setip +if "%input%"=="3" goto snapshot +if "%input%"=="4" goto list +if "%input%"=="5" goto start +if "%input%"=="6" goto stop +if "%input%"=="7" goto restart +if "%input%"=="8" goto revertToSnapshot +if "%input%"=="9" goto delete +if "%input%"=="10" goto suspend +if "%input%"=="11" goto pausevm +if "%input%"=="12" goto unpausevm +if "%input%"=="13" goto delsnapshot + +:wait +echo. +echo 执行完毕, 等待中... +for /l %%a in (1,1,5) do ( +ping /n 2 127.1>nul +set /p a=^> /etc/hostname && echo '127.0.0.1 node!num!' >> /etc/hosts; sudo sed -i 's/IPADDR=.*$/IPADDR="!ip!"/g' /etc/sysconfig/network-scripts/ifcfg-e* || sudo sed -i 's/address .*$/address !ip!/g' /etc/network/interfaces; init 6" nogui +) + +echo. +echo 创建快照: +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui +vmrun -T ws snapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui +vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx nogui +) + +goto wait + + +:start +echo [启动虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws start !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:stop +echo [关闭虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:restart +echo [重启虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui +vmrun -T ws start !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:suspend +echo [挂起虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws suspend !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:pausevm +echo [暂停虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws pause !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:unpausevm +echo [恢复虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws unpause !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:revertToSnapshot +echo [恢复虚拟机快照...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +set /p VMSnapshot=请输入快照名称(默认:%VMSnapshot%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws revertToSnapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui +) +goto wait + +:list +echo [虚拟机启动列表...] +vmrun list +echo. +pause +goto wait + + +:create +echo [创建虚拟机...] +set "cname=" +set "ccount=" +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): + +echo. +echo ============= +echo. +echo. 虚拟机模板: !VMowa! +echo. 虚拟机存放目录: !VMpath! +echo. 虚拟机名称: !VMname! +echo. 虚拟机数量: !VMcount! +echo. +echo ============= + +for /l %%a in (1,1,!VMcount!) do ( +echo. +echo 创建虚拟机: !VMname!%%a +cd OVFTool +ovftool --name=!VMname!%%a !VMowa! !VMpath! +cd .. +echo 启动虚拟机: !VMname!%%a +vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx +) +goto wait + + +:delete +echo [删除虚拟机...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +set is=no +set /p is=确定删除么?(yes/no, 默认:%is%): + +if "%is%" NEQ "yes" ( +echo 已取消 +goto wait +) + +echo 关闭vmware +taskkill /f /t /im vmware.exe + +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo 删除: !name! +vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui +vmrun deleteVM !VMpath!\!name!\!name!.vmx nogui +) +goto wait + + +:snapshot +echo [创建快照...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +set /p VMSnapshot=请输入快照名称(默认:%VMSnapshot%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws stop !VMpath!\!name!\!name!.vmx nogui +vmrun -T ws snapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! nogui +vmrun -T ws start !VMpath!\!VMname!%%a\!VMname!%%a.vmx nogui +) +goto wait + +:delsnapshot +echo [删除快照...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +set /p VMSnapshot=请输入快照名称(默认:%VMSnapshot%): +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +echo !name! +vmrun -T ws deleteSnapshot !VMpath!\!name!\!name!.vmx !VMSnapshot! +) +goto wait + + + +:setip +echo [设置ip地址...] +set /p VMname=请输入虚拟机名称(默认:%VMname%): +set /p VMcount=请输入虚拟机数量(默认:%VMcount%): +set /p VMuser=请输入用户名(默认:%VMuser%): +set /p VMpass=请输入密码(默认:%VMpass%): +set /p VMipStart=请输入ip开始地址(默认:%VMipStart%): + +for /l %%a in (1,1,%VMcount%) do ( +set name=!VMname!%%a +set /a num=%VMipStart%+%%a-1 +set ip=!VMnetwork!.!num! +echo !name!:!ip! +vmrun -T ws -gu !VMuser! -gp !VMpass! runProgramInGuest !VMpath!\!name!\!name!.vmx -noWait /bin/bash -c "echo 'node!num!' > /etc/hostname && echo '127.0.0.1 node!num!' >> /etc/hosts; sudo sed -i 's/IPADDR=.*$/IPADDR="!ip!"/g' /etc/sysconfig/network-scripts/ifcfg-e* || sudo sed -i 's/address .*$/address !ip!/g' /etc/network/interfaces; init 6" nogui +) +goto wait + + +:exit +echo 退出... +ping /n 5 127.1>nul exit \ No newline at end of file diff --git a/powershell/exchange/ADDS_Inventory_V2.ps1 b/powershell/exchange/ADDS_Inventory_V2.ps1 new file mode 100644 index 0000000..711c8ae --- /dev/null +++ b/powershell/exchange/ADDS_Inventory_V2.ps1 @@ -0,0 +1,16523 @@ +锘#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 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, 2020 at 6PM is 2020-06-01_1800. + Output filename will be ReportName_2020-06-01_1800.docx (or .pdf). + This parameter is disabled by default. +.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 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 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 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 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 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 Log + Generates a log file for troubleshooting. +.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 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 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 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 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. +.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 SuperSleuth@SherlockHolmes.com + + Will use: + Sherlock Holmes Consulting for the Company Name. + Facet for the Cover Page format. + Dr. Watson for the User Name. + SuperSleuth@SherlockHolmes.com 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, 2020 at 6PM is 2020-06-01_1800. + Output filename will be company.tld_2020-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, 2020 at 6PM is 2020-06-01_1800. + Output filename will be corp.carlwebster.com_2020-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 -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" +.EXAMPLE + PS C:\PSScript > .\ADDS_Inventory_V2.ps1 -ADForest corp.carlwebster.com + -SmtpServer mail.domain.tld + -From XDAdmin@domain.tld + -To ITGroup@domain.tld + -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 XDAdmin@domain.tld, + sending to ITGroup@domain.tld. + + The script will use the default SMTP 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 + -SmtpServer mailrelay.domain.tld + -From Anonymous@domain.tld + -To ITGroup@domain.tld + + ***SENDING UNAUTHENTICATED EMAIL*** + + The script will use the email server mailrelay.domain.tld, sending from + anonymous@domain.tld, sending to ITGroup@domain.tld. + + To send unauthenticated email using an email relay server requires the From email account + to use the name Anonymous. + + The script will use the default SMTP port 25 and will not use SSL. + + ***GMAIL/G SUITE SMTP RELAY*** + https://support.google.com/a/answer/2956491?hl=en + https://support.google.com/a/answer/176600?hl=en + + To send email using a Gmail or g-suite account, you may have to turn ON + the "Less secure app access" option on your account. + ***GMAIL/G SUITE SMTP RELAY*** + + The script will generate an anonymous secure password for the anonymous@domain.tld + account. +.EXAMPLE + PS C:\PSScript > .\ADDS_Inventory_V2.ps1 + -SmtpServer labaddomain-com.mail.protection.outlook.com + -UseSSL + -From SomeEmailAddress@labaddomain.com + -To ITGroupDL@labaddomain.com + + ***OFFICE 365 Example*** + + https://docs.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-office-3 + + This uses Option 2 from the above link. + + ***OFFICE 365 Example*** + + The script will use the email server labaddomain-com.mail.protection.outlook.com, + sending from SomeEmailAddress@labaddomain.com, sending to ITGroupDL@labaddomain.com. + + The script will use the default SMTP port 25 and will use SSL. +.EXAMPLE + PS C:\PSScript > .\ADDS_Inventory_V2.ps1 + -SmtpServer smtp.office365.com + -SmtpPort 587 + -UseSSL + -From Webster@CarlWebster.com + -To ITGroup@CarlWebster.com + + The script will use the email server smtp.office365.com on port 587 using SSL, + sending from webster@carlwebster.com, sending to ITGroup@carlwebster.com. + + If the current user's credentials are not valid to send email, + the user will be prompted to enter valid credentials. +.EXAMPLE + PS C:\PSScript > .\ADDS_Inventory_V2.ps1 + -SmtpServer smtp.gmail.com + -SmtpPort 587 + -UseSSL + -From Webster@CarlWebster.com + -To ITGroup@CarlWebster.com + + *** NOTE *** + To send email using a Gmail or g-suite account, you may have to turn ON + the "Less secure app access" option on your account. + *** NOTE *** + + The script will use the email server smtp.gmail.com on port 587 using SSL, + sending from webster@gmail.com, sending to ITGroup@carlwebster.com. + + If the current user's credentials are not valid to send email, + the user will be prompted to enter valid credentials. +.INPUTS + None. You cannot pipe objects to this script. +.OUTPUTS + No objects are output from this script. This script creates a Word or PDF document. +.NOTES + NAME: ADDS_Inventory_V2.ps1 + VERSION: 2.26 + AUTHOR: Carl Webster and Michael B. Smith + LASTEDIT: May 8, 2020 +#> + + +#thanks to @jeffwouters and Michael B. Smith for helping me with these parameters +[CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = "None", DefaultParameterSetName = "Word") ] + +Param( + [parameter(ParameterSetName="HTML",Mandatory=$False)] + [Switch]$HTML=$False, + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [Switch]$MSWord=$False, + + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Switch]$PDF=$False, + + [parameter(ParameterSetName="Text",Mandatory=$False)] + [Switch]$Text=$False, + + [parameter(Mandatory=$False)] + [Switch]$AddDateTime=$False, + + [parameter(Mandatory=$False)] + [string]$ADDomain="", + + [parameter(Mandatory=$False)] + [string]$ADForest=$Env:USERDNSDOMAIN, + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CA")] + [ValidateNotNullOrEmpty()] + [string]$CompanyAddress="", + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CE")] + [ValidateNotNullOrEmpty()] + [string]$CompanyEmail="", + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CF")] + [ValidateNotNullOrEmpty()] + [string]$CompanyFax="", + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CN")] + [ValidateNotNullOrEmpty()] + [string]$CompanyName="", + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CPh")] + [ValidateNotNullOrEmpty()] + [string]$CompanyPhone="", + + [parameter(Mandatory=$False)] + [Alias("ServerName")] + [string]$ComputerName=$Env:USERDNSDOMAIN, + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("CP")] + [ValidateNotNullOrEmpty()] + [string]$CoverPage="Sideline", + + [parameter(Mandatory=$False)] + [Switch]$DCDNSInfo=$False, + + [parameter(Mandatory=$False)] + [Switch]$Dev=$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)] + [Switch]$Log=$False, + + [parameter(Mandatory=$False)] + [Alias("MAX")] + [Switch]$MaxDetails=$False, + + [parameter(Mandatory=$False)] + [Alias("SI")] + [Switch]$ScriptInfo=$False, + + [parameter(Mandatory=$False)] + [array]$Section="All", + + [parameter(Mandatory=$False )] + [Switch]$Services=$False, + + [parameter(ParameterSetName="Word",Mandatory=$False)] + [parameter(ParameterSetName="PDF",Mandatory=$False)] + [Alias("UN")] + [ValidateNotNullOrEmpty()] + [string]$UserName=$env:username, + + [parameter(Mandatory=$False)] + [string]$SmtpServer="", + + [parameter(Mandatory=$False)] + [int]$SmtpPort=25, + + [parameter(Mandatory=$False)] + [switch]$UseSSL=$False, + + [parameter(Mandatory=$False)] + [string]$From="", + + [parameter(Mandatory=$False)] + [string]$To="" + + ) + + +#webster@carlwebster.com +#Created by Carl Webster and Michael B. Smith +#webster@carlwebster.com +#@carlwebster on Twitter +#https://www.CarlWebster.com +# +#michael@smithcons.com +#@essentialexch on Twitter +#https://www.essential.exchange/blog/ +# +#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.26 8-May-2020 +# Add checking for a Word version of 0, which indicates the Office installation needs repairing +# Add Receive Side Scaling setting to Function OutputNICItem +# Change color variables $wdColorGray15 and $wdColorGray05 from [long] to [int] +# Change location of the -Dev, -Log, and -ScriptInfo output files from the script folder to the -Folder location (Thanks to Guy Leech for the "suggestion") +# Change Text output to use [System.Text.StringBuilder] +# Updated Functions Line and SaveAndCloseTextDocument +# Reformatted the terminating Write-Error messages to make them more visible and readable in the console +# Update Function SetWordCellFormat to change parameter $BackgroundColor to [int] +# Update Functions GetComputerWMIInfo and OutputNicInfo to fix two bugs in NIC Power Management settings +# +#Version 2.25 21-Apr-2020 +# Remove the SMTP parameterset and manually verify the parameters +# Reorder parameters +# Update Function SendEmail to handle anonymous unauthenticated email +# Update Help Text with examples +# +#Version 2.24 13-Feb-2020 +# Fixed several variable name typos +# General code cleanup +# Updated the following Exchange Schema Versions: +# "15312" = "Exchange 2013 CU7 through CU23" +# "15317" = "Exchange 2016 Preview and RTM" +# "15332" = "Exchange 2016 CU7 through CU15" +# "17000" = "Exchange 2019 RTM/CU1" +# "17001" = "Exchange 2019 CU2-CU4" +# +#Version 2.23 17-Dec-2019 +# Fix Swedish Table of Contents (Thanks to Johan Kallio) +# From +# 'sv-' { 'Automatisk inneh氓llsf枚rteckning2'; Break } +# To +# 'sv-' { 'Automatisk inneh氓llsf枚rteckn2'; Break } +# Updated help text +# +#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 +# Unable to retrieve Read-only status on +# 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 鈥揺q $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' + +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 " + `n`n + `t`t + Unable to determine output parameter. + `n`n + `t`t + Script cannot continue. + `n`n + " + 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`n + `t`t + The Section parameter specified, $($Section), is an invalid Section option. + `n`n + `t`t + Valid options are: + + `t`tForest + `t`tSites + `t`tDomains + `t`tOUs + `t`tGroups + `t`tGPOs + `t`tMisc + `t`tAll + + `t`t + Script cannot continue. + `n`n + " + 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 " + `n`n + `t`t + Folder $Folder is a file, not a folder. + `n`n + `t`t + Script cannot continue. + `n`n + " + Exit + } + } + Else + { + #does not exist + Write-Error " + `n`n + `t`t + Folder $Folder does not exist. + `n`n + `t`t + Script cannot continue. + `n`n + " + Exit + } +} + +If($Folder -eq "") +{ + $Script:pwdpath = $pwd.Path +} +Else +{ + $Script:pwdpath = $Folder +} + +If($Script:pwdpath.EndsWith("\")) +{ + #remove the trailing \ + $Script:pwdpath = $Script:pwdpath.SubString(0, ($Script:pwdpath.Length - 1)) +} + + +#V2.18 added +If($Log) +{ + #start transcript logging + $Script:LogPath = "$($Script:pwdpath)\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 = "$($Script:pwdpath)\ADInventoryScriptErrors_$(Get-Date -f yyyy-MM-dd_HHmm).txt" +} + + +If(![String]::IsNullOrEmpty($SmtpServer) -and [String]::IsNullOrEmpty($From) -and [String]::IsNullOrEmpty($To)) +{ + Write-Error " + `n`n + `t`t + You specified an SmtpServer but did not include a From or To email address. + `n`n + `t`t + Script cannot continue. + `n`n" + Exit +} +If(![String]::IsNullOrEmpty($SmtpServer) -and [String]::IsNullOrEmpty($From) -and ![String]::IsNullOrEmpty($To)) +{ + Write-Error " + `n`n + `t`t + You specified an SmtpServer and a To email address but did not include a From email address. + `n`n + `t`t + Script cannot continue. + `n`n" + Exit +} +If(![String]::IsNullOrEmpty($SmtpServer) -and [String]::IsNullOrEmpty($To) -and ![String]::IsNullOrEmpty($From)) +{ + Write-Error " + `n`n + `t`t + You specified an SmtpServer and a From email address but did not include a To email address. + `n`n + `t`t + Script cannot continue. + `n`n" + Exit +} +If(![String]::IsNullOrEmpty($From) -and ![String]::IsNullOrEmpty($To) -and [String]::IsNullOrEmpty($SmtpServer)) +{ + Write-Error " + `n`n + `t`t + You specified From and To email addresses but did not include the SmtpServer. + `n`n + `t`t + Script cannot continue. + `n`n" + Exit +} +If(![String]::IsNullOrEmpty($From) -and [String]::IsNullOrEmpty($SmtpServer)) +{ + Write-Error " + `n`n + `t`t + You specified a From email address but did not include the SmtpServer. + `n`n + `t`t + Script cannot continue. + `n`n" + Exit +} +If(![String]::IsNullOrEmpty($To) -and [String]::IsNullOrEmpty($SmtpServer)) +{ + Write-Error " + `n`n + `t`t + You specified a To email address but did not include the SmtpServer. + `n`n + `t`t + Script cannot continue. + `n`n" + 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 + [int]$wdColorGray15 = 14277081 + [int]$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) +{ + [System.Text.StringBuilder] $global:Output = New-Object System.Text.StringBuilder( 16384 ) +} +#endregion + +#region email function +Function SendEmail +{ + Param([string]$Attachments) + Write-Verbose "$(Get-Date): Prepare to email" + + $emailAttachment = $Attachments + $emailSubject = $Script:Title + $emailBody = @" +Hello,
+
+$Script:Title is attached. + +"@ + + If($Dev) + { + Out-File -FilePath $Script:DevErrorFile -InputObject $error 4>$Null + } + + $error.Clear() + + If($From -Like "anonymous@*") + { + #https://serverfault.com/questions/543052/sending-unauthenticated-mail-through-ms-exchange-with-powershell-windows-server + $anonUsername = "anonymous" + $anonPassword = ConvertTo-SecureString -String "anonymous" -AsPlainText -Force + $anonCredentials = New-Object System.Management.Automation.PSCredential($anonUsername,$anonPassword) + + If($UseSSL) + { + Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From ` + -Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To ` + -UseSSL -credential $anonCredentials *>$Null + } + Else + { + Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From ` + -Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To ` + -credential $anonCredentials *>$Null + } + + If($?) + { + Write-Verbose "$(Get-Date): Email successfully sent using anonymous credentials" + } + ElseIf(!$?) + { + $e = $error[0] + + Write-Verbose "$(Get-Date): Email was not sent:" + Write-Warning "$(Get-Date): Exception: $e.Exception" + } + } + Else + { + 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 + } + + If(!$?) + { + $e = $error[0] + + #error 5.7.57 is O365 and error 5.7.0 is gmail + If($null -ne $e.Exception -and $e.Exception.ToString().Contains("5.7")) + { + #The server response was: 5.7.xx 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 -UserName $From -Message "Enter the 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 + } + + If($?) + { + Write-Verbose "$(Get-Date): Email successfully sent using new credentials" + } + ElseIf(!$?) + { + $e = $error[0] + + 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 + # k.baggerman@myvirtualvision.com + # @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 $RemoteComputerName + } + 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, [string]$RemoteComputerName) + + $powerMgmt = Get-WmiObject -computername $RemoteComputerName 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 ($ThisNic.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} + } + + #attempt to get Receive Side Scaling setting + $RSSEnabled = "N/A" + Try + { + #https://ios.developreference.com/article/10085450/How+do+I+enable+VRSS+(Virtual+Receive+Side+Scaling)+for+a+Windows+VM+without+relying+on+Enable-NetAdapterRSS%3F + $RSSEnabled = (Get-WmiObject -ComputerName $RemoteComputerName MSFT_NetAdapterRssSettingData -Namespace "root\StandardCimV2" -ea 0).Enabled + + If($RSSEnabled) + { + $RSSEnabled = "Enabled" + } + ELse + { + $RSSEnabled = "Disabled" + } + } + + Catch + { + $RSSEnabled = "Not available on $Script:RunningOS" + } + + $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 = "Receive Side Scaling"; Value = $RSSEnabled; }) > $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 "Receive Side Scaling`t: " $RSSEnabled + 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 += @(,('Receive Side Scaling',($htmlsilver -bor $htmlbold),$RSSEnabled,$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 + # Vladimir.Radojevic@Commerzreal.com + + # 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 } + # fix in 2.23 thanks to Johan Kallio 'sv-' { 'Automatisk inneh氓llsf枚rteckning2'; Break } + 'sv-' { 'Automatisk inneh氓llsf枚rteckn2'; 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 鈥搉e ((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`t + The Word object could not be created. + `n`n + `t`t + You may need to repair your Word installation. + `n`n + `t`t + Script 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`t + Unable to determine the Word language value. + `n`n + `t`t + Script 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`t + Microsoft Word 2007 is no longer supported. + `n`n + `t`t + Script will end. + `n`n + " + AbortScript + } + ElseIf($Script:WordVersion -eq 0) + { + Write-Error " + `n`n + `t`t + The Word Version is 0. You should run a full online repair of your Office installation. + `n`n + `t`t + Script cannot continue. + `n`n + " + Exit + } + Else + { + $ErrorActionPreference = $SaveEAPreference + Write-Error " + `n`n + `t`t + You are running an untested or unsupported version of Microsoft Word. + `n`n + `t`t + Script will end. + `n`n + `t`t + Please send info on your version of Word to webster@carlwebster.com + `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`t + For $($Script:WordProduct), $($CoverPage) is not a valid Cover Page option. + `n`n + `t`t + Script 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`t + An empty Word document could not be created. + `n`n + `t`t + Script 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`t + An unknown error happened selecting the entire Word document for default formatting options. + `n`n + `t`t + Script 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 +#@essentialexch on Twitter +#https://essential.exchange/blog +#for creating the formatted text report +#created March 2011 +#updated March 2014 +# updated March 2019 to use StringBuilder (about 100 times more efficient than simple strings) +{ + Param + ( + [Int] $tabs = 0, + [String] $name = '', + [String] $value = '', + [String] $newline = [System.Environment]::NewLine, + [Switch] $nonewline + ) + + while( $tabs -gt 0 ) + { + $null = $global:Output.Append( "`t" ) + $tabs-- + } + + If( $nonewline ) + { + $null = $global:Output.Append( $name + $value ) + } + Else + { + $null = $global:Output.AppendLine( $name + $value ) + } +} + +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 " + } else { + $Output += "" + } + + $Output += ("

Exchange Environment Report

Organization: {3}

+

Generated {0}

+ + + " -f (Get-Date -Format $DateLabelFormat), $ExchangeEnvironment.TotalMailboxesByVersion.Count, $LabelTotalServers, $ExchangeEnvironment.OrganizationName) + + if ($ExchangeEnvironment.RemoteMailboxes) { + $Output+=("" -f ($ExchangeEnvironment.TotalMailboxesByVersion.Count+2), $LabelTotalMailboxes) + } + else { + $Output+=("" -f ($ExchangeEnvironment.TotalMailboxesByVersion.Count+1), $LabelTotalMailboxes) + } + + $Output+=(" + " -f $ExchangeEnvironment.TotalServersByRole.Count, $LabelTotalRoles) + + # Show Column Headings based on the Exchange versions we have + $ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+=""} + $ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator() | Sort-Object -Property Name | ForEach-Object{$Output+=""} + + if ($ExchangeEnvironment.RemoteMailboxes) { + $Output+="" + } + + $Output+="" + + # Exchange Server Roles + $ExchangeEnvironment.TotalServersByRole.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+=""} + + $Output += '' + + $Output += "" + + $ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="" } + $ExchangeEnvironment.TotalMailboxesByVersion.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+="" } + + if ($RemoteMailboxes) { + $Output+="" + } + + $Output += "" + + $ExchangeEnvironment.TotalServersByRole.GetEnumerator()|Sort-Object -Property Name| ForEach-Object{$Output+=""} + + #$Output+="
{2}{1}{1}{1}
$($ExVersionStrings[$_.Key].Short)$($ExVersionStrings[$_.Key].Short)Office 365Org$($ExRoleStrings[$_.Key].Short)
$($_.Value.ServerCount)$($_.Value.MailboxCount)$($ExchangeEnvironment.RemoteMailboxes)$($ExchangeEnvironment.TotalMailboxes)$($_.Value)

" + $Output += '
' + + $Output +} + +function Get-HtmlDagHeader { + [CmdletBinding()] + param ( + $DAG + ) + + # Database Availability Group Header + $Output+=" + + + +
Database Availability Group NameMember Count# DatabasesDatabase Availability Group Members
$($DAG.Name)$($DAG.MemberCount)$(($DAG.Databases | Measure-Object).Count)" + + $DAG.Members | ForEach-Object { $Output+=('{0} ' -f $_) } + + $Output += '

' + + $Output +} + +# Sub Function to neatly update progress +function Show-ProgressBar { + [CmdletBinding()] + param( + [int]$PercentComplete, + [string]$Status, + [int]$Stage + ) + + $TotalStages=5 + Write-Progress -Id 1 -Activity 'Get-ExchangeEnvironmentReport' -Status $Status -PercentComplete (($PercentComplete/$TotalStages)+(1/$TotalStages*$Stage*100)) +} + +# 1. Initial Startup + +# 1.0 Check Powershell Version +if ((Get-Host).Version.Major -eq 1) { + throw 'Powershell Version 1 not supported' +} + +# 1.1 Check Exchange Management Shell, attempt to load +if (!(Get-Command -Name Get-ExchangeServer -ErrorAction SilentlyContinue)) +{ + # 2019-05-17 Thomas Stensitzki, Support for Exchange Scripts located in non-default locations + # Use $env:ExchangeInstallPath for Exchange 2010/2013+ installations + $ExchangeInstallPath = $env:ExchangeInstallPath + + if (($ExchangeInstallPath -eq '') -or ($ExchangeInstallPath -eq $null)) { + # $env:ExchangeInstallPath not available on Exchange Server 2007 Setups + try { + $ExchangeInstallPath = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Exchange\Setup).MsiInstallPath + } + catch {} + } + + Write-Verbose -Message ('Exchange Install Path: {0}' -f $ExchangeInstallPath) + + $RemoteExchangePath = Join-Path -Path $ExchangeInstallPath -ChildPath 'bin\RemoteExchange.ps1' + $LocalExchangePath = Join-Path -Path $ExchangeInstallPath -ChildPath 'bin\Exchange.ps1' + + if (Test-Path -Path $RemoteExchangePath) { + . $RemoteExchangePath + Connect-ExchangeServer -auto + } + elseif (Test-Path -Path $LocalExchangePath) { + Add-PSSnapIn -Name Microsoft.Exchange.Management.PowerShell.Admin + . $LocalExchangePath + } + else { + throw 'Exchange Management Shell cannot be loaded' + } +} + +# 1.2 Check if -SendMail parameter set and if so check -MailFrom, -MailTo and -MailServer are set +if ($SendMail) +{ + if (!$MailFrom -or !$MailTo -or !$MailServer) + { + throw 'If -SendMail specified, you must also specify -MailFrom, -MailTo and -MailServer' + } +} + +# 1.3 Check Exchange Management Shell Version +if ((Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.Admin -ErrorAction SilentlyContinue)) +{ + $E2010 = $false; + if (Get-ExchangeServer | Where-Object {$_.AdminDisplayVersion.Major -gt 14}) { + Write-Warning -Message "Exchange 2010 or higher detected. You'll get better results if you run this script from the latest management shell" + } +} +else{ + + $E2010 = $true + + # 2019-05-17 Thomas Stensitzki, Support for Exchange 2013+ servers with installed management tools + $localversion = $localserver = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiProductMajor + + if ($localversion -eq 15) { $E2013 = $true } +} + +# 1.4 Check view entire forest if set (by default, true) +if ($E2010) { + Set-ADServerSettings -ViewEntireForest:$ViewEntireForest +} +else { + $global:AdminSessionADSettings.ViewEntireForest = $ViewEntireForest +} + +# 1.5 Initial Variables + +# 1.5.1 Hashtable to update with environment data +$ExchangeEnvironment = @{ + Sites = @{} + Pre2007 = @{} + Servers = @{} + DAGs = @() + NonDAGDatabases = @() + OrganizationName = '' +} + +# 1.5.7 Exchange Major Version String Mapping +$ExMajorVersionStrings = @{ + '6.0' = @{Long='Exchange 2000';Short='E2000'} + '6.5' = @{Long='Exchange 2003';Short='E2003'} + '8' = @{Long='Exchange 2007';Short='E2007'} + '14' = @{Long='Exchange 2010';Short='E2010'} + '15' = @{Long='Exchange 2013';Short='E2013'} + '15.1' = @{Long='Exchange 2016';Short='E2016'} + '15.2' = @{Long='Exchange 2019';Short='E2019'} #2019-05-17 TST Exchange Server 2019 added +} + +# 1.5.8 Exchange Service Pack String Mapping +$ExSPLevelStrings = @{ + '0' = 'RTM' + '1' = 'SP1' + '2' = 'SP2' + '3' = 'SP3' + '4' = 'SP4' + 'SP1' = 'SP1' +'SP2' = 'SP2'} + +# Add many CUs +for ($i = 1; $i -le 40; $i++) { + $ExSPLevelStrings.Add("CU$($i)","CU$($i)"); +} + +# 1.5.9 Populate Full Mapping using above info +$ExVersionStrings = @{} + +foreach ($Major in $ExMajorVersionStrings.GetEnumerator()) { + foreach ($Minor in $ExSPLevelStrings.GetEnumerator()) { + $ExVersionStrings.Add("$($Major.Key).$($Minor.Key)",@{Long="$($Major.Value.Long) $($Minor.Value)";Short="$($Major.Value.Short)$($Minor.Value)"}) + } +} +# 1.5.10 Exchange Role String Mapping +$ExRoleStrings = @{'ClusteredMailbox' = @{Short='ClusMBX';Long='CCR/SCC Clustered Mailbox'} + 'Mailbox' = @{Short='MBX';Long='Mailbox'} + 'ClientAccess' = @{Short='CAS';Long='Client Access'} + 'HubTransport' = @{Short='HUB';Long='Hub Transport'} + 'UnifiedMessaging' = @{Short='UM';Long='Unified Messaging'} + 'Edge' = @{Short='EDGE';Long='Edge Transport'} + 'FE' = @{Short='FE';Long='Front End'} + 'BE' = @{Short='BE';Long='Back End'} + 'Hybrid' = @{Short='HYB'; Long='Hybrid'} + 'Coexistence' = @{Short='COEX'; Long='Coexistence'} #2019-05-17 TST Coexistence added +'Unknown' = @{Short='Unknown';Long='Unknown'}} + +# 2 Get Relevant Exchange Information Up-Front + +# 2.1 Get Server, Exchange and Mailbox Information +Show-ProgressBar -PercentComplete 1 -Status 'Getting Exchange Server List' -Stage 1 + +$ExchangeServers = [array](Get-ExchangeServer $ServerFilter | Sort-Object Name) +if (!$ExchangeServers) { + throw ('No Exchange Servers matched by -ServerFilter {0}' -f $ServerFilter) +} + +$HybridServers=@() +if (Get-Command -Name Get-HybridConfiguration -ErrorAction SilentlyContinue) { + $HybridConfig = Get-HybridConfiguration + $HybridConfig.ReceivingTransportServers | ForEach-Object{ $HybridServers+=$_.Name } + $HybridConfig.SendingTransportServers | ForEach-Object{ $HybridServers+=$_.Name } + $HybridServers = $HybridServers | Sort-Object -Unique +} + +Show-ProgressBar -PercentComplete 10 -Status 'Getting Mailboxes' -Stage 1 + +$Mailboxes = [array](Get-Mailbox -ResultSize Unlimited) | Where-Object {$_.ServerName -like $ServerFilter} + +if ($E2010) { + + Show-ProgressBar -PercentComplete 60 -Status 'Getting Archive Mailboxes' -Stage 1 + + $ArchiveMailboxes = [array](Get-Mailbox -Archive -ResultSize Unlimited) | Where-Object {$_.ServerName -like $ServerFilter} + + Show-ProgressBar -PercentComplete 70 -Status 'Getting Remote Mailboxes' -Stage 1 + + $RemoteMailboxes = [array](Get-RemoteMailbox -ResultSize Unlimited) + $ExchangeEnvironment.Add('RemoteMailboxes',$RemoteMailboxes.Count) + + Show-ProgressBar -PercentComplete 90 -Status 'Getting Databases' -Stage 1 + + if ($E2013) { + # 2019-05-17 TST Sorting added + $Databases = [array](Get-MailboxDatabase -IncludePreExchange2013 -Status) | Sort-Object -Property Name | Where-Object {$_.Server -like $ServerFilter} + } + elseif ($E2010) { + # 2019-05-17 TST Sorting added + $Databases = [array](Get-MailboxDatabase -IncludePreExchange2010 -Status) | Sort-Object -Property Name | Where-Object {$_.Server -like $ServerFilter} + } + + $DAGs = [array](Get-DatabaseAvailabilityGroup) | Where-Object {$_.Servers -like $ServerFilter} +} +else { + $ArchiveMailboxes = $null + $ArchiveMailboxStats = $null + $DAGs = $null + + Show-ProgressBar -PercentComplete 90 -Status 'Getting Databases' -Stage 1 + $Databases = [array](Get-MailboxDatabase -IncludePreExchange2007 -Status) | Where-Object {$_.Server -like $ServerFilter} + $ExchangeEnvironment.Add('RemoteMailboxes',0) +} + +# 2.3 Populate Information we know +$ExchangeEnvironment.Add('TotalMailboxes',$Mailboxes.Count + $ExchangeEnvironment.RemoteMailboxes) + +# 2.4 Organizational Info + +$ExchangeEnvironment.OrganizationName = (Get-OrganizationConfig).Name + +# 3 Process High-Level Exchange Information + +# 3.1 Collect Exchange Server Information +for ($i=0; $i -lt $ExchangeServers.Count; $i++) { + Show-ProgressBar -PercentComplete ($i/$ExchangeServers.Count*100) -Status 'Getting Exchange Server Information' -Stage 2 + + # Get Exchange Info + $ExSvr = Get-ExchangeServerInformation -E2010 $E2010 -ExchangeServer $ExchangeServers[$i] -Mailboxes $Mailboxes -Databases $Databases -Hybrids $HybridServers + + # Add to site or pre-Exchange 2007 list + if ($ExSvr.Site) { + # Exchange 2007 or higher + if (!$ExchangeEnvironment.Sites[$ExSvr.Site]) { + $ExchangeEnvironment.Sites.Add($ExSvr.Site,@($ExSvr)) + } + else { + $ExchangeEnvironment.Sites[$ExSvr.Site]+=$ExSvr + } + } + else { + # Exchange 2003 or lower + if (!$ExchangeEnvironment.Pre2007['Pre 2007 Servers']) { + $ExchangeEnvironment.Pre2007.Add('Pre 2007 Servers',@($ExSvr)) + } + else { + $ExchangeEnvironment.Pre2007['Pre 2007 Servers']+=$ExSvr + } + } + + # Add to Servers List + $ExchangeEnvironment.Servers.Add($ExSvr.Name,$ExSvr) +} + +# 3.2 Calculate Environment Totals for Version/Role using collected data +Show-ProgressBar -PercentComplete 1 -Status 'Getting Totals' -Stage 3 + +$ExchangeEnvironment.Add('TotalMailboxesByVersion',(Get-TotalsByVersion -ExchangeEnvironment $ExchangeEnvironment)) +$ExchangeEnvironment.Add('TotalServersByRole',(Get-TotalsByRole -ExchangeEnvironment $ExchangeEnvironment)) + +# 3.4 Populate Environment DAGs +Show-ProgressBar -PercentComplete 5 -Status 'Getting DAG Info' -Stage 3 + +if ($DAGs) { + foreach($DAG in $DAGs) { + $ExchangeEnvironment.DAGs+=(Get-DatabaseAvailabilityGroupInformation -DAG $DAG) + } +} + +# 3.5 Get Database information +Show-ProgressBar -PercentComplete 60 -Status 'Getting Database Info' -Stage 3 + +for ($i=0; $i -lt $Databases.Count; $i++) +{ + $Database = Get-DatabaseInformation -Database $Databases[$i] -ExchangeEnvironment $ExchangeEnvironment -Mailboxes $Mailboxes -ArchiveMailboxes $ArchiveMailboxes -E2010 $E2010 + $DAGDB = $false + for ($j=0; $j -lt $ExchangeEnvironment.DAGs.Count; $j++) { + if ($ExchangeEnvironment.DAGs[$j].Members -contains $Database.ActiveOwner) { + $DAGDB=$true + $ExchangeEnvironment.DAGs[$j].Databases += $Database + } + } + if (!$DAGDB) { + $ExchangeEnvironment.NonDAGDatabases += $Database + } +} + +# 4 Write Information +Show-ProgressBar -PercentComplete 5 -Status 'Writing HTML Report Header' -Stage 4 + +$Output = Get-HtmlReportHeader -ExchangeEnvironment $ExchangeEnvironment -Path $MyInvocation.MyCommand.Path + +# Sites and Servers +Show-ProgressBar -PercentComplete 20 -Status 'Writing HTML Site Information' -Stage 4 + +foreach ($Site in $ExchangeEnvironment.Sites.GetEnumerator()) { + $Output+=Get-HtmlOverview -Servers $Site -ExchangeEnvironment $ExchangeEnvironment -ExRoleStrings $ExRoleStrings +} + +Show-ProgressBar -PercentComplete 40 -Status 'Writing HTML Pre-2007 Information' -Stage 4 + +foreach ($FakeSite in $ExchangeEnvironment.Pre2007.GetEnumerator()) { + $Output+=Get-HtmlOverview -Servers $FakeSite -ExchangeEnvironment $ExchangeEnvironment -ExRoleStrings $ExRoleStrings -Pre2007:$true +} + +Show-ProgressBar -PercentComplete 60 -Status 'Writing HTML DAG Information' -Stage 4 + +foreach ($DAG in $ExchangeEnvironment.DAGs) { + + if ($DAG.MemberCount -gt 0) { + + # Get DAG Header + $Output += Get-HtmlDagHeader -DAG $DAG + + # Get Table HTML for DAG databases + $Output += Get-HtmlDatabaseInformationTable -Databases $DAG.Databases + } +} + +if ($ExchangeEnvironment.NonDAGDatabases.Count) { + + Show-ProgressBar -PercentComplete 80 -Status 'Writing HTML Non-DAG Database Information' -Stage 4 + + $Output+=' +
Mailbox Databases (Non-DAG)
' + + # Get Table HTML for non-DAG databases + $Output+=Get-HtmlDatabaseInformationTable -Databases $ExchangeEnvironment.NonDAGDatabases +} + + +# End +Show-ProgressBar -PercentComplete 90 -Status 'Finishing off..' -Stage 4 + +$Output+='' + +# 2019-05-20 TST Updated to ensure script path as storage location +$HtmlReportFullPath = Join-Path -Path (Split-Path -Path $script:MyInvocation.MyCommand.Path) -ChildPath $HTMLReport + +$Output | Out-File -FilePath $HtmlReportFullPath -Force -Encoding utf8 + + +if ($SendMail) +{ + Show-ProgressBar -PercentComplete 95 -Status 'Sending mail message..' -Stage 4 + + # 2019-05-17 TST, Changed to .NET send method to work as scheduled job + + $smtpMail = New-Object Net.Mail.SmtpClient($MailServer) + + $smtpMessage = New-Object System.Net.Mail.MailMessage $MailFrom, $MailTo + + if(Test-Path -Path $HtmlReportFullPath) { + $smtpAttachment = New-Object Net.Mail.Attachment($HtmlReportFullPath, 'text/plain') + $smtpMessage.Attachments.Add($smtpAttachment) + } + + $smtpMessage.Subject = 'Exchange Environment Report' + $smtpMessage.Body = $Output + $smtpMessage.IsBodyHtml = $true + + $smtpMail.Send($smtpMessage) + + Return 0 +} \ No newline at end of file diff --git a/powershell/exchange/Get-ExchangeTracking.ps1 b/powershell/exchange/Get-ExchangeTracking.ps1 new file mode 100644 index 0000000..8aeb649 --- /dev/null +++ b/powershell/exchange/Get-ExchangeTracking.ps1 @@ -0,0 +1,4086 @@ +锘#Requires -Version 3.0 +#------------------------------------------------------------------------ +# Source File Information (DO NOT MODIFY) +# Source ID: e980eb3e-c21a-46d5-81a4-ce1345d6c8c4 +#------------------------------------------------------------------------ +<# + .NOTES + -------------------------------------------------------------------------------- + Code generated by: SAPIEN Technologies, Inc., PowerShell Studio 2017 v5.4.136 + Generated on: 05/03/2017 19:02 + Generated by: Daniele Catanesi + Organization: http://blog.helocheck.com + -------------------------------------------------------------------------------- + .DESCRIPTION + Script generated by PowerShell Studio 2017 +#> + + + +#region Source: Startup.pss +#---------------------------------------------- +#region Import Assemblies +#---------------------------------------------- +[void][Reflection.Assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') +[void][Reflection.Assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') +[void][Reflection.Assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') +[void][Reflection.Assembly]::Load('System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') +[void][Reflection.Assembly]::Load('System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') +#endregion Import Assemblies + +#Define a Param block to use custom parameters in the project +#Param ($CustomParameter) + +function Main +{ +<# + .SYNOPSIS + The Main function starts the project application. + + .PARAMETER Commandline + $Commandline contains the complete argument string passed to the script packager executable. + + .NOTES + Use this function to initialize your script and to call GUI forms. + + .NOTES + To get the console output in the Packager (Forms Engine) use: + $ConsoleOutput (Type: System.Collections.ArrayList) +#> + Param ([String]$Commandline) + + #-------------------------------------------------------------------------- + #TODO: Add initialization script here (Load modules and check requirements) + + + #-------------------------------------------------------------------------- + + if ((Show-MainForm_psf) -eq 'OK') + { + + } + + $global:ExitCode = 0 #Set the exit code for the Packager +} + + + + + + + +#endregion Source: Startup.pss + +#region Source: MainForm.psf +function Show-MainForm_psf +{ + #---------------------------------------------- + #region Import the Assemblies + #---------------------------------------------- + [void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') + [void][reflection.assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') + [void][reflection.assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + [void][reflection.assembly]::Load('System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + [void][reflection.assembly]::Load('System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + #endregion Import Assemblies + + #---------------------------------------------- + #region Generated Form Objects + #---------------------------------------------- + [System.Windows.Forms.Application]::EnableVisualStyles() + $MainForm = New-Object 'System.Windows.Forms.Form' + $btn_About = New-Object 'System.Windows.Forms.Button' + $btn_Csv = New-Object 'System.Windows.Forms.Button' + $btn_Quit = New-Object 'System.Windows.Forms.Button' + $btn_Track = New-Object 'System.Windows.Forms.Button' + $grp_Recipient = New-Object 'System.Windows.Forms.GroupBox' + $txt_ResultsLimit = New-Object 'System.Windows.Forms.TextBox' + $lblResultLimit = New-Object 'System.Windows.Forms.Label' + $cmb_Sender = New-Object 'System.Windows.Forms.ComboBox' + $cmb_Recipient = New-Object 'System.Windows.Forms.ComboBox' + $chk_Detail = New-Object 'System.Windows.Forms.CheckBox' + $btn_Sender = New-Object 'System.Windows.Forms.Button' + $chk_endDate = New-Object 'System.Windows.Forms.CheckBox' + $chk_StartDate = New-Object 'System.Windows.Forms.CheckBox' + $btn_Recipient = New-Object 'System.Windows.Forms.Button' + $date_end = New-Object 'System.Windows.Forms.DateTimePicker' + $labelEnd = New-Object 'System.Windows.Forms.Label' + $lbl_Start = New-Object 'System.Windows.Forms.Label' + $date_start = New-Object 'System.Windows.Forms.DateTimePicker' + $txt_reference = New-Object 'System.Windows.Forms.TextBox' + $chk_reference = New-Object 'System.Windows.Forms.CheckBox' + $lbl_Reference = New-Object 'System.Windows.Forms.Label' + $txt_Subject = New-Object 'System.Windows.Forms.TextBox' + $chk_subject = New-Object 'System.Windows.Forms.CheckBox' + $lbl_subject = New-Object 'System.Windows.Forms.Label' + $txt_InternalId = New-Object 'System.Windows.Forms.TextBox' + $chk_InternalId = New-Object 'System.Windows.Forms.CheckBox' + $lbl_internalMessageId = New-Object 'System.Windows.Forms.Label' + $txt_MessageId = New-Object 'System.Windows.Forms.TextBox' + $chk_messageId = New-Object 'System.Windows.Forms.CheckBox' + $lbl_MessageId = New-Object 'System.Windows.Forms.Label' + $cmb_eventID = New-Object 'System.Windows.Forms.ComboBox' + $chk_EventId = New-Object 'System.Windows.Forms.CheckBox' + $lbl_eventId = New-Object 'System.Windows.Forms.Label' + $txt_Server = New-Object 'System.Windows.Forms.TextBox' + $chk_Server = New-Object 'System.Windows.Forms.CheckBox' + $lbl_server = New-Object 'System.Windows.Forms.Label' + $chk_Sender = New-Object 'System.Windows.Forms.CheckBox' + $lbl_Sender = New-Object 'System.Windows.Forms.Label' + $chk_recipient = New-Object 'System.Windows.Forms.CheckBox' + $lbl_recipient = New-Object 'System.Windows.Forms.Label' + $tooltip1 = New-Object 'System.Windows.Forms.ToolTip' + $InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState' + #endregion Generated Form Objects + + #---------------------------------------------- + # User Generated Script + #---------------------------------------------- + Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn + $MainForm_Load = { + + # We check if the correct PowerShell version + if ($PSVersionTable.PSVersion.Major -lt 3) + { + [System.Windows.Forms.MessageBox]::Show('PowerShell 3.0 or above is required!', 'Wrong PowerShell Version', 'Ok', 'Error') + $MainForm.Close() + } + + # Preload SMTP Events for tracking in Combox + Load-ComboBox -ComboBox $cmb_eventID -Items 'RECEIVE', 'SEND', 'FAIL', 'DSN', 'DELIVER', 'BADMAIL', 'RESOLVE',` + 'EXPAND', 'REDIRECT', 'TRANSFER', 'SUBMIT', 'POISONMESSAGE', 'DEFER' + + # Specify default maximum number of results + #$global:resultLimit = $txt_ResultsLimit.Text + } + + #region Get Tracking Logs Function + function Get-TrackInfo + { + param + ( + $Recipients, + $Sender, + $Server, + $Detailed, + $MessageId, + $InternalMessageId, + $MessageSubject, + $Reference, + $Start, + $End, + $EventId + ) + + if ($HubServer) + { + # Get tracking log for all servers - limit results to 300 for performance reasons + $global:trackingCommand = "Get-TransportService '$HubServer' | Get-MessageTrackingLog -WarningAction Continue -ResultSize $resultLimit" + + } + else + { + # If nothing is specified get all tracking logs - limit to 300 for perforamnce reasons + $global:trackingCommand = "Get-TransportService | Get-MessageTrackingLog -WarningAction Continue -ResultSize $resultLimit" + } + + if ($smtpEvent) + { + $global:trackingCommand += " -EventId '$smtpEvent'" + } + + if ($Recipient) + { + $global:trackingCommand += " -recipients '$Recipient'" + } + + if ($senders) + { + $global:trackingCommand += " -sender '$Senders'" + } + + if ($msgId) + { + $global:trackingCommand += " -messageId '$msgId'" + } + + if ($internalId) + { + $global:trackingCommand += " -InternalMessageId '$internalId'" + } + + if ($msgSubject) + { + $global:trackingCommand += " -MessageSubject '$msgSubject'" + } + + if ($msgReference) + { + $global:trackingCommand += " -Reference '$msgReference'" + } + + if ($startDate) + { + $global:trackingCommand += " -Start '$startDate'" + } + + if ($endDate) + { + $global:trackingCommand += " -End '$endDate'" + } + + if ($details) + { + $global:trackingCommand += "| select *" + } + } + #endregion + + #region Test IP Address function + function Test-IPAddress + { + [CmdletBinding()] + param + ( + [string]$IpAddress + ) + # Check if input field in the server textbox is an IP Address or Server Name + $ipReg = [regex]"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" + if ($ipReg.IsMatch($IpAddress) -eq $true) + { + $result = ([System.Net.Dns]::GetHostEntry($IpAddress)).HostName.ToString() + $txt_Server.Text = $result + } + } #endregion + + $btn_Quit_Click = { + # Close the form and quit + $MainForm.Close() + } + + #region Control Helper Functions + function Load-ComboBox + { + <# + .SYNOPSIS + This functions helps you load items into a ComboBox. + + .DESCRIPTION + Use this function to dynamically load items into the ComboBox control. + + .PARAMETER ComboBox + The ComboBox control you want to add items to. + + .PARAMETER Items + The object or objects you wish to load into the ComboBox's Items collection. + + .PARAMETER DisplayMember + Indicates the property to display for the items in this control. + + .PARAMETER Append + Adds the item(s) to the ComboBox without clearing the Items collection. + + .EXAMPLE + Load-ComboBox $combobox1 "Red", "White", "Blue" + + .EXAMPLE + Load-ComboBox $combobox1 "Red" -Append + Load-ComboBox $combobox1 "White" -Append + Load-ComboBox $combobox1 "Blue" -Append + + .EXAMPLE + Load-ComboBox $combobox1 (Get-Process) "ProcessName" + #> + Param ( + [ValidateNotNull()] + [Parameter(Mandatory = $true)] + [System.Windows.Forms.ComboBox]$ComboBox, + [ValidateNotNull()] + [Parameter(Mandatory = $true)] + $Items, + [Parameter(Mandatory = $false)] + [string]$DisplayMember, + [switch]$Append + ) + + if (-not $Append) + { + $ComboBox.Items.Clear() + } + + if ($Items -is [Object[]]) + { + $ComboBox.Items.AddRange($Items) + } + elseif ($Items -is [Array]) + { + $ComboBox.BeginUpdate() + foreach ($obj in $Items) + { + $ComboBox.Items.Add($obj) + } + $ComboBox.EndUpdate() + } + else + { + $ComboBox.Items.Add($Items) + } + + $ComboBox.DisplayMember = $DisplayMember + } + #endregion + + #region Checkboxes evaluation + $chk_recipient_CheckStateChanged = { + # Manage Texbox status and text + if ($chk_recipient.Checked -eq $true) + { + $cmb_Recipient.Enabled = $true + } + else + { + $cmb_Recipient.Enabled = $false + $cmb_Recipient.Text = $null + $btn_Recipient.Enabled = $false + } + } + + $chk_sender_CheckStateChanged = { + # Manage Texbox status and text + if ($chk_Sender.Checked -eq $true) + { + $cmb_Sender.Enabled = $true + } + else + { + $cmb_Sender.Enabled = $false + $cmb_Sender.Text = $null + } + } + + $chk_Server_checkStateChanged = { + # Manage Texbox status and text + if ($chk_Server.Checked -eq $true) + { + $txt_Server.Enabled = $true + } + else + { + $txt_Server.Enabled = $false + $txt_Server.Text = $null + } + } + + $chk_EventId_checkStateChanged = { + # Manage Texbox status and text + if ($chk_EventId.Checked -eq $true) + { + $cmb_eventID.Enabled = $true + } + else + { + $cmb_eventID.Enabled = $false + } + } + + $chk_messageId_checkStateChanged = { + # Manage Texbox status and text + if ($chk_messageId.Checked -eq $true) + { + $txt_MessageId.Enabled = $true + } + else + { + $txt_MessageId.Enabled = $false + $txt_MessageId.Text = $null + } + } + + $chk_InternalId_CheckStateChanged = { + #TODO: Place custom script here + if ($chk_InternalId.Checked -eq $true) + { + $txt_InternalId.Enabled = $true + } + else + { + $txt_InternalId.Enabled = $false + $txt_InternalId.Text = $null + } + } + + $chk_subject_CheckStateChanged = { + #TODO: Place custom script here + if ($chk_subject.Checked -eq $true) + { + $txt_Subject.Enabled = $true + } + else + { + $txt_Subject.Enabled = $false + $txt_Subject.Text = $null + } + } + + $chk_reference_CheckStateChanged = { + # Manage Texbox status and text + if ($chk_reference.Checked -eq $true) + { + $txt_reference.Enabled = $true + } + else + { + $txt_reference.Enabled = $false + $txt_reference.Text = $null + } + } + + $chk_StartDate_CheckStateChanged = { + # Manage Texbox status and text + if ($chk_StartDate.Checked -eq $true) + { + $date_start.Enabled = $true + } + else + { + $date_start.Enabled = $false + } + + } + + $chk_endDate_CheckStateChanged = { + # Manage Texbox status and text + if ($chk_endDate.Checked -eq $true) + { + $date_end.Enabled = $true + } + else + { + $date_end.Enabled = $false + } + } + #endregion + + $btn_Quit_Click = { + # Close the form and quit + $MainForm.Close() + } + + #region Recipients Checks + $btn_Recipient_Click = { + # Use ANR to resolve mailbox to an internal user + # Load the combox with ANR return data and select first element + Load-ComboBox -ComboBox $cmb_Recipient -Items ((Get-Mailbox -Anr $cmb_Recipient.Text).PrimarySmtpAddress) + $cmb_Recipient.SelectedIndex = 0 + + } + + $cmb_Recipient_Leave = { + # Validate Recipient textbox content + if ($cmb_Recipient.Text.Length -lt 3) + { + $cmb_Recipient.Focus() + $cmb_Recipient.Text = 'Enter at least 3 characters' + $btn_Recipient.Enabled = $false + Start-Sleep -Seconds 1 + $cmb_Recipient.Text = '' + } + } + + $cmb_Recipient_TextChanged = { + # Disable to button if string is less than 3 characters + if ($cmb_Recipient.Text.Length -lt 3) + { + $btn_Recipient.Enabled = $false + } + else + { + $btn_Recipient.Enabled = $true + } + } + #endregion + + #region Senders Checks + $btn_Sender_Click = { + + # Use ANR to resolve mailbox to an internal user + # Load the combox with ANR return data and select first element + Load-ComboBox -ComboBox $cmb_Sender -Items ((Get-Mailbox -Anr $cmb_Sender.Text).PrimarySmtpAddress) + $cmb_Sender.SelectedIndex = 0 + } + + $cmb_Sender_Leave = { + # Validate Sender textbox content + if ($cmb_Sender.Text.Length -lt 3) + { + $cmb_Sender.Focus() + $cmb_Sender.Text = 'Enter at least 3 characters' + $btn_Sender.Enabled = $false + Start-Sleep -Seconds 1 + $cmb_Sender.Text = '' + } + } + + $cmb_Sender_TextChanged = { + # Disable to button if string is less than 3 characters + if ($cmb_Sender.Text.Length -lt 3) + { + $btn_Sender.Enabled = $false + } + else + { + $btn_Sender.Enabled = $true + } + } + #endregion + + $btn_Track_Click = { + + # Check the value of ResultSize textbox + if ($txt_ResultsLimit.Text -eq 0) + { + $global:resultLimit = "Unlimited" + } + else + { + $global:resultLimit = $txt_ResultsLimit.Text + } + + # Variable Definition + $Recipient = $cmb_Recipient.Text + $senders = $cmb_Sender.Text + $HubServer = $txt_Server.Text + $details = $chk_Detail.Checked + $msgId = $txt_MessageId.Text + $internalId = $txt_InternalId.Text + $msgSubject = $txt_Subject.Text + $msgReference = $txt_reference.Text + $smtpEvent = $cmb_eventID.Text + + #region Tracking Date + # If no date has been specified just grab all tracking logs + if ($date_start.Enabled -eq $true) + { + $startDate = $date_start.Text + } + + if ($date_end.Enabled -eq $true) + { + + $endDate = $date_end.Text + } + #endregion Tracking dates + + Get-TrackInfo + Invoke-Expression $trackingCommand | + Select-Object Timestamp, EventId, Soruce, Sender, Recipients, MessageSubject, RecipientStatus | + Out-GridView -PassThru + } + $btn_Csv_Click = { + + # Check the value of ResultSize textbox + if ($txt_ResultsLimit.Text -eq 0) + { + $global:resultLimit = "Unlimited" + } + else + { + $global:resultLimit = $txt_ResultsLimit.Text + } + + # Variables Definition + $Recipient = $cmb_Recipient.Text + $senders = $cmb_Sender.Text + $HubServer = $txt_Server.Text + $details = $chk_Detail.Checked + $msgId = $txt_MessageId.Text + $internalId = $txt_InternalId.Text + $msgSubject = $txt_Subject.Text + $msgReference = $txt_reference.Text + $smtpEvent = $cmb_eventID.Text + $exportDate = (Get-Date -Format G) -replace ":", "." + $exportPath = "$PSScriptRoot\$exportDate" + "_MessageTracking.csv" + + #region Tracking Date + # If no date has been specified just grab all tracking logs + if ($date_start.Enabled -eq $true) + { + $startDate = $date_start.Text + } + + if ($date_end.Enabled -eq $true) + { + + $endDate = $date_end.Text + } + #endregion Tracking dates + + Get-TrackInfo + <#Invoke-Expression $trackingCommand | Select-Object Timestamp, ServerHostname, ClientHostname, Source, EventId, Sender, ` + @{ l = "Recipients"; e = { $_.Recipients -join " " } }, MessageSubject | ` + Sort-Object -Property Timestamp | Export-Csv $exportPath -NoTypeInformation -UseCulture #> + + Invoke-Expression -Command $trackingCommand | + Select-Object -Property Timestamp, ServerHostname, ClientHostname, Source, EventId, MessageId, Sender, ` + @{ + l = 'Recipients' + e = { + $_.Recipients -join ' ' + } + }, MessageSubject, + @{ + l = 'RecipientStatus'; + e = { $_.RecipientStatus } + } | + ` + Sort-Object -Property Timestamp | + Export-Csv -Path $exportPath -NoTypeInformation -UseCulture + } + + $btn_About_Click = { + Show-About_psf + } + $txt_Server_Leave = { + # Check if user inserted an IP in place of the server's name + Test-IPAddress -IpAddress $txt_Server.Text + } # --End User Generated Script-- + #---------------------------------------------- + #region Generated Events + #---------------------------------------------- + + $Form_StateCorrection_Load = + { + #Correct the initial state of the form to prevent the .Net maximized form issue + $MainForm.WindowState = $InitialFormWindowState + } + + $Form_StoreValues_Closing = + { + #Store the control values + $script:MainForm_txt_ResultsLimit = $txt_ResultsLimit.Text + $script:MainForm_cmb_Sender = $cmb_Sender.Text + $script:MainForm_cmb_Sender_SelectedItem = $cmb_Sender.SelectedItem + $script:MainForm_cmb_Recipient = $cmb_Recipient.Text + $script:MainForm_cmb_Recipient_SelectedItem = $cmb_Recipient.SelectedItem + $script:MainForm_chk_Detail = $chk_Detail.Checked + $script:MainForm_chk_endDate = $chk_endDate.Checked + $script:MainForm_chk_StartDate = $chk_StartDate.Checked + $script:MainForm_date_end = $date_end.Value + $script:MainForm_date_start = $date_start.Value + $script:MainForm_txt_reference = $txt_reference.Text + $script:MainForm_chk_reference = $chk_reference.Checked + $script:MainForm_txt_Subject = $txt_Subject.Text + $script:MainForm_chk_subject = $chk_subject.Checked + $script:MainForm_txt_InternalId = $txt_InternalId.Text + $script:MainForm_chk_InternalId = $chk_InternalId.Checked + $script:MainForm_txt_MessageId = $txt_MessageId.Text + $script:MainForm_chk_messageId = $chk_messageId.Checked + $script:MainForm_cmb_eventID = $cmb_eventID.Text + $script:MainForm_cmb_eventID_SelectedItem = $cmb_eventID.SelectedItem + $script:MainForm_chk_EventId = $chk_EventId.Checked + $script:MainForm_txt_Server = $txt_Server.Text + $script:MainForm_chk_Server = $chk_Server.Checked + $script:MainForm_chk_Sender = $chk_Sender.Checked + $script:MainForm_chk_recipient = $chk_recipient.Checked + } + + + $Form_Cleanup_FormClosed = + { + #Remove all event handlers from the controls + try + { + $btn_About.remove_Click($btn_About_Click) + $btn_Csv.remove_Click($btn_Csv_Click) + $btn_Quit.remove_Click($btn_Quit_Click) + $btn_Track.remove_Click($btn_Track_Click) + $cmb_Sender.remove_TextChanged($cmb_Sender_TextChanged) + $cmb_Sender.remove_Leave($cmb_Sender_Leave) + $cmb_Recipient.remove_TextChanged($cmb_Recipient_TextChanged) + $cmb_Recipient.remove_Leave($cmb_Recipient_Leave) + $btn_Sender.remove_Click($btn_Sender_Click) + $chk_endDate.remove_CheckStateChanged($chk_endDate_CheckStateChanged) + $chk_StartDate.remove_CheckStateChanged($chk_StartDate_CheckStateChanged) + $btn_Recipient.remove_Click($btn_Recipient_Click) + $chk_reference.remove_CheckStateChanged($chk_reference_CheckStateChanged) + $chk_subject.remove_CheckStateChanged($chk_subject_CheckStateChanged) + $chk_InternalId.remove_CheckStateChanged($chk_InternalId_CheckStateChanged) + $chk_messageId.remove_CheckStateChanged($chk_messageId_CheckStateChanged) + $chk_EventId.remove_CheckStateChanged($chk_EventId_CheckStateChanged) + $txt_Server.remove_Leave($txt_Server_Leave) + $chk_Server.remove_CheckStateChanged($chk_server_CheckStateChanged) + $chk_Sender.remove_CheckStateChanged($chk_sender_CheckStateChanged) + $chk_recipient.remove_CheckStateChanged($chk_recipient_CheckStateChanged) + $MainForm.remove_Load($MainForm_Load) + $MainForm.remove_Load($Form_StateCorrection_Load) + $MainForm.remove_Closing($Form_StoreValues_Closing) + $MainForm.remove_FormClosed($Form_Cleanup_FormClosed) + } + catch { Out-Null <# Prevent PSScriptAnalyzer warning #> } + } + #endregion Generated Events + + #---------------------------------------------- + #region Generated Form Code + #---------------------------------------------- + $MainForm.SuspendLayout() + $grp_Recipient.SuspendLayout() + # + # MainForm + # + $MainForm.Controls.Add($btn_About) + $MainForm.Controls.Add($btn_Csv) + $MainForm.Controls.Add($btn_Quit) + $MainForm.Controls.Add($btn_Track) + $MainForm.Controls.Add($grp_Recipient) + $MainForm.AutoScaleDimensions = '8, 17' + $MainForm.AutoScaleMode = 'Font' + $MainForm.AutoSize = $True + $MainForm.ClientSize = '761, 490' + $MainForm.FormBorderStyle = 'FixedSingle' + #region Binary Data + $MainForm.Icon = [System.Convert]::FromBase64String(' +AAABAAEAgIAAAAEAIAAoCAEAFgAAACgAAACAAAAAAAEAAAEAIAAAAAAAAAABABILAAASCwAAAAAA +AAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD+/v4A/v39AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+ +/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79 +AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A +/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+ +/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+ +/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79 +AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A +/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+ +/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/f0A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP7+/gD///8A59/WANzSxQDf1skA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA +3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe +1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7V +yADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXI +AN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA +3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe +1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7V +yADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXI +AN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADf1skA3NPGAPf18QD///8A +/v7+AP///wDKu6cAtJ+CALmmigC4pYkAuKWJALiliQC4pYkAuKWJALiliQC4pYkAuKWJALiliQG4 +pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbil +iQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJ +AbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkB +uKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4 +pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbil +iQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJ +AbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkB +uKWJAbiliQG4pYkAuKWJALiliQC4pYkAuKWJALiliQC4pYkAuKWJALmmigC1oIMA7ejhAP///wD+ +/v4A////AM/BrgC6qI0Av62UAL6skwC+rJMAvqyTAL6skwG+rJMCvqyTBL6skwS+rJMFvqyTBr6s +kwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyT +Br6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMG +vqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+ +rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6s +kwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyT +Br6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMG +vqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+ +rJMGvqyTBr6skwW+rJMEvqyTAr6skwK+rJMBvqyTAL6skwC+rJMAv62UALupjgDu6uMA////AP7+ +/gD///8AzsCtALmmiwC+rJIAvauRAL2rkQG9q5EDvauRBb2rkQi9q5ELvauRDr2rkRC9q5ERvauR +Eb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ER +vauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9 +q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2r +kRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauR +Eb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ER +vauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9 +q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2r +kRG9q5EQvauRD72rkQy9q5EJvauRBr2rkQO9q5EBvauRAL2rkQC+rJIAuqeMAO7q4wD///8A/v7+ +AP///wDOwK0AuaaLAL6skgC9q5EBvauRBL2rkQi9q5ENvauRE72rkRi9q5EdvauRH72rkSG9q5Eh +vauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9 +q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2r +kSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauR +Ir2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5Ei +vauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9 +q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2r +kSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EhvauR +Ib2rkSC9q5EevauRGr2rkRS9q5EPvauRCb2rkQS9q5ECvauRAL6skgC6p4wA7urjAP///wD+/v4A +////AM7ArQC5posAvqySAb2rkQS9q5EIvauRD72rkRi9q5EivauRK72rkTK9q5E1vauROL2rkTm9 +q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2r +kTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauR +Ob2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5 +vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9 +q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2r +kTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauR +Ob2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E4 +vauRNr2rkTO9q5EsvauRJL2rkRu9q5ESvauRCr2rkQW9q5EBvqySALqnjADu6uMA////AP7+/gD/ +//8AzsCtALmmiwC+rJICvauRB72rkQ+9q5EavauRJ72rkTe9q5FBvauRR72rkUu9q5FNvauRTr2r +kU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauR +Tr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FO +vauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69 +q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2r +kU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauR +Tr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FO +vauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU29 +q5FLvauRR72rkUK9q5E5vauRK72rkR29q5ERvauRCL2rkQO+rJIAuqeMAO7q4wD///8A/v7+AP// +/wDOwK0AuaaLAL6skgS9q5ELvauRFr2rkSa9q5E5vauRPr2rkU+9q5FlvauRa72rkW29q5FuvauR +br2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5Fu +vauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69 +q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2r +kW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauR +br2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5Fu +vauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69 +q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbb2r +kWy9q5FovauRV72rkUO9q5E7vauRKr2rkRq9q5ENvauRBb6skgG6p4wA7urjAP///wD+/v4A//// +AM7ArQC5posBvqySBr2rkQ+9q5EdvauRMb2rkTy9q5CFvauQ072rkPC8qo/1vKqP9ryqkPa8q5D2 +vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8 +q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryr +kPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ +9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2 +vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8 +q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryr +kPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryqkPa8qo/2vKqP +9ryqkPS8q5LivKuSpb2rkUa9q5E0vauRI72rkRK9q5EIvqySArqnjADu6uMA////AP7+/gD///8A +zsCtALmmiwG+rJIIvauRE72rkSa9q5EwvauQm72rkP+6qI3/uqiM/7qrk/+9q5D/u6mN/7qojP+6 +qI3/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qo +jP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM +/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/ +uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6 +qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qo +jP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM +/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojf+6qIz/u6mN/72rkP+6q5T/ +u6uT/7moj/+8q5H/vauQw72rkTy9q5EovauRF72rkQq+rJICuqeMAO7q4wD///8A/v7+AP///wDO +wK0AuaaLAr6skgm9q5EYvauRIb2rkWS+rJH/u6iM/si5o/va0cP6vbKh/sG1o/3d1Mf54NjL+d3V +x/ne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ ++d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn5 +3tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne +1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7W +yfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ ++d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn5 +3tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53dXH+eDYy/nd1Mb5wbWk/buvnf29 +sJ/+xrmm/L2rj/u9q5D/vauRkr2rkSK9q5EcvauRDL6skgO6p4wA7urjAP///wD+/v4A////AM7A +rQC5posCvqySCr2rkRq9q5EfvauSrrypjv/FtZ366ubd+Ovm3/jAtaX+u7Cf/si/sfzo49v48Ozl +9+zo4fjt6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL3 +7eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft +6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p +4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni +9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL3 +7eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft +6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+zo4fjw7OX36OPb+Mi/sfy8saD+vLCf/tbO +w/vu6uP3zsGu+rqojP2+rJHcvauRL72rkRy9q5EOvqySBLqnjADu6uMA////AP7+/gD///8AzsCt +ALmmiwK+rJILvauRGr2rkSS+rZLLuqiL/9TJt/nu6+T36+be99zVyfm/tKT/vLCf/8O5qf3k39X4 +7uri9evm3vfs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs +59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn +3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off +9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/3 +7Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs +59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn +3/fs59/37Off9+zn3/fs59/37Off9+zn3/fr5t737uri9eTf1fjDuan9vLCe/7+0pP/c1cr57uri +9+7q4vff18r5u6mN/72skvK9q5E+vauRGb2rkQ6+rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0A +uaaLAr6skgu9q5EavauRJL+tk827qIz/1cq5+e/s5vbt6eH56OTX5dXQveDAtqT5vLCe/MG2pPnY +0cHm49/O2eDcy9zg3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+Hc +zNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM +2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb +4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh +3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+Hc +zNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM +2+HczNvh3Mzb4dzM2+HczNvg3Mzb4NzL3OPfztnY0cHmwbak+bywnvzAtqT51tC+4eXh0t/s5+D3 +7+vl9+DYzPi8qY7/vq2S872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5 +posCvqySC72rkRq9q5Ekv62TzbunjP/Wy7r58u/p9u/s5vnk4dPh4N3K0NrVxNvDuaf0vrGf+7+0 +offTzbvg4d7N0d/bydTf28rU39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK +09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT +39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf +28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/b +ytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK +09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT +39vK09/bytPf28rT39vK1N/bydTh3s3R08274L+0ofe+sZ/7w7mn9NrVxNrg3cvQ4d3N2u7r5Pfx +7un34drO+Lypjv++rZPzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmm +iwK+rJILvauRGr2rkSS/rpPNuqiM/9fMvPn08u328e/p+ejl2ODf3MvS4+HQ0d/bytXHvqzwv7Sh ++sC1ovjRyrnl5OHR0eLeztPh3s7T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T +4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi +387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+Lf +ztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O +0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T +4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi +387T4t/O0+HeztPi3s7T5OHR0dHKueXAtaL4v7Sh+se+rPDf28rV4+HQ0eDdzNLk4dLZ8e7o9vPx +7Pfj3ND4vKqO/76tk/O9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaL +Ar6skgu9q5EavauRJL+ulM27qI3/2M6++fb18fX08u346ujb3+Lgz8/j4dHS5ePTz+Pg0NHMw7Lr +wbWj+MG2pPjQybjm5OPTz+Ti0tDj4dDR5OLR0OPi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj +4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi +0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR +0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR +4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj +4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0eTi +0dDj4dDR5OLS0OTj08/Qybjmwbak+MG1o/jMw7Lr4+DQ0eXj08/j4dHS4uDQ0Obk1dfz8ev29vTw +9uXe0/i9qo//v66U872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posC +vqySC72rkRq9q5Ekv66Vzbupjf/a0MD4+fj09ff08Pjs6t7e5OPSzebl1dDl5NTQ5uXVz+fl1c7R +yrnmw7il9sK4pffPx7Xp5uTUz+fm1s7l5NTQ5uXVz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl +1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXU +z+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP +5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m +5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl +1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dXP5eTU +0Ofm1s7m5NTPz8e16cK4pffDuKX20cq55ufl1c7m5dXP5eTU0Obl1c/l49PO6OfY1vb07vb49/P2 +5+DV972rkP+/rpXzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+ +rJILvauRGr2rkSS/rpXNu6mN/9rQwfj6+vb1+Pby+e3s4d7l5dTL5+bXz+fm187n5tbO5+fXzuno +2czW0L/hxbqo9MS5p/XNxbTq5OPT0Ono2czn5dbO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bX +zufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO +5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n +5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm +187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bX +zufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufl1s7p6NnM +5OPT0M3FtOrEuaf1xbqo9NbQv+Hp6NnM5+fXzufm1s7n5tfO5+bXzubl1c3p6NvV9/bx9vn59fbo +4tf3vauQ/7+ulfO9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6s +kgu9q5EavauRJMCvls28qo7/29LC+Pz7+PX5+PT47u7j3efm1sro6NnO6OjYzejo2c3o59jN6OfY +zerr3Mrb18Xcx72q8cW7qPTMw7Ds4+LS0erq28vo59jO6OjZzejo2M3o6NjN6OjYzejo2M3o6NjN +6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o +6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo +2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjY +zejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN +6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2c3o59jO6urby+Pi0tHM +w7Dsxbuo9Me9qvHb18Xc6uvcyujn2M3o59jN6OjZzejo2M3o6NjN5+fXzOrq3dT4+PP1+/v39unj +2fe+rJH/wK+W872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqyS +C72rkRq9q5EkwK+Wzbyqj//c0sP4/Pz59fr59vjv7+Xc5+fYyujp283o6NvM6OjbzOjp28zo6NrM +6Ojazevs3srf3MzXysGu7se9qvLMw7Dt4d/P1evr3sro6NrN6OjazOjo28zo6NvM6OjbzOjo28zo +6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo +28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6Ojb +zOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM +6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo +6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NrM6Ojazevr3srh38/VzMOw7ce9 +qvLKwa7u39zM1+vs3sro6NrN6OjazOjp28zo6NvM6OjbzOjp28zn59nL6+vf1Pr59fX8/Pn16ePZ +976skv/Ar5fzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJIL +vauRGr2rkSTBsJbNvauP/9zTxPj9/fv0+/r3+PDw5tzo6NnJ6urbzerq28zq6tvM6urbzOrq28zq +6tvM6enazezt3snj4dLTzsWy68m/rPHLwq/u4N3N1+zt3snq6dvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM +6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq +6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrp28zs7d7J4N3N18vCr+7Jv6zxzsWy +6+Ph0tPs7d7J6enazerq28zq6tvM6urbzOrq28zq6tvM6urbzOnp2svs7N/T+vn29fz9+vXp5dr3 +v62S/8Gwl/O9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9 +q5EavauRJMGwl829q5D/3NPE+P39/PT7+vj48PDn3Onp2snq6tvN6urbzOrq28zq6tvM6urbzOrq +28zq6tzM6unbzOzs3srm5dbQ0cm36MvBru/Mw7Du3dnJ2uzs3srq6tvM6urbzOrq3Mzq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM +6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq +6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urczOrq28zq6tvM7Ozeyt3ZydrMw7Duy8Gu79HJt+jm5dbQ +7Ozeyurp28zq6tzM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6enby+3t4NP6+ff1/f379erl3Pe/ +rZP/wbCX872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72r +kRq9q5EkwbGYzb2skf/c1MX4/f789Pv6+Pjw8Ofc6enayerq3M3q6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6unbzOzs3srp6NrO1c685MzDsO3Nw7Ht29bG3Ovs3srq6t3M6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urdzOvs3srb1sbczcOx7czDsO3Vzrzk6ejazuzs3srq +6dvM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzp6dvL7e3h0/r59/X9/fv16uXc97+u +lP/BsZjzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauR +Gr2rkSTCsZjNvqyS/93Uxfj9/vz0+/r4+PDw59zp6drJ6urczerq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6unbzOvr3cvq6tzM2NLC4M7FsuzOxbLs2dPC3+rq3Mzr693L6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOvr3cvq6tzM2dPC387FsuzOxbLs2NLB4Orq3Mzr693L6unbzOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOnp28vt7eHT+vr39f39+/Xq5dz3wK6U +/8KxmPO9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5Ea +vauRJMKxmM2+rZP/3dTG+P3+/PT7+vj48fHo3Orq28nr693N6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+rdzOvr3cvs7N/L3dfH3NHIterQxrTr2NLA4erq3M3s7N7L6urczOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vd +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOrq3Mzs7N7L6urczdjSwOHQxrTr0ci16t3Xx9zs7N/L6+vdy+vq3czr693M6+vd +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6urcy+3t4dP6+vf1/f379erm3PfAr5X/ +wrKZ872rkj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9 +q5EkwrKYzb+tk//d1cb4/f789Pv6+Pjx8ejc6urbyevr3c3r693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czt7d/K4NzM2NPKuOjSybbp2NC/4ujn2c/s7d/K6+vc +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vd +zOvr3czr69zM7O3fyujn2c/Y0L/i0sm26dPKuOjg3MzY7e3fyuvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czq6tzL7e3h0/r69/X9/fv16+bc98Gwlf/D +s5nzvauSP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2r +kSTCspnNv66U/93Vxvj9/vz0+/r4+PHx6Nzq6tzJ6+vezevr3szr697M6+vezOvr3szr697M6+ve +zOvr3szr697M6+vezOvr3szr697M6+vdzOvr3czt7eDK4+DR1dXNu+XUy7jn19C+4+fm19Ds7eDK +6+vdzOvr3czr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr +697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOrq +3czq6t3M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+ve +zOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr693M +6+vdzOzt4Mrn5tfQ19C+49TLuOfVzbvl4+DR1e3t4Mrr693M6+vdzOvr3szr697M6+vezOvr3szr +697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOrq3cvt7eLT+vr39f39+/Xr5tz3wbCW/8Oz +mvO9rJI/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavauR +JMOzms3Ar5X/3tXH+P3+/PT7+vj48fHo3Orq3Mnr697N6+vezOvr3szr697M6+vezOvr3szr697M +6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szt7uHK5uTV0tjQvuPVzLrm18+94+Xj1NPt +7eDK6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr +3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szq6t3M7+/h +y+/v4cvq6t3M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M +6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szt +7eDK5ePU09fPvePVzLrm2NC+4+bk1dLt7uHK6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr +3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6urdy+3t4tP6+vf1/f379evm3ffCsZf/xLSb +872skj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9q5Ek +w7ObzcCvlv/e1cf4/f789Pv6+Pjy8ujc6+vcyezs3s3s7N7M7OzezOzs3szs7N7M7OzezOzs3szs +7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szt7uDK6ejZ0NvUwuDXz7zk2NG/4+Xi +09Tt7uDK7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7Oze +zOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M6uvdzPLy5cvR0sbR +0dLG0fLy5cvq6t3M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs +7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7e7gyuXi +09TY0b/j18+85NvUwuDp6NnQ7e7gyuzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7Oze +zOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szr693L7u7i0/r69/X9/fv16+bd98KxmP/EtJzz +vaySP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2rkSTE +tJvNwbGW/97WyPj9/fz0+/r4+PLy6dzr693J7Ozfzezs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs +38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zt7uHL6urczt3Yxt3Y0b/j2dLA +4uPf0Nbt7eHL7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M +7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOrq3s3z8+TK1dTFz5uajdqb +mo3a1dTFz/Tz5Mnr697M6+zfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs +38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzO3t4cvj39DW2dLA +4tjRv+Pd2Mbd6urczu3u4cvs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M +7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOvr3svu7uPT+vr39f39+/Xr5t33w7KY/8W1nPO+ +rJI/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavauQJMS0 +m83Cspf/3tbI+P39+/T7+vj48vLp3Ovr3cns7N/N7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7Ozf +zOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zt7eDL7OvfzODby9ra08Hh +2tPB4eLeztjs7ODM7OzgzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs +7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOvr38zr69/M9fPkycrMx9aNkpTknKGm4pyh +puKOkpTjx8rH2PPy48rt7N/L6+vfzezs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7Ozf +zOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs4Mzs7ODM4t7O2NrTweHa08Hh +4NvL2uzr38zt7eDL7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs +7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M6+vey+7u49P6+vf1/f379evn3ffDs5n/xbad876s +kj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9q5AkxbSc +zcOymP/f1sj4/f379Pv6+Pjy8unc7Ozeye3t4M3t7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM +7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzOzs4Mzt7eHM7e3hy+Pfz9fc +1cTf3NXE4OHdzdns7ODM7e3hy+zs4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7OzgzO3t4Mzr7ODN7e3gy/Ly5Mq0vsrlbYCp/niOwP+fu/r/n7v6 +/3iPwP9rf6n/p7XJ7ezt5M/v7+DK6+vgze3t4Mzt7ODM7OzgzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM +7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzOzs4Mzt7eHL7OzgzOHdzdnc1cTg3NXE3+Pfz9ft +7eHL7e3hzOzs4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzs7N/L7+/k0/r69/X9/fv16+fe98Szmv/Gtp7zvqyS +P72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2qkCTFtZ3N +w7OZ/9/Xyfj9/fv0+/r4+PLy6dzs7N7J7e3gze3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt +7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7u7iy+bj +09Xe2Mfe3tfG3+Ldzdns697N7u7hy+3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3g +zO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7OzgzfDv4Mru7uPOpLPK8Gp/rP99k8P/or/4/6bF//+mxf// +or/4/36Uw/9qf6z/mKrK9+Pn4tXy8eDJ7Ozgze3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt +7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7u7hy+zr3s3i3c3Z3tfG397Yx97m49PV7u7iy+3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3g +zO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t38vv7+TT+vr39f39+/Xr5973xLSb/8a3n/O+rJI/ +vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavaqQJMW2ns3E +tJr/39fJ+P39+/T7+vj48vLq3Ozs38nt7eHN7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t +4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7u7j +y+fl19Lg2src39nI3eHczdrr6t7O7u7iy+3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzOzs4c3y8OHJ5+rk0pmry/Znfq3/e5TE/py9+f+fvv//nLr+/5y6/v+f +vv//m7z5/3qTxP5nfq3/i6LL/Nff49vx8ODI7e3hze3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t +4czt7eHM7e3hzO3t4czt7eHM7e3hzO7u4svr6t7O4dzN2t/ZyN3g2src5+XX0u7u48vt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3gy+/v5dP6+vf1/f379ezn3vfFtJz/x7ig876skj+9 +q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9qpAkxbaezcS0 +nP/f18r4/f379Pv6+Pjy8urc7Ozfye3t4c3t7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7u/jy+ro29Di3c3a4dvL2+Ldztrr6dzP7u7iy+3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt +7eHM7e3hzO3t4czt7eHN7+7gydrh49eNo8v6aoCt/4edxv6yzPv/utH//7zO/v/C0v//xNP//8PS +/v/C1///vNL7/5Okxv53ia3/kKXL/9Td5eHu7eDI7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czu7uLL6+nc0OLdztrh28vb4t3N2uro29Du7+PL7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt +7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eDL7+/l0/r69/X9/fv17Ofe98W1nf/HuKHzvqySP72r +kRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2qkCTGtp7NxbWc +/+DYyvj9/fv0+/r4+PPz69zt7eDJ7u7ize7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM +7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu +7uLM7+/jy+zr3s7k39DY493N2uPeztnr6dzQ7+/jy+7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7e7ize7u4Mjr7urbqrfM/ZKbrP+8wMj+9ff8//////////////////////////////// +///////////////8/8rKyP+oqaz/ys7W//f49ebr6+DJ6+zgzO7u4szu7uLM7u7izO7u4szu7uLM +7u7izO7u4szu7uLM7+/jy+vp3NDj3s7Z493N2uTf0Njs697O7+/jy+7u4szu7uLM7u7izO7u4szu +7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7u7izO7u4szu7uLM7u7izO7u4cvw8ObT+vr39fz9+/Xs5973xrae/8i5ofO+rJI/vauR +Gr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavaqQJMa2n83Ftp3/ +4NjK+P39+/T7+vj48/Pr3O3t4Mnu7uLN7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu +7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7+/jy+3s4M3m4tPW5N/P2eTf0Njp6NrS7+/jy+7u4szu7uLM7u7izO7u4szu7uLM7u7i +zO3t4s3q6+DI9vfx39fY1/+6ta7/4NnM/////v///////fz+//H0/f/n7fz/4Ob7/9zk+//c5Pv/ +4un8/+jt/P/x9P3/+/v8/9LPyv+6tKz+5ODa////+Or49uXK7e3izO3t4s3u7uLM7u7izO7u4szu +7uLM7u7izO/v48vp6NrS5N/Q2OTfz9nm4tPW7ezgze/v48vu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7i +zO7u4szu7uLM7u7izO7u4szu7uLM7u7hy/Dw5tP6+vf1/P379ezn3/fGtp//yLqi876skj+9q5Ea +vauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkx7egzca2nv/g +2Mv4/f389Pv6+fjz8+zc7e3hye7u483u7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u +48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7j +zO7u48zu7uPM7u/ky+7u4szo5NbU5eDS1+Xg0tfp59rS7+/kzO7u48zu7uPM7u7jzO7u48zq6+LN +8PDiyf//9OPe29f/p6ap/5urx/+Yt/b/bZHy/0h47/80ae7/H1zt/xZX7f8TVO3/EVPt/xFS7f8U +Ve3/F1bu/x5Z7f8zae//Q4Hx/0Zzwf9Wb6L+lqnQ/87X5u3w7+LL8vHly+zs4s3u7uPM7u7jzO7u +48zv7+TM6efa0eXg0tfl4NLX6OTW1O3u4szu7+TL7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7j +zO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM +7u7jzO7u48zu7uPM7u7jzO7u48zu7uLL8PDn0/r6+PX8/Pz17Ojg98e3oP/JuqPzvqySP72rkRq9 +q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTHuKHNxref/+DZ +zPj9/fz0+/r5+PPz7Nzu7uHJ7+/jze/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/j +zO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM +7+/jzO/v48zv7+PM7+/kzO/v48zq59rS5+PU1ufj1Nbq6NrS7+/jzO/v5Mzu7uPM7u7jzP765snt +7+/mmKrN/z9hn/4fXMD/DF/u/wBG7f8AQ+7/AEjw/wJM8f8HUPP/CVLz/wpU9P8LVPT/C1T1/wpT +9f8JUfT/B0/0/wJJ8v8ASvP/BFzz/wBLwv8AOKD+BUCr/0t0tvDq6+DM9fTmy+zs4s3v7+TM7+/j +zOro2tLn49TW5+PU1urn2tLv7+PM7+/kzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM +7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv +7+PM7+/jzO/v48zv7+PM7+/jzO/v4svx8efT+vr49fz8+/Xs6OD3x7mh/8m7pPO+rJI/vauRGr2r +kQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMe5oc3Ht6D/4dnM ++P39/PT7+vn48/Ps3O7u48nv7+TN7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM +7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5czr6t3Q6OXX1Ojk1tXr6NvS7e3jzfDx5cz29OTKhaDK6SJW +t/8AN53+Ak7D/wph8v8MUfL/DlLy/w9W9P8PV/T/D1j1/w5Y9v8OWPf/Dln4/w5Z+P8OWfn/Dln6 +/w5Y+v8OV/n/Dlb5/w5U+P8PVPj/EWj6/w9Zyv8MRqj+ADyw/zpptfPl59/O9/Xnyuvs4s3r6NvS +6OTW1ejl19Tr6t3Q7+/lzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/jy/Hx59P6+vj1/Pz79ezo4PfIuqL/yruk876skj+9q5EavauR +D76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkyLmizci4of/h2c34 +/f389Pv6+fjz8+zc7u7jye/v5M3v7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/w5czs69/P6ufZ0+fk2NTs6dzS9vPjzF+Fv+sAOKr/BT+i +/hBZyP8RZvT/DFLy/wtR8f8MVPL/DFXz/wxX9P8NWPT/DVn1/w1a9v8NW/j/DVv5/w1b+v8NW/v/ +DVv8/w1a/P8NWPz/DVj8/wxU+v8MVPr/D2f8/w1ZzP8OSan+AD+y/zFjtPXe4dzQ9O/e0OXj19Tq +59nT7Ovfz+/w5czv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/k +zO/v5Mzv7+TM7+/kzO/v5Mzv7+PL8fHn0/r6+PX8/Pv17ejg98m6o//Ku6XzvqySP72rkRq9q5EP +vqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTIuaPNyLqh/+Lazfj9 +/vz0/Pv5+PT07dzv7+TJ8PDlzfDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw +5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5szs6+HO7uvd0fDs29FVfLrvADyt/w5Ipv4OWsn/ +DmP0/wtQ8P8LUe//DFTv/wxV7/8MVu//DFfv/wxZ8P8MWvH/DFrz/w1b9P8NXPb/DV34/w1d+v8N +Xfv/DVz8/w1b/f8NWvz/DVn8/wxV+f8MU/j/Dmb6/wxZy/8OSKb+AECv/ylbrvjV2NTW9vHg0Orq +4c/w8ObM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5Mvy8ujT+/v49f39+/Xt6eH3ybuj/8u8pfO+rJI/vauRGr2rkQ++ +rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMi5o83JuqP/4tvO+P3+ +/PT8+/n49PTt3O/v5Mnw8OXN8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM7e7kzfX06Mvx7+HNTXa58AA9rf8OSKb+DlrL/w5j8/8M +UO3/DFHr/wxT6v8NU+j/DVXn/w1W5/8NVuf/DVfo/w1Y6f8OWuv/Dlvt/w5b8P8OXPP/Dl32/w5d ++P8OXfn/Dl36/w5b+v8OWfn/DVj4/wxU9P8MUfL/D2Pz/w1YyP8ORqH+AT6p/yJUqPnT2djT/Prq +yezs483w8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw +8OXM8PDlzPDw5czw8OXM8PDky/Ly6NP7+/j1/f379e3p4ffJu6X/y7yn876skz+9q5EavauRD76s +kgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkybqjzcm7pP/i2874/f78 +9Pz7+fj09O7c7+/lyfDw5s3w8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM +8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw +8ObM8PDmzPDw5szw8ObM8PDmzO3t5c319OjL7u7jzEdzuvAAPaz/D0qm/g5czf8PY/L/DU/p/wxR +5v8NUuP/DVLg/w1S3v8NU93/DVTc/w1U3P8OVt3/Dlff/w5Y4f8OWeT/Dlvo/w9c7P8PXfD/D17z +/w5e9f8OXvf/Dlz3/w5b9f8OWPP/DVbw/w1T6/8NT+f/D1/o/w1WwP8ORJr+Ajyf/x5Qo/nP1tjT +/frryevs5M3w8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw +8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw +5szw8ObM8PDmzPDw5szw8OXL8vLp0/v7+PX9/fv17unh98q8pf/LvafzvqyTP72rkRq9q5EPvqyS +BLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTKu6TNyryl/+Pbzvj9/vz0 +/Pv5+PX17tzw8OXJ8fHmzfHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx +8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx +5szx8ebM8PDmzPHx5szt7uXN9vXoy+3t481CcbvyAECs/w9Lpv4OX8//EGPw/w1P5v8NUeL/DVHd +/w1Q2f8OUdX/D1LT/w9T0v8QU9H/EFTR/xBV0f8QVtT/D1bW/w5X2v8OWd7/D1vj/w9c6P8PXez/ +D17v/w9e8v8OXfL/Dlvv/w5Z7P8OVuj/DVPj/wxP3f8NS9j/EFva/w9Vt/8QRJH+BTyX/xxNnPrN +1NfU//3tyu3u5c3x8ebM8PDmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx +5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHm +zPHx5szx8ebM8fHmzPHx5cvy8unT+/v49f39+/Xu6eH3y72m/8y+qPO+rJM/vauRGr2rkQ++rJIE +uqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMq8pc3Lvab/49zP+P3+/PT7 ++/n49fXu3PDw5snx8efN8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx +58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zy8efM7u/mzff26cvr7OPNP2+88wBBrf8QTaf+D2HR/xFk7/8NT+P/DVDe/w5Q2P8QUdP/ +Dk7N/wdJx/8DRsL/AkXA/wJFv/8CRcD/A0fC/wVKxf8KT8r/D1XQ/xFY1f8PWNr/D1rg/w9c5f8Q +Xej/EF3q/w9c6v8PWuj/D1jk/w5V3v8PUtn/EE/T/wtKzf8FQMb/BE3G/wFHqP8CNoP+ADKL/xFC +kvvAydHV+vnqye7v5s3x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzPHx58zx8efM8fHmy/Ly6tP7+/j1/f379e7q4ffLvqf/zL+p876skz+9q5EavauRD76skgS6 +p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkyrymzcy9p//j3M/4/f789Pv7 ++fj19e7c8PDmyfHx583x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzO7u5s349urK6Orjzjttu/MAQq3/EU6o/hFj0/8RY+7/Dk/i/w5Q3P8QUdb/DU7O/wNFxP8M +S8L/J2DK/0V31P9Yh9z/XYzf/1yL3/9UhNv/PnPT/yFey/8JTsb/BU3K/xBX0/8RWdj/D1rc/xBb +4P8QW+L/EFvi/xBZ4P8PVtr/EVXW/wxOzv8DRMT/EkzE/y1gzP9HcdT/WY3g/1iIx/9FaJz+Nl2d +/0ltqfvX3d/V9/Xoye/v583x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx +8efM8fHnzPHx58zx8ebL8vLq0/v7+PX9/fv17uri98y+qP/Nv6rzvqyTP72rkRq9q5EPvqySBLqn +jADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTKvKbNzL6o/+Pc0Pj9/fz0+/v5 ++Pb279zx8efJ8fHozfHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM +8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPLy6Mzu +7+fN+Pfqyufp4844arj0AEOr/xFPqf4SZdf/EmTv/w9R4v8PUtz/ElPV/wdJyf8RT8X/UYDa/4+x +8f+syf7/tdL//7fT//+41P//uNT//7bT//+00f//p8f8/4Sr7v9Fe9r/DFLL/wtTz/8TWtX/EVrY +/xFa2v8RWtr/EFjX/xJX0/8HTcn/GVbJ/2GM4P+Yt/X/r8r//7XO//+2zf//utf//6PB6P+Cl7X+ +hJu//6S73/vm6unW9fTnyfDx6M3y8ujM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx +8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx +6Mzx8ejM8fHozPHx58vz8+vT+/v49f39+/Xu6uL3zL+p/83Aq/O+rJM/vauRGr2rkQ++rJIEuqeM +AO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMu9p83Nv6n/5N3Q+P39/PT7+/n4 +9vbv3PHx58ny8ujN8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy +8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM7+/nzfn4 +68rm6eTON2m29AFEqf8SUqr+Emna/xRm8f8RUuT/EFTe/xNU1v8HScf/MmnQ/5a28/+40///s9D/ +/63L//+ryf7/qsn+/6vJ/v+syv7/rMr+/6zL/v+vzf//ttP//7fT//+JrvD/JWXS/whRy/8UWtH/ +EVjS/xFY0f8TV87/CU3F/zVr0P+gv/f/udP//7HN//+syP//q8f+/6rG/v+qxP3/sM///5295v96 +kLH+fpa6/6e94Pzm6unW9fToyfHx6M3y8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy +6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLo +zPLy6Mzy8ujM8vLny/Pz69P7+/j1/f379e7q4vfNwKr/zsGs876tkz+9q5EavauRD76skgS6p4wA +7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkzL6nzc7Aqf/k3dH4/f399Pv7+fj2 +9vDc8fHoyfLy6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy +6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzO/w6M36+OzK5ejk +zjdos/UCRKb/FFOr/hRs3f8VafT/E1Xn/xJW4f8VVtj/B0nJ/0V31/+xzf7/tND//6vJ/v+ty/7/ +rsv//6/M//+wzP//rMr//6nJ//+qyf//qsn//6nI//+qyf7/rcv+/7nU//+pxvv/N3DU/wpQx/8V +WMv/FFbJ/wxPwv8vZ8v/rcn7/7TQ//+qyP7/rcr//67K//+uyf//r8n//6rG//+nwv7/rc3//5u9 +6v94kLP+fZW4/6e83/zm6uvX9fTpyfHx6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLp +zPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM +8vLpzPLy6czy8ujL8/Ps0/v7+fX9/fz17+rj987Bqv/Pwqzzvq2TP72rkRq9q5EPvqySBLqnjADu +6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTMvqnNzsCr/+Td0fj9/fz0+/v5+Pb2 +8Nzx8ejJ8vLpzfLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLp +zPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czx8enN9/bryuTn5M83ZrD1 +AkSj/xRVrP4Vb+D/F2v2/xRX6/8UWeX/Flnc/wpNzP9AdNf/tM///7DN//+syv7/r8z//6/M//+w +zf//rsz//6vK//+91v//0eL//9zp///b6P//0OL//77W//+szP//qcn+/7XS//+ryfv/LGjO/w1S +wv8VVsP/EVC9/5G08f+30///rMr+/6/M//+vzP//rsv//6/L//+qx///vtT//9fk///a5f3/1ej/ +/7HK7P95krT+epO2/6a83fzm6uvX9vTpyfHx6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM +8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy +8unM8vLpzPLy6Mvz8+zT+/v59f39/PXv6uP3zsGs/8/CrvO+rZM/vauRGr2rkQ++rJIEuqeMAO7q +4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMy+qc3Owav/5d7S+P39/PT7+/n49vbw +3PLy6cnz8+rN8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM +8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8vLqzfLz6cr19evPRG6w9QBCnv8V +Vq3+FnHi/xdt9/8VWu7/FVzq/xZb4f8QVNT/J2LR/6vI+/+z0P//r8z+/7HO//+wzv//sc///6/O +//+y0P//3ur//////////////////////////////////+Xv//+81v//qcr+/7nW//+WufT/F1fB +/wpNuf9Ed83/udX//67N/v+xzv//sM7//7DN//+xzv//rcr//8ze//////////////////////// +/////9Le7v+Bl7b+epG0/6e73Pzn7O3X9vXqyfLy6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz +8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz +6szz8+rM8/Ppy/T07dP7+/n1/f389e/r4/fPwqz/z8Ou876tkz+9q5AavauRD76skgS6p4wA7urj +AP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkzb6pzc/CrP/l3tL4/f389Pv7+fj29vDc +8vLpyfPz6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz +8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPLz6s3w8ejK///0zqm71fUAQJf/Fler/hd0 +4/8Yb/f/Flzw/xZf7v8WXub/F13c/w5Szv+Eqe//u9b//67M/v+xz///sc///7HP//+xz///sc// +/+ry/////////Pz+/8zc+P+wyfX/s8v1/8/e+f/7+/7///////z9///E2///qcr+/77Z//9jkd3/ +AEOw/3eg4/+81///r83+/7HP//+xz///ss///6/N//+40v////////j5/f+8z/T/r8Xz/8va+P/7 ++/3//////+Po8P+Emrf+eZKz/6a62vzo7O3X9vXqyfLy6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz +6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/Pq +zPPz6szz8+nL9PTt0/v7+fX9/fz17+vk99DCrf/QxK/zvq2TP72rkBq9q5EPvqySBLqnjADu6uMA +////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTNv6rN0MOu/+Xe0/j9/fz0+/v5+Pb28dzy +8urJ8/PrzfPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz +68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zy8uvN9fTqyu3x7s7V2uD1QWqi/whOpf4bd+P/GW/3 +/xZf8v8WYvL/F2Ls/xlh4/8OV9X/P3fZ/7jT//+vzf7/sc///7HP//+xz///stD//6vM///b6f// +/////6jD8v85eub/EmTm/wxh6P8MYun/FGbo/zZ76f+PtPH/+/z+//////+91v//sdD//6nI+/8h +XLz/kLTu/7jV//+wzv7/sc///7HP//+y0P//qcr//+Ht///X4fb/NXLf/wtY3v8KWOL/EVvj/zhy +5v+mvfH//////+Ho8v9/mbn+epKx/6S52Pzo7e7W9vXryfLy683z8+vM8/PrzPPz68zz8+vM8/Pr +zPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM +8/PrzPPz6sv09O3T+/v59f39/PXv6+T30cOu/9HEsPO+rZM/vauQGr2rkQ++rJIEuqeMAO7q4wD/ +//8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJM3Aq83QxK//5d/T+P39/PT7+/n49/fx3PPz +6sn09OvN9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTr +zPT068z09OvM9PTrzPT068z09OvM8/PrzfX06sr09e/OssHY9Y2Zq/8YVaL+GXXf/xtw9P8YYPL/ +GGX0/xlm8f8ZZOr/GmPh/xBX0v+Eq+7/vdb//7HO/v+z0P//s9D//7TR//+xz///u9X///////+H +rOz/B1zg/xJm6f8cbvD/HXDz/x1w8/8bb/H/E2jt/wde5/9Ihej/6fD7//b6//+vzf7/vdj//2SQ +2f+XufH/utb//7LP//+z0P//s9D//7PQ//+z0P//6O/8/zx43f8HWd7/HWno/xxo7P8bZ+3/EV7q +/wdS4/91m+v//////8/f8/9/mbv+e5Kw/6S41vzq7u7W9/bryfPz68309OvM9PTrzPT068z09OvM +9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z0 +9OvM9PTqy/X17dP7+/n1/f389e/r5PfRxK//0cWx876tkz+9q5AavauRD76skgS6p4wA7urjAP// +/wD+/v4A////AM7ArQC5posCvqySC72rkRq8qo8kzsGrzdHFsP/m39T4/f389Pv7+fj39/Lc8/Pr +yfT07M309OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM +9PTszPT07Mz09OzM9PTszPPz7M319OvK9vbwzau81PSHmbH/cIuv/hJu2P8ecO7/GmDw/xpm9f8a +aPT/G2jw/xtm6P8WYN3/KmvX/67L/P+30v//tdH//7XR//+10f//ttL//63M///p8v//ssnx/whb +3f8ebur/H3Hx/xxx9f8cc/f/HHP4/xxy9/8ecvX/IXLx/wxi5/8+f+b/9Pf8/9ro//+y0P//pMP2 +/6rI+f+40///tdH//7XR//+10f//sc7+/8je//+gvO7/DFra/yBs5/8bau3/G2vy/xtq9P8davL/ +Hmjt/wVR4v+Dpez//////7PS9f+Fnr7+e5Gu/6S31Pzr7+/W9/bryfPz7M309OzM9PTszPT07Mz0 +9OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT0 +7Mz09OvL9fXu0/v7+fX9/fz17+vk99LFsP/SxrHzvq2TP72rkBq9q5EPvqySBLqnjADu6uMA//// +AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqjyTOwavN0cWx/+bf1Pj9/fz0+/v5+Pf38tzz8+vJ +9PTszfT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz0 +9OzM9PTszPT07Mzz8+zN9fTsy/b38M2vvtLzcoys/6Kwwf5dmt3/EGbk/x1i7P8bZ/P/G2r1/xxr +9P8cau//Hmrn/xFe2f9SiOH/wNj//7TR//+20///ttP//7bT//+20///t9T//+Xt/P82d97/Fmjl +/yBx7v8dcvT/HnX5/x52/P8edvz/Hnb7/x50+P8dcvX/I3Tw/wph5P9rm+n//////7fT//+31P// +uNT//7bT//+20///ttP//7bT//+x0P//y+D//1+R4v8PYN7/IG7q/x1u8f8cb/b/HW/4/xxu9/8d +a/P/Hmjs/w1X4f/B0PP/7Pj//6nN9v+KosD+epCt/6S20vvt8PDV9/bsyfPz7M309OzM9PTszPT0 +7Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTs +zPT07Mv19e7T+/v59f39/PXv6+X30sWy/9LGsfO+rZM/vauQGr2rkQ++rJIEuqeMAO7q4wD///8A +/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqPJM/BrM3SxrH/5uDU+P39/PT7+/n4+Pjy3PT07Mn1 +9e3N9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX1 +7cz19e3M9PTtzPb17Mv4+PHNr7zQ83mPq/+Jo8P+x974/z5/4f8UWuP/Hmju/x1q9P8dbfb/HW70 +/x1s7v8fa+f/El7X/3ik7P/B2///tdL+/7fU//+31P//t9T//7LR/v/L4f//ob7u/w9g3P8icer/ +HnLx/x519/8fd/v/H3j+/x94/v8feP3/H3f7/x92+P8ec/P/InLt/w9i4f/A0/T/4O7//6/P//+4 +1f//t9T//7fU//+31P//t9T//7XT///A2P3/O3rc/xdm4f8fcOz/HnHz/x5z+P8ec/r/HnH6/x1u +9v8gbPD/FGDn/0B54//09v3/vdr//7LU+P+MpML+epCr/6S1z/rv8fHU9/btyfP07c319e3M9fXt +zPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M +9fXty/b279P7+/n1/f389fDs5ffTxrL/08ey87+tkz+9q5AavauRD76skgS6p4wA7urjAP///wD+ +/v4A////AM7ArQC5posCvqySC72rkRq8qo8kz8GtzdPHsv/m4NX4/f389Pv7+fj4+PLc9PTsyfX1 +7c319e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXt +zPT07cz29e3L+fnxzK+8zvJ4jqr/jqbE/rnb+v+30vr/IV/a/xxk5/8eau//H270/x5w9v8ecPT/ +H2/t/x9t5f8ZZNj/kbfz/7/a//+10/7/t9T//7fU//+31P//stH//87i//9gk+L/EmXf/yJz6/8g +dPP/IHf5/yB5/P8gev//IHr//yB6/v8gef3/IXj6/x929v8idfD/FGjm/06J5P/v9f7/tNL//7fU +//+31P//t9T//7fU//+21P//udb//7HN+v8pb9n/HGvj/yBx7P8fc/T/IHX5/yB2/P8fdfv/H3L5 +/x5v8/8ibe3/DVng/6vC8P/a5v//stP//7bY+f+Op8T+epCq/6O0zvrw8/HT9/ftyvP07M319e3M +9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz1 +9e3L9vbv0/v7+fX9/fz18Ozl99PHs//TyLPzv62TP72rkBq9q5EPvqySBLqnjADu6uMA////AP7+ +/gD///8AzsCtALmmiwK+rJILvauRGryqjyTPwq7N1Mez/+fg1fj9/fz0+/v5+Pj489z09O3J9fXu +zfX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz09O7M +9fXuzPn58suvu83wd46q/5Cqxv622Pr/w+D//5u38P8XXdv/IGno/x9t8P8fcPX/H3P2/x9z9P8g +cO3/Hm3k/yJr2f+jxPj/vdn//7fU//+41f//uNX//7jV//+31f//v9j9/zl63P8aa+L/InTs/yB2 +9P8hePn/IXv8/yF7//8he///IXv//yF7/f8hevv/IXj4/yB18v8idOv/Fmff/7/U9P/L4v//tNL/ +/7jV//+41f//uNX//7fU//+82f//pcX2/yRs2P8ebuP/IHPs/yB19P8hd/n/IXj8/yF3/f8gdfr/ +H3L2/yJv7/8UYuX/UIXj/+Xt/v+xy/7/u9r//7fa+v+Qqsb+eY+q/6SzzPny9fLS+vnvyvLz7c31 +9e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX1 +7sv29vDT+/v59fz9+/Xw7OX31Me0/9TItPO/rZM/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+ +AP///wDOwK0AuaaLAr6skgu9q5EavKqPJM/Drs3UyLT/5+HW+P7+/PT7+/n4+Pjz3PX17cn29u7N +9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPX17sz29u7M9PXuzPX17sz6 ++vLKsbvK73iOqf+TrMn+utz8/7nY///E2f//hans/xRe2/8jben/IXDw/yF09f8hdvb/InX0/yNz +7v8fbuT/KXHb/63M+/+92P//udX//7rW//+61v//udX//73Z//+uy/n/KHDZ/x9v4/8ide3/Inj0 +/yN6+f8jfP3/I3z//yN8//8jfP//I3z//yN8/P8jevn/Inj0/yV37f8SZ+H/dqPn/9zr//+z0v// +u9b//7rW//+61v//udX//8Db//+ewPX/IGrX/yFx4/8idOz/Inf0/yN5+f8jevz/I3r9/yN4+/8h +dff/IXLx/yBt6f8gZt7/xdb3/8LZ//+3z/7/vNr//7rc/P+SrMn+fJGq/5mpw/jj6OnR/fzxyvPz +7c329u7M9fXuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbu +y/f38NP7+/n1/f379fHt5ffUyLX/1Mi187+tkz+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A +////AM7ArQC5posCvqySC72rkRq8qo8k0MSvzdXJtf/o4db4/v789Pz8+fj4+PPc9vbuyfb27832 +9u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPX17sz29+/M/v30yrG7 +yO15jqj/lrDL/rzd/f+82///t9D+/8fd//9wnOj/FWDc/yVw6v8jc/H/Inb2/yN39/8kd/T/JXbu +/yBw5P8sdNz/stD8/7/Z//+71///vNf//7zX//+71v//wdv//6TD9f8kbdj/InHk/yR27f8kePT/ +JXz5/yV+/f8lfv//JX7//yV+//8lfv//JX79/yV8+/8kevb/JXjv/xxv5v8+f9//1OT8/7rW//+8 +1///vNf//7zX//+71v//wdv//6PE9v8jbNf/InHj/yR27P8kefP/JXr5/yV9/P8lff7/JXv8/yR4 ++f8idfP/JXPs/xNh3/+Psez/0OP//7bR/v+70v7/vNv//7zd/f+XsMz+d42m/09tnPbj6OrP/fzy +yvP07c329u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/L +9/fx0/z8+fX9/vv18e3m99XJtv/VyLbzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD/ +//8AzsCtALmmiwK+rJILvauRGryqjyTQxK/N1cm2/+jh1vj+/vz0/Pz5+Pj489z29u7J9vbvzfb2 +78z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z19e7M9/fwzPn58cmZpbfre4+o +/5iyzv6+3/7/vtz//7vT/v+60///xtz//2aU5P8YZNz/JXPq/yN18f8kePb/JXn3/yR59P8ld+7/ +IXHl/y523f+10vz/v9n//7zX//+91///vdf//7zW///D2///o8P1/yRt1/8jc+P/JHfs/yR69P8m +ffn/Jn78/yZ///8mf///Jn///yZ///8mf/7/Jn77/yR89/8lefD/JHXp/yFv3f+40PX/xNz//7zW +//+91///vdf//7zW///B2///rcv5/yhx2P8iceL/JHfs/yR68/8lfPn/Jn78/yZ//v8mffz/JXv5 +/yR49P8mde3/Fmfi/1+R4//T4///t9L//7zV//+70/7/vtz//7ze/f+iutL+NlyN/zldlPXu8e/O ++vrxy/T07s329u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278v3 +9/HT/Pz59f3++/Xx7eb31cm2/9XJtvO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP// +/wDOwK0AuaaLAr6skgu9q5EavKqPJNHEsM3Wyrf/6OLX+P7+/fT8/Pr4+Pj03Pb278n29vDN9vbw +zPb28Mz29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9fXvzPX28Mz///XJa4Kk6Tpejf+kvNT+ +vd/+/77c//+80/7/vNX//7rU///F3P//XpDj/xln3f8mder/JXjx/yV69v8lfPf/JXv1/yZ57/8h +c+X/MXre/7rV/P++2f//vdj//73Y//+92P//vNf//8Pd//+hw/X/I23W/yR04v8leOz/JXvz/yZ+ ++f8mgPz/JoD//yaB//8mgf//JoH//yaB/v8mf/v/JX34/yV78f8oeer/GGvd/5G37f/L4v//utb+ +/73Y//+92P//vNj//8Da//+00vz/L3bZ/yFx4f8meOv/JXvz/yV++P8mgPz/Jn/+/yZ//f8mffr/ +JXr1/yZ37v8dbeT/PXze/8fb/f+71f//vNX//7zV//+80/7/vNz+/8Xl//92ncb+AjyB/0xrnPPz +9fHM+fnyy/T078z29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9vbwy/f3 +8tP8/Pr1/f389fHt5vfWyrf/1sq387+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A//// +AM7ArQC5posCvqySC72rkRq8qo8k0cSxzdbLuP/o4tf4/v799Pz8+vj5+fTc9/fvyff38M339/DM +9/fwzPf38Mz39/DM9/fwzPf38Mz39/DM9/fwzPb278z29/DN///0yX2PqugAO4P/Y5PE/sjn//+8 +3P7/vNT//73V//+91v//vNb//8Td//9NiOD/G2rd/yd26v8mevH/Jnz2/yZ9+P8mfPX/J3vw/yF0 +5v8ze97/vNf7/77b//+92v//vdr//73a//+82f//wt7//6TG9v8mb9X/JHPh/yZ56v8mfPH/Jn74 +/yaA/P8mgv7/JoL//yaC//8mgv//JoL+/yaB/P8mfvj/Jnzy/yh76/8YbN7/caHm/83k//+52P// +vdr//73a//+92v//vdr//7zX/f89ftv/H3Df/yd56/8mfPL/Jn74/yaB/P8mgv7/JoD9/yZ/+v8m +fPb/J3nv/yNz5v8qcdv/ts/4/8Da//+81v//vdb//73V//+70/7/xeL//5/L9/8garX+CkSJ/1Rx +oPH3+PLL+PnxzPX178z39/DM9/fwzPf38Mz39/DM9/fwzPf38Mz39/DM9/fwzPf38Mz39/DL+Pjy +0/z8+vX9/fz18e3n99bLuP/Wyrjzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8A +zsCtALmmiwK+rJILvauRGrypjyTSxbHN18y5/+ni2Pj+/vz0/Pz6+Pn59dz39/DJ9/fxzff38cz3 +9/HM9/fxzPf38cz39/HM9/fwzPf38cz29vDM9vfxzP//9MmCkarmD0eL/xhrvP53su3/y+b//7vV +/v++1///vtf//77Y//+91///xt7//06K4P8ebNz/KXnp/yd88f8nfvb/J4D4/yd/9v8ofvH/JHfp +/zJ73/+91vr/wNz//77a//+/2v//v9r//77a///D3v//r875/yxz1f8ldN7/KHrp/yd98f8ngPf/ +KIL7/yiE/v8ohP//KIT//yiE//8ohP//KIP8/yiB+f8nfvP/KXzs/x1w4f9XkeL/yuH//7zZ//+/ +2v//v9r//7/a//++2v//wtz//0aH3v8fcd//KXvq/yd+8f8ngPj/KIL8/yiE/v8og/7/KIH7/yd/ +9v8nfPD/J3jo/yFt2/+iw/T/xt///73X//++2P//vtf//73W///C2f//r9T6/y2H3P8kdML+C0aM +/194ou/7+/PL+PjyzPX278z39/HM9/fwzPf38cz39/HM9/fxzPf38cz39/HM9/fxzPf38cv4+PLT +/Pz69f39/PXx7ef318y5/9fLufO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDO +wK0AuaaLAr6skgu9q5EavKmPJNLFsc3XzLr/6ePY+P7+/PT8/Pr4+fn13Pf38Mn39/HN9/fxzPf3 +8cz39/HM9/fxzPf38cz39/HM9vbwzPb38sz///TJhZKo5AI9hv8YccX+DHjb/2qn4//D1/z/tM75 +/7fR+v+30vr/t9L6/7XS+v/A2fv/Tofb/wxf1P8YbeL/F3Hq/xdz8P8XdfP/F3Xy/xhy7P8WbuX/ +GGnY/67J8f+92fv/ttP6/7jU+v+41Pr/t9T6/7nW+v+y0Pf/KG/P/xFl1P8XbeH/F3Hp/xd08P8Y +d/b/GHj4/xh5+f8Yefr/GHn6/xh5+v8YePj/GHb0/xdz7v8Zcef/Dmbb/zt82P++1vn/ttT6/7jU ++v+41Pr/uNT6/7bT+v+/2fv/Q4Pa/w1i1v8Zb+P/F3Lr/xd18v8Yd/f/GHn5/xh5+f8Yd/b/F3Px +/xdx6v8YbeL/DmDT/4mw6f+/2vz/tdH5/7fS+v+30vr/ttH6/7jR+/+xyvf/KYDV/xN93/8Ycsb+ +ADuH/2B4oOz9/PTK9/jyzPb28Mz39/HM9/fxzPf38cz39/HM9/fxzPf38cz39/HM9/fxy/j48tP8 +/Pr1/f389fHu5/fXzLr/18u687+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7A +rQC5posCvqySC72rkRq8qY8k0sayzdjNuv/p49j4/v789Pz8+vj5+fXc9/fwyff38c339/HM9/fx +zPf38cz39/HM9/fxzPb28Mz39/PM//7zyo6YqOECPYj/D3HM/haF6P8EcNb/YJDf/8LX+v+xzff/ +tND4/7TQ+P+00Pj/stD4/77Y+/9PiNn/BFrQ/xNp3v8RbOf/EXDu/xFx8f8RcfD/EW/r/xNs5P8G +X9f/nr7r/8Ha+/+z0fj/tdL4/7XS+P+10vj/tNL4/7vW+f83edD/BlzN/xJo2/8Ra+T/EW/s/xFx +8f8Rc/X/EXX3/xF1+P8Rdfj/EXX4/xF09v8RcvL/EW/s/xFs5P8LY9n/JG7S/7LP9f+20/n/tdL4 +/7XS+P+10vj/s9H4/7/Z+/9Qitv/BFzR/xNr4P8Rbej/EXDv/xFz9f8Rdff/EXX3/xFz9P8RcO// +EWzo/xJp4P8FW9D/eaTk/7/Z+/+yz/f/tND4/7TQ+P+00Pj/tND4/7XN9/8tbdX/CnTY/xOD6P8T +c8z+ADiG/2p+oen+/fPK9/jzzPb28Mz39/HM9/fxzPf38cz39/HM9/fxzPf38cz39/HL+Pjy0/z8 ++vX9/fz18u7n99jNuv/YzLrzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCt +ALmmiwK+rJILvauRGrypjyTSxrPN2M67/+nj2fj+/vz0/Pz6+Pr69tz4+PHJ+Pjyzfj48sz4+PLM ++PjyzPj48sz39/HM+Pn0zP788suYn6nfCD2F/g900/8UifL/FYTl/wNZ2f9Vhd7/xdv7/7LO+f+1 +0fn/tdL5/7XS+f+y0fj/wtv8/1uT3f8EW87/FGve/xJu5v8Sce3/EnPx/xJz8f8Sce3/FXDn/wJf +2v+Fruf/zeH8/7HQ+P+20/n/ttP5/7bT+f+00fj/wtv9/1iP2f8EWsj/E2jY/xFr4f8Sb+n/EnLw +/xN19f8Tdvf/E3f4/xN3+f8Td/j/E3b2/xJ08v8Scez/Em3k/w5m2v8aatD/rMv0/7nV+v+10/n/ +ttP5/7bT+f+z0fj/wtv8/1yV3/8EXdH/FGzg/xJv6P8Scu//E3X1/xN3+P8Td/j/E3X1/xJy7/8S +b+j/FGzg/wVc0f9onOP/wtz9/7LR+P+10vn/tdL5/7XR+f+00Pn/utP5/zRw2P8HXNn/FIPk/xSI +8f8Td9T+ADWD/3eHo+b//fPK+Pn0zPf38cz4+PLM+PjyzPj48sz4+PLM+PjyzPj48sv4+fPT/Pz6 +9f39/PXy7uf32M67/9jNu/O/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0A +uaaLAr6skgu9q5EavKmPJNPGs83Zzrz/6uPZ+P3+/PT8/Pr4+vr23Pj48cn4+PLN+PjyzPj48sz4 ++PLM9/fxzPn69Mz5+PDLpKaq3BJChf0Nddb/FY/6/xOK7f8TbOf/A1Tb/0SA3P/H2/r/stD5/7bS ++f+20/n/ttP5/7TS+P/C3P3/bJ/h/wVby/8Ua9z/Em7l/xJx7P8SdPD/EnTy/xJz7/8Vcun/AmPe +/12W4f/Y5/r/sND5/7jU+f+31Pn/t9T5/7XS+P/B2/3/gq3m/wdaw/8SZ9P/EWrd/xJu5v8Scez/ +EnTy/xN29f8Td/f/E3j4/xN4+P8TdvX/EnTx/xJx6/8SbeP/DmfY/xhozv+myPP/utf6/7bT+f+3 +1Pn/t9T5/7TS+P/C3Pz/ap/i/wVe0P8UbeD/EnDo/xJz7v8TdvT/E3f3/xN39/8TdvT/EnPv/xJw +6P8UbeD/BF3Q/2KZ4f/E3f3/s9L4/7bT+f+20/n/ttL5/7TR+f+91vn/OXja/wZU2f8Sa+X/E4ns +/xSN+P8Sedf+ATaC/4aSpOP9+/DL+Pn0zPf38cz4+PLM+PjyzPj48sz4+PLM+Pjyy/j589P8/Pr1 +/f389fLu5/fZzrz/2M2887+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5 +posCvqySC72rkRq8qY8k08ezzdnPvP/q5Nn4/f389Pz8+vj6+vbc+Pjyyfj48834+PPM+PjzzPb2 +8cz7+/bM9PPtza+vrdkiSYX5CnLV/xaT//8UjvP/EXPv/xFh6P8HXd7/NXba/8fa9/+10fn/uNP5 +/7jU+f+41Pn/ttL4/8Lb/P+Er+j/B13J/xNr2v8RbuT/EnHr/xJ08P8TdvL/EnTw/xRy7P8KauP/ +MHvd/9zn9/+21Pn/uNX5/7jV+f+41fn/t9T5/7vX+v+tzPT/HGfF/w1iy/8Radf/EW3h/xJw6f8S +c+7/E3Xy/xN39f8Td/b/E3f2/xN28/8Sc+//EnDp/xJt4P8OZ9X/F2fL/6bJ8/+82Pr/t9T5/7jV ++f+41fn/ttP4/8Lc/P98quf/B1/P/xNu3/8Sb+j/EnPv/xN29P8Td/f/E3f3/xN29P8Sc+7/EnDo +/xRu3/8EXs//Zpzh/8Td/f+10vj/uNT5/7jU+f+40/n/ttL5/8DX+f88e9r/Blra/xFg5v8Rc+3/ +E47x/xWS/v8Qdtb/CTeA/pefqd759+7L+vr2zPf38cz4+PPM+PjzzPj488z4+PPL+Pn00/z8+vX9 +/fz18u7o99nPvP/Yzrzzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmm +iwK+rJILvauRGrypjyTTx7TN2c+9/+rk2vj9/fz0/Pz6+Pr69tz4+PLJ+Pjzzfj488z39/LM/Pz3 +y/Dw7M23ta7XO1WG9Adnzf8Xlv//FJD2/xN99f8QY/D/Emjq/wxj4f8ga9j/wtb0/7rW+v+51fn/ +utX5/7rV+f+41Pj/wNr7/57B7/8RY8r/EWnW/xJu4f8Tcur/E3Xw/xN28/8TdvL/E3Tu/xRx5/8L +aNz/wdXw/8zh+/+10/n/utb5/7rW+f+61vn/t9T4/8Xe/f9RitT/BVnC/xRo0f8Sa9r/Em7i/xNx +6f8TdO7/E3Xx/xN28v8TdvL/E3Xw/xNy6/8ScOT/Emzc/w9l0f8ZZ8n/q8v0/73Y+/+51fn/utb5 +/7rW+f+41Pj/w9z8/4m06/8IYc//FG7e/xNx5/8TdO7/FHf0/xR59/8Ueff/E3b0/xN07v8TcOf/ +FG7e/wZfzf90pOP/xd78/7fU+P+61fn/utX5/7nV+f+30/n/wNf4/zp72f8HXdz/E2fn/xBi7v8T +fPP/FZD1/xaV//8Mas3/Gj+B+6irq9rz8uzN+/v3y/f38sz4+PPM+PjzzPj488v4+fTT/Pz69f39 +/PXy7uj32c+9/9nOvfO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaL +Ar6skgu9q5EavKmPJNTItM3a0L3/6uTa+P39/PT8/Pr4+vr23Pn58sn5+fPN9/fxzP3998vu7urO +t7Sr12B0kesGQqn/Foby/xSU+/8Vkvf/Em/2/xFp8f8Ra+z/Emnk/w9j2P+yy+//wdv7/7jU+f+6 +1fr/utb6/7rW+v+71/v/tNH3/yNuzP8NZdH/E27e/xNy5/8Tdu7/E3fy/xN38/8TdvD/F3br/wFl +3/99q+f/7PP8/7LR+v+71vr/utb6/7rW+v+51fn/wdv9/5q+7f8NXb//EWTK/xFo0v8SbNr/Em/h +/xNy5/8TdOr/E3Ts/xN07P8TdOr/EnHl/xJu3v8Ta9b/C2LL/ydvyf+20vj/u9f7/7rW+v+61vr/ +utb6/7jV+v/C2/3/l73u/w5kz/8Sbdz/E3Lm/xN17v8Td/T/FHr3/xR59/8TePP/E3Xt/xNy5f8U +btv/B2DM/4Cs5//F3f3/uNT5/7rW+v+61fr/utX5/7fV+v/B2Pj/NXjZ/wlh3v8Tauj/EWjv/xJu +9P8Vkvb/FJT7/xaF8P4GQqn/O1qL87Oyqtjv7+rO/f33y/f38cz5+fPM+fnzy/n59NP8/Pr1/f38 +9fLv6Pfa0L7/2c+987+tlD+8qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posC +vqySC72rkRq8qY8k1Mi1zdrQvv/q5Nr4/f389Pz8+vj6+vfc+fnzyff38s3+/vnL7e3pzqurp9qg +oJzcKF+0+QtPwf8VhOv/FZj8/xWV+P8Tc/b/Emzz/xFu7v8Vbuf/Al/a/5W56v/O4v3/ttP5/7rX ++v+61vr/utf6/7jW+f/D3fz/RIXU/wdgzP8Ubtv/EnLk/xN27P8TePH/E3nz/xN48f8Vd+3/DG/l +/yt84P/u8vj/w937/7jW+v+62Pr/utf6/7rX+v+31vn/xN79/02I0P8EWL3/FGfM/xFo0v8SbNn/ +Em/f/xJx4v8ScuT/EnLk/xJw4v8Sbt3/EmvY/xNp0f8HXcT/Pn7N/8Hc/P+41/n/utf6/7rX+v+6 +1/r/udb6/7/b/P+kx/L/FWnQ/xFs2/8TcuX/E3bt/xN58/8Ue/f/FHr2/xN48v8Tduv/EnHj/xNt +2v8KYcv/jrns/8Pe/f+41vn/utf6/7rX+v+61vr/utb7/7/X9/8mctj/DGbg/xJs6v8Ra/D/E3L1 +/xWV9/8VmPz/FILp/xBSwv8YU7D/lZeY4a6tqNnt7ejO/v75y/f38sz5+fTL+fn10/z8+vX9/fz1 +8u/o99rQvv/Zz73zv62UP7yqkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+ +rJILvauRGrypjyTVybbN29HA/+vl2/j9/fz0/Pz6+Pr699z39/LJ/v75zOzs586oqKTao6Od29nb +29Ixbc34C0y8/xaG7P8Vm/z/Fpj4/xN29/8Sb/X/EnHw/xVx6v8BY93/ap7j/9zq/P+01Pr/vNj6 +/7vY+v+72Pr/uNb5/8bh/v9ypOH/BV3H/xRt2P8SceH/E3Xq/xN47/8TevP/E3rz/xN48P8XeOv/ +Amjg/6XE7v/x9/z/stT6/7zZ+v+72Pr/u9j6/7rY+f+/2/z/rMzz/xpkvv8MXsD/E2bJ/xBoz/8R +a9X/EmzZ/xJu2/8Sbtv/Em3Z/xFr1v8RaND/E2fK/wNYvP9rntv/x+H+/7jW+f+72Pr/u9j6/7vY ++v+61/r/vtv7/63P9f8abdH/EGzb/xNz5f8Td+3/E3ry/xR79v8Ue/X/E3nw/xN16v8TceL/EGvW +/xRny/+kx/H/wNz8/7nX+v+72Pr/u9j6/7rX+v+92vv/u9P0/xtu2P8Pa+L/E2/r/xFt8v8Tdfb/ +Fpj3/xWb/P8Vhev/DU69/yNhxv7O0tPZpqWf2qeopNvs7OfO/v75zPf38sv5+fXT/Pz69f39/PXy +7+j329G//9rQvvO/rpQ/vKqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6s +kgu9q5EavKmPJNXJts3b0cD/6+Xb+P39/PT8/Pr4+fn13P7++cjq6uXQp6ej26Kin9zm5eDP+Pr5 +zC5px/gLTb7/Fojt/xWe/P8Wm/n/E3j5/xJy9v8Sc/H/FHPr/wlq4v82gN3/3ur5/7nX+v+82fr/ +vNn6/7zZ+v+72Pr/wt38/6LG8f8SZMf/EGrS/xJw3f8TdOf/E3ju/xN68v8Te/T/E3ry/xV57v8L +cOf/NIPj//v6+v/Q5Pv/uNb7/77Z+/+82fv/vdn7/7rX+f/G4P7/jLXm/wlYt/8OX8D/E2bH/xBm +yv8RaM7/EWjQ/xFp0P8RaM//EGfM/xJmyf8NYML/FmK+/6zM8//B3Pz/vNj6/73Z+/+92fv/vdn7 +/7zZ+/++2vv/uNX4/yd10/8Ma9n/FHTl/xN37f8TevP/FHz1/xN79P8TePD/E3Xo/xNx3/8MZ9L/ +JnLN/7jV+P++2vv/vNn6/7zZ+v+82fr/utj6/8Xf/P+tyvD/DGfZ/xJv5f8Scez/EnDy/xN49/8W +mvj/FZ78/xWH6/8PUL//HlzB/+zv8dPp6OLOoqKe3Keno9vp6eXP/v75yvj49NP8/Pr1/f389fLv +6Pfb0b//2tC+87+ulD+8qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqyS +C72rkRq8qY8k1cm2zdzSwf/r5dv4/f389Pv7+fj///3b5+fjzaWlotylpaLb5ufj0P//+8vx8/LO +LmrK+AtNv/8Xiu3/GKD9/xed+f8Ue/n/FHT3/xR28/8Tde3/E3Pn/xBs3P/G2fL/zOL8/7vY+v++ +2fv/vtn7/77a+/+92fv/xN38/zp+0P8JYsv/FG/Z/xNy4v8Ud+v/FHnw/xR78/8Ue/T/FHrx/xd6 +7f8DauL/lb3t/////f/A2vv/vNn7/7/a+/++2vv/vtr7/7zY+v/J4v//ha/j/w5buP8GWbn/EWLB +/xNlxf8TZcb/E2XH/xNlx/8UZcX/DWDA/wNWuP96qOD/yeH//7vY+v++2vv/vtr7/77a+/++2vv/ +vtr7/77a+//A2vr/NH7X/wpp2P8VdeX/FHfs/xR68v8Ue/T/FHrz/xR47f8Tc+T/FXDc/wdizP9I +idb/x+D9/7zY+v++2vv/vtn7/77Z+/+61/r/0eX9/5O56v8EZdv/FnPo/xN07v8Tc/T/FHr4/xec ++f8YoP3/Fonr/w5QwP8fXcP/5ejq1P///cnm5uLPpaWi26Wlotvn5+PO///80vv7+fX9/fz18u/p +99vSwP/a0b/zv66UP7yqkBm9q5EOvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJIK +vauRGbypjyLVybfN3dLC/+vl3Pj9/fv0/v799+bm49+jo6DZp6ej2+rq5c////rL+/r0zPP1880v +a8r4DE6//xiL7f8Yof3/GJ35/xR9+f8Ud/j/FHn1/xN48P8YeOv/Amfe/4m06f/m8f3/uNb7/8Db ++/+/2/v/v9v7/7zZ+v/K4///fKrj/wZexP8VbdT/E3He/xR15/8Uee7/FHvy/xR89P8UfPP/FXvw +/xF26v8ad+T/3+n3//T4/P++2vv/vtr7/8Dc+/+/2/v/v9v7/73Z+v/K4///ncHs/zh5xv8KWrf/ +BFe3/wVZuf8FWrr/BFm6/wRZuf8YZb//e6nh/8nh/v+72fr/yuH7/8ff+/+92vv/v9v7/7/b+/+/ +2/v/vdr7/8bf/P89htv/CWrZ/xV25v8UeOz/FHvx/xR78v8UevD/FHfq/xNz4f8Vb9j/Bl/H/3Wn +4v/L4///vNn6/7/b+/+/2/v/wNr7/7nW+//e7P3/bKLk/wNn3v8Xduv/E3fw/xR29f8UfPj/GJ35 +/xih/f8Xiuz/D1HB/yBew//n6uzU/fz2yv7/+szq6uXPp6ej26OjoNrl5eLW////9Pz8+/Xz7+n3 +3NLB/9vRwPO/rpQ9vKqQGL2rkQ6+rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgm9 +q5EXvKmPH9XKt8vd08L/6+Xc+P7+/fT6+vn3q6yq56eno9js7OjP///6y/j488z8+/XM8/X0zS9r +y/gMTsD/GIzu/xii/f8Yn/n/FH/6/xR4+P8Ue/f/FHrz/xZ57f8Kb+T/PYnh/+/0+v++2/v/v9v7 +/7/b+/+/2/v/v9v7/8Hd/P+41fj/Im/I/w1nzP8Tb9n/E3Pi/xR46v8Ue/D/FHz0/xR99P8UfPP/ +F3zw/whx6f9Ikej//f38/+30/P/A3Pz/vdr8/8Dc/P+/3Pz/v9v8/73a+//H4f//xN79/5e96/9n +nNn/UIvQ/0yJz/9XkdT/eanh/7DQ9v/K4///vtv7/7vZ+//v9vz/0ub8/7vZ/P/A2/z/v9v8/7/b +/P+92vz/yeL+/1CT4f8Iatv/Fnfn/xR57f8Ue/D/FHvx/xR57f8Tdub/E3Hd/xFs0v8TZ8b/pcjy +/8Xg/v++2vv/v9z7/7/c+//A3Pv/vNr7/+Hs+v87ht//Cm7j/xV47f8TefL/FHf2/xV++f8Yn/n/ +GKL9/xeL7P8PUcL/IF7E/+fq7NT//vfK9/fzzf//+svs7OjPqKil2aOjoeDo6Ob1////9PHu6Pfc +08L/29LA87+ulDq8qpAVvauRDL6skgO6p4wA7urjAP///wD+/v4A////AM7ArQC5posBvqySB72r +kRS8qY8Z1cq3yt3Tw//r5dz4////9Pb19Pe2trXk6+vnzf//+8z4+PTN+fn2zPz79szz9fXOL2zL ++AxPwP8Yju7/GKT9/xmh+f8Vgfr/FHr6/xR9+P8VfPX/FHvw/xV56v8Jbt//wtfy/93t/v+52fz/ +wd38/8Dc/P/A3Pz/vdr7/83l//9sod7/BF7D/xVv0/8Sctz/E3bl/xR67P8UfPH/FH70/xR+9P8U +ffP/GH7v/wRv5/9wqe3////9/+/2/P/J4fz/vNr8/8Dc/P/B3fz/wd38/7/b+//A3Pz/yeP//83l +///L5P//yuP//8zk///M5f//xeD+/77b+/+62fz/4O79/+7w9f/C2vf/wd79/8Hd/P/B3fz/wd38 +/77b/P/M5P7/Xp7n/wZs3f8Weef/FHrs/xR77v8Ue+3/FHjp/xN14v8Ucdj/CGTK/z6D0f/H4f3/ +v9z8/8Dc/P/A3Pz/wNz8/73b/P/M5P3/y930/xFw3f8Tduf/FHnu/xR79P8Uefj/FYD6/xmh+f8Z +pP3/F4zs/w9Swv8gX8X/5+rt1P/++Mr5+fbM+Pj0zf//+8zt7erOra2r36ysq/jm5ub1+PTv99zS +wf/b0sDyv66UNbyqkBK9q5ELvqySArqnjADu6uMA////AP7+/gD///8AzsCtALmmiwG+rJIFvauR +D7ypjxPWyrjI3tPD/+zm3vj+/v71+Pj39vX09PH////u/Pz77/39/e/8/Pzv/v787/f4++8ybc39 +DU/B/xmO7v8ZpP3/GqH5/xWC+/8UfPv/FH/5/xV+9/8UffP/F33u/wRv4/9joOb//fz9/7zb/P/C +3fz/wd78/8He/P/B3fz/w+D9/7vY+f8kccf/DGfK/xNw1v8TdN//E3nn/xR87v8UfvL/FID0/xSA +9P8Uf/P/F3/w/wRx6P97sO/////9//z9/f/b6/z/w9/8/73c/P+/3fz/wd78/8Le/P/A3fv/v9z7 +/8Dd+//A3fv/wN37/7/c+/+92/z/v938/9vr+///////e6je/6XH7//H4///v9z8/8Dd/P/A3fz/ +vdv7/83l/v9rqOz/Bm7g/xZ76f8Ue+z/FHzt/xR76v8TeOT/E3Pd/xRw0/8GYMP/hbLm/8zm//+/ +3Pv/wd78/8Hd/P/D3vz/utj8/+nz/v+OuOv/AWvg/xd77P8Ue/H/FH32/xR7+f8Vgvv/GqH6/xmk +/f8Yje3/EFLD/yFgxf/q7fPy///+7vz8/O/9/f3v/Pz77////+7z8/Lwurm5966urvfc2NP44NbF +/9vRwPG/rpQvvKqQDb2rkQi+rJICuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAL6skgO9q5IN +u6mOB9XKt6/e1sX95d/S+f39/fX+/v71////9v7+/vb////2////9v7+//b////2+fv99jNuzv4N +UML/GY7u/xml/f8bofr/FoP7/xR9+/8Uf/r/FYD4/xV/9v8VfvH/E3rq/xN14P/a5vb/4e/+/7zb +/P/E3/3/wt/9/8Pf/f/A3Pz/zuf//4Ox5f8GX8D/FG7P/xJx2P8TduH/FHrp/xV97/8Uf/P/FYD1 +/xSA9f8Vf/T/F3/x/wZz6v9rqO//+Pr8/////v/z+P3/3ez9/8zj/f/C3v3/v939/77c/f++3P3/ +v9z9/77c/f+/3P3/wt79/9Dl/f/r8/z//////77W8v8JaNL/psnx/9Lo///H4Pz/yeL9/8ni/f/G +4P3/1Oj//4S38v8JcuT/Fnzq/xR76/8Ue+r/FHjm/xN13/8Ucdb/C2fK/yx3yv/B3Pv/w9/9/8Le +/f/D3/3/w9/8/8Le/P/D3vz/9Pf7/z2L4v8Kc+b/F3zv/xR+9P8Ufvf/FHz5/xaD+/8aofr/GaX9 +/xmN7f8QU8T/IWDG/+zv9fj////2/v7+9v////b////2/v7+9v////b39/b1vb6+9rOvqfnh2Mf9 +29HA4L+tkxu8qpALvauRBb6skgG6p4wA7urjAP///wD+/v4A////AM7ArQC5posAvqySAr2skgm7 +qI0AyrymXeHZyv/c0sL66+bc+fr59/b7+vn1/Pv69fz7+vX8+/r1+/r69f38+vX29/n1M27O/g1Q +w/8akO7/Gqf9/xuk+v8WhPv/FX/7/xWB+/8Wgvr/FoH4/xWA8/8YgO//BXHl/2ml6f////7/w9/9 +/8Le/f/D3/3/w9/9/8Pf/f/B3v3/y+T+/0eI0P8GYsP/FnHR/xNz2v8UeOL/FXzq/xV+7/8VgPP/ +FYH1/xWB9f8VgfT/GIHy/wZ17P89ke3/xdz4/////v////7//f39//D2/f/l8f3/3u39/9rr/f/Z +6v3/2+v9/+Lv/f/v9v3////+//////+81vX/HHvh/wNr3v+gxfH//P7///b5/f/6+/3/+vv9//f5 +/f/8/v7/s9L2/whz5/8Xfer/FHvp/xR55f8UduD/E3LY/xVvz/8GYMD/hrPm/87n///A3fz/w9/9 +/8Pf/f/E3/3/vNv9/+fz/v/A1/P/CXHh/xd+6/8VfvH/FoD3/xWA+f8Vfvr/FoT7/xuj+v8ap/7/ +GY/t/xBTxP8hYMb/6ezx9/38+/X7+vn1/Pv69fz7+vX8+/r1+vr59f39+/Xn5N34vrao+eLZyv/T +xrOXu6mOAb2skgu9q5EDvqySALqnjADu6uMA////AP7+/gD///8AzsCtALmmiwC+rJIAvauRA72r +kQm9q5AH1su4quLZyv/d08L/39bG/eHYyPzg2Mj84NjI/ODYyPzf18j84tnI/NvUyPwwasj/D1HF +/xqQ7v8ap/7/G6T6/xaF+/8Vf/z/FYL8/xaD+/8Wg/n/FoP2/xWB8v8Wfuz/DnXi/87g9f/w9/// +vNv9/8Tf/f/D3/3/w9/9/8Le/P/I4v7/tdT2/x5uxP8NZ8f/FHHT/xN02v8UeeL/FHzp/xV/7/8V +gfP/FYL1/xWC9f8VgvT/GYPz/wt77/8Qe+z/Y6bw/8Lb+P/7+/3////+/////v////7////+//// +/v////7////+/////v/s8/z/gbbx/xB45/8UfOn/BXTo/4C28f////7///79/////f////3///79 +/////v/N4fj/DXfo/xV86P8UeuX/FHff/xNz2P8VcdL/B2PE/0GFz//K4/7/wt/9/8Pf/f/D3/3/ +w9/9/8Lf/f/E4P3////+/1eb5/8Hc+b/GIHv/xWA9P8Wgfj/FYH6/xV++/8Whfv/G6T6/xqn/v8Z +j+3/EVTF/yJgx//Szsf85NnI/N/XyPzg2Mj84NjI/ODXyPzh2Mj839bH/N/VxP/j2sr/3NPC2cGw +lxy8qpAFvauSBb2rkQG+rJIAuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAL6skgC9q5EBvauR +BLyqkAW+rZMK08azid/Wx+rg2Mj/4NfH/+DXx//g18f/4NfH/9/Xx//i2Mf/29TH/zFqyP8PUsb/ +GpDv/xqn/v8bpfr/Fob7/xWA/P8Vg/z/FoT7/xaF+v8Whfj/FYP1/xiC8P8Jduj/R5Tn/////f/X +6v3/wN79/8bh/f/F4f3/xeH9/8Lf/P/P6P//lr/r/wxjv/8SbMr/FHHT/xN02/8UeeP/FHzp/xV/ +7v8VgfL/FYL0/xWD9f8Vg/X/GIT0/xaC8/8HeO//Dnvt/zuS7/9zsPP/osn2/8Hb+P/R4/n/1+b6 +/8zh+f+v0ff/dbHz/ymI7v8Gd+3/FYDw/xeC8f8Rf/D/KYrx/0uc8v9ImvH/SJrx/0iZ8P9ImO// +SZnv/z+T7f8Ue+j/FHrl/xN34P8TdNn/FHDS/w9pyP8XasP/r9D0/8rl/v/D3/z/xeH9/8Xg/f/G +4P3/vdz9//H4///H3PT/C3Tj/xd/7f8VgvP/FoP2/xaD+f8Vgvv/FYD8/xaG/P8bpfr/Gqf+/xmP +7v8RVMb/ImHI/9LOxv/k2cf/39bH/+DXx//g18f/4NfH/+DXx//g18f/4NfH9dfMuqvCsZkgu6mP +Ab2rkga9q5EBvauRAL6skgC6p4wA7urjAP///wD+/v4A////AM/BrgC6p4wAv62TAL6skgC+rJIB +vqyTA72rkgO8qo8Aw7KaG8e4oULHt6BKx7egSse3oEvHt6BLxregTMm4n0m+s6NPJWLG5xFUx/8Z +ke/+Gqj+/xum+v8Wh/z/FYH8/xWE/P8Whvz/Fob7/xaH+v8Whfj/FYP0/xmC7/8Ec+X/mcLw//// +///L5P3/w+D+/8bi/v/F4v7/xuL+/8Lf/f/S6v//fa7i/wVgvf8Tbcv/E3HT/xN12v8UeeH/FHzo +/xV/7f8VgfH/FYLz/xWE9f8VhPb/FYT2/xiF9v8WhPT/DX7y/wV58P8Hee//DHvv/xR/7/8YgfD/ +EX7w/wh68P8FevH/EIH0/xmF9v8WhPb/FYT3/xaE9v8RgfX/Cn3z/wp88f8Ke/D/Cnru/wp57P8J +eOr/C3fn/xR65P8TeN//E3TZ/xNx0f8Tbcr/BmC9/4q35//R6v//wt/9/8bi/v/F4v7/x+L+/8Hf +/f/X6v3///79/0aU5/8Jd+j/GIPw/xWE9f8Whfn/FoX6/xWE/P8Vgfz/Fof8/xum+/8aqf7/GZDu +/hJUx/8gYMfzu7GjVsq4n0jGt6BMx7egS8e3oEvHt6BKx7egSse3oUfFtZ0ovauRALyqkQC+rZMF +vqySAL6skgC+rJIAv62TALuojQDu6uMA////AP7+/gD///8Ay72pALWhhQC6qIwAuaaLALmmiwC5 +posAuqeLAbqnjAS4pYkAuKSIALikiAC4pIgAuKSIALikiAC3pIgAu6WHAK2fjQAgX8bcE1XI/xmS +7/0aqv7/G6f7/xaI/P8Vgvz/FYb8/xaI/f8WiP3/Foj7/xaH+v8Vhff/FoTy/xN/7f8VfOX/1ub4 +//r9///G4v7/xeH+/8fi/v/G4v7/xuL+/8Tg/f/T6v//bqTd/wRfvP8Tbsr/E3LR/xN12P8Ued// +FHvl/xR/6v8VgO7/FYLx/xWD9P8VhPX/FYX2/xWF9v8Xhvb/GIf2/xiG9v8XhfX/FYT1/xSE9f8V +hfb/GIb2/xmH9/8Xhvj/Fob4/xaG+P8Whvj/FYb4/xaF9v8YhfX/F4Tz/xeD8f8Xge//F4Dr/xd+ +6P8WfOP/E3je/xN01/8TcdD/FG/K/wRgvP9vpd3/0+r//8Tg/f/G4v7/xuL+/8fi/v/E4f7/y+T+ +//////+Zw/D/BHTl/xmD7/8VhPT/Fob4/xaH+v8Wh/z/FYb8/xWC/P8WiPz/G6f7/xqq/v8Zke7+ +E1XI/yBgxu6toI0Iu6WHALekiAC4pIgAuKSIALikiAC4pIgAuKSIALiliQC6p4sEuqeMArmmiwC5 +posAuaaLALmmiwC6qIwAtqKGAO3p4QD///8A/v7+AP///wDb0MIAy72pAM/BrgDOwa0AzsGtAM7B +rQDOwa0AzsGtAM7BrgHOwa4CzsGuA87BrgPOwa4DzsGuA83BrgTRwq0DwruwCiJhyd0TVcj/GpLv +/Ruq/v8cp/v/F4n8/xaD/P8Wh/z/F4n9/xeJ/f8Xifz/F4n7/xeH+f8Vhvb/GYXy/wx76/85j+j/ +9fj8//H4///G4v7/xuL+/8jj/v/H4/7/x+P+/8Xi/f/S6v//cKbd/wZhvP8Rbcf/FXLP/xN11f8U +eNz/FHvh/xV+5/8VgOv/FYHu/xaC8P8Wg/L/FoT0/xaF9f8WhfX/FoX2/xaG9v8Whvb/Fob2/xaG +9/8Whvf/Fob3/xaG9/8Whvf/Fob3/xaG9/8Whfb/FoX0/xaD8v8WgvD/FYHt/xWA6v8Vfeb/FHvh +/xR42/8TddX/FHLP/xRvyf8EYLv/ZJ3Y/9Lq///F4v3/x+P+/8fj/v/I4/7/xuL+/8jj/v/5/f// +2uj5/xd95v8UgO3/F4Xy/xaG9/8XiPr/F4j7/xeI/f8Wh/z/FoP8/xeJ/P8cp/v/G6r+/xqR7v4T +Vcj/ImHJ7sK6sBPRwq0DzcGuBM7BrgPOwa4DzsGuA87BrgPOwa4DzsGuAs7BrQDOwa0AzsGtAM7B +rQDOwa0AzsGtAM/BrgDMvqkA8u/qAP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////Af///wDz+P8HJWbP3RJVx/8ak/D9 +G6v+/xyn+/8Xivz/FoT8/xaH/f8Xif3/F4n9/xeL/f8Xifz/F4n6/xeI+f8Whvb/Goby/wZ46f9g +pez////+/+z1/v/G4v7/xuL+/8jj/v/H4/7/xuP+/8Xi/f/T6///gLHi/w5mvf8MacP/FnLM/xNz +0f8Tdtf/FHnd/xV84v8Vf+b/FYDp/xaC7P8Wg+7/FoTw/xaF8f8WhfL/FoXz/xaG8/8WhvT/Fob0 +/xaG9P8WhvT/Fobz/xaF8/8WhfL/FoXy/xaE8P8Wg+7/FoLs/xWA6f8Vf+b/FXzi/xR63f8Ud9j/ +E3PS/xZyzf8Qa8X/B2K8/26l3P/R6f//xuL+/8bj/v/H4/7/yOP+/8Xi/v/H4/7/8vj///f5/f8/ +kun/C3vr/xmG8v8Wh/b/F4j5/xeJ+/8Xifz/F4n9/xaH/f8WhPz/F4r8/xyn+/8brP7/GpLv/hJV +x/8lZs/u8/j/Ef///wD///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A//7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A +/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD9/f4B/v7+AO/z+wclZs/dElXI/xqU8P0b +rP7/HKj7/xeK/P8Whfz/Foj9/xeK/f8Xiv3/F4v9/xeL/f8Xivz/F4r7/xeJ+f8Wh/b/Gobx/wR3 +6P98te///////+z2/v/I4/7/xeL+/8jk/v/H4/7/x+P+/8Xh/f/U7P//nsbs/yV1w/8EY73/FHDI +/xVzzv8TdNL/E3fX/xR53P8UfOD/FX7j/xV/5v8VgOj/FYLq/xaC6/8Wg+z/FoPt/xaD7f8WhO7/ +FoTu/xaD7f8Wg+3/FoPs/xWC6/8Vger/FYDo/xV/5f8UfuP/FHzg/xR53P8Ud9j/E3XT/xVzzv8V +ccn/B2W//xdsv/+Kt+X/1Ov//8bi/f/H4/7/yOP+/8nk/v/F4v7/yeT+/+/3/v////7/Yaft/wZ5 +6f8ah/L/FYf2/xeJ+f8Xivr/F4r8/xeK/f8Xiv3/Foj9/xaF/P8Xivz/HKj7/xus/v8ak+/+ElXI +/yVmz+7v8/sR/v7+AP39/gH+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A +/v7+AP7+/gD+/v4A/v7+AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PT8ByZn0N0TVsn/GpXw/Rut +/v8cqfz/F4v8/xaG/P8Wif3/F4v9/xeL/f8XjP3/F4z9/xeL/f8Xi/3/F4r7/xaJ+f8Wh/X/GYfw +/wZ66P+JvPD///////D3/v/M5v7/xeL+/8nk/v/I5P7/yOT+/8Xi/f/S6///v935/1aW0/8LZbv/ +CWfA/xRxyP8Vc83/E3TR/xN21P8UeNj/FHrb/xR73v8UfeD/FH7i/xV+4/8Vf+T/FX/k/xV/5f8V +f+X/FX/k/xV/5P8UfuL/FX7h/xR84P8UfN7/FHrb/xR42P8UdtX/FHTR/xVzzf8Vccn/C2nB/wZj +u/9CiMz/sdPz/9Ts///F4v3/yOT+/8jk/v/J5P7/xeL+/87n/v/z+f7//////3ez7/8Eeen/Gofy +/xaH9f8Xifn/F4v7/xeL+/8XjP3/F4v9/xeL/f8Wif3/Fob8/xeL/P8cqfz/G63+/xqU7/4TVsn/ +JmfQ7vD0/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A/v7/Af///wDw9PwHJmfR3RNWyv8alfD9G63+ +/xyp/P8XjPz/Fob8/xaK/f8XjP3/F4z9/xeN/f8Xjf3/F439/xeM/f8XjPz/F4v7/xaK+P8XifX/ +GIjw/wZ76P+IvPH///////f7/v/U6v7/xeL+/8nk/v/J5P7/yeT+/8bi/f/M5v//1Ov//5vE6/87 +hMn/CWW7/wdmv/8RbsX/FnPK/xV0zf8UddD/E3bS/xR31P8UeNb/FHnX/xR52f8Uetn/FHra/xR6 +2v8Uetn/FHnZ/xR51/8UeNX/FHfU/xR20v8UddD/FXTN/xVzyv8Sbsb/CGa//wdku/8xfsf/irnl +/9Do///P6P//xuL9/8nk/v/K5P7/yeT+/8Xi/v/W6v7/+vz+//////97tfD/Bnrp/xmH8f8WiPX/ +F4r5/xeL+/8XjPz/F4z8/xeN/f8XjP3/F4z9/xaK/f8Whvz/F4z8/xyp/P8brf7/GpTv/hNWyv8m +Z9Hu8PT8Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD+/v8B////APD0/AcmZ9HdE1bK/xqW8f0br/// +HKv9/xeO/v8WiP7/Fov+/xeO/v8Xjf7/F47+/xeO/v8Xjv7/F47//xeN/v8Xjf3/F438/xaM+f8X +ivb/GIny/wZ86v92s/D////////////h8P//yeX//8jk///L5v//y+b//8nl/v/I5P7/1Oz//8/o +//+WwOr/R43O/xRsv/8FY7z/CGfA/w9txf8Tccj/FXPK/xV0zP8VdM3/FXXO/xV1z/8Vdc//FXXP +/xV1zv8Vdc7/FnXN/xV0zP8Vcsr/EnDH/w5sw/8HZr//BWO8/xRsv/9Cis3/jLvn/8vl/f/W7f// +yeX//8nk/v/L5v//y+b//8fk///K5f//4/H////////9/f7/aa3v/wV86/8ZifL/For2/xaL+f8X +jfz/F439/xeN/v8Xjv//F47+/xeN/v8Xjv7/Fov+/xaI/v8Xjv7/HKv9/xuv//8alvH+E1bK/yZn +0e7w9PwR////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Bydo0t0UV8v/G5jz/Ryx//8d +rf7/GJD//xeK//8Xjf//GI7//xiP//8YkP//GJD//xiQ//8YkP//GI///xiP/v8Yj/3/GI78/xeM ++v8Xi/f/Gorz/wV97P9YpO//7fT9///////w+P//1uv//8jl///K5f//zOf//8zm///J5f7/yub/ +/9bt///U7P//sdP0/3it4P9Cis7/HnPC/wxovf8GZb3/BWW9/wZmv/8IZ8D/CWnB/wppwf8JaMH/ +CGjA/wdnwP8FZb7/BWS9/wdlvf8Qar7/JXfE/0iO0P96r+H/r9L0/9Pr///X7v//y+b//8nk/v/M +5v//zOb//8nl///I5f//1+z///L5////////5/H8/06f7v8Gfez/Gov0/xeL9/8Xjfr/GI78/xiP +/f8Yj/7/GI///xiQ//8YkP//GI///xiO//8Xjf//F4r//xiQ//8drf7/HLH//xuY8/4UV8v/J2jS +7vD1/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHJ2jS3RRXy/8cmPP9HbH//x6t +/v8YkP//F4v//xeN//8Yjv//GI///xiQ//8YkP//GJD//xiQ//8YkP//GJD//xiP/v8Yj/3/GI/8 +/xiO+v8XjPj/G4z0/wiA7v8yku3/w934////////////6PT//9Lq///J5f//yub//8zn///M5/// +yuX+/8rm///S6///2fD//9Ps//++3fn/oMju/4K04/9notv/VpfU/0SMz/89iM3/O4bM/z2Izf9C +i8//UZPS/2Ce2P9zq9//jrzo/6rP8f/E4Pv/1e3//9nw///S6///yub//8rl/v/M5///zOf//8rm +///J5v//0+r//+n1/////////////7rZ9/8rj+z/CYHu/xuM9f8XjPj/GI76/xiP/P8Yj/3/GI/+ +/xiQ//8YkP//GJD//xiQ//8Yj///GI7//xeN//8Xi///GJD//x6t/v8dsf//HJjz/hRXy/8naNLu +8PX8Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD+/v8B////APD1/AcnadPdFFjM/xyY8/0dsf//Hq3+ +/xiR//8XjP//F47//xiP//8YkP//GJH//xiR//8Ykf//GJH//xiR//8Ykf//GJH//xiQ//8YkP3/ +GJD8/xiO+/8Xjfj/G472/w+G8f8Sg+z/f7rz//P4/f///////P7//+bz///U6///yub//8nl///L +5v//zOf//8vm/v/K5f7/y+b//9Dq///V7f//2fD//9nw///X7v//1e3//9Lq///Q6f//0ur//9Xt +///W7f//2O///9nw///X7///1Oz//87p///K5v7/yuX+/8vm/v/M5///y+b//8jl///L5v//1uz/ +/+n1///+/////////+/1/f91tfL/D4Ls/xGG8f8bjfb/F435/xiP+/8YkPz/GJD9/xiQ//8Ykf// +GJH//xiR//8Ykf//GJH//xiQ//8Yj///F47//xeM//8Ykf//Hq3+/x2x//8cmPP+FFjM/ydp0+7w +9fwR////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Bydp090UWMz/HJnz/R2y//8erv7/ +GJL//xeN//8Xj///GJD//xiR//8Ykv//GJL//xiS//8Ykv//GJL//xiS//8Ykv//GJL//xiR//8Y +kf7/GJD9/xiQ+/8Xjvn/GY73/xeL9P8HgO7/NJXu/6rR9////v/////////////t9///3e///9Dp +///K5v//yeX//8rm///L5v//y+b//8vm/v/K5f7/yuX+/8rm/v/L5v7/y+f//8zn///L5///y+b+ +/8rm/v/K5f7/yuX+/8rm/v/L5v7/y+b//8vm///J5v//yOX//8rm///S6v//3/D//+/3//////// +//////z8/v+hzPb/LpLt/weB7v8YjPT/GY73/xeO+f8YkPz/GJD9/xiR/v8Ykf//GJL//xiS//8Y +kv//GJL//xiS//8Ykv//GJH//xiQ//8Xj///F43//xiS//8erv7/HbL//xyZ8/4UWMz/J2nT7vD1 +/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHKGnT3RVYzf8cmvP9HbP//x6v/v8Y +k///F47//xeQ//8Ykf//GJL//xiT//8Yk///GJP//xiT//8Yk///GJP//xiT//8Yk///GJP//xiS +//8Ykv7/GJH9/xiR/P8YkPr/GI/5/xuP9v8QiPL/CoLt/0Oe7/+v1Pf/+/z+////////////+/3/ +/+32///h8f//1+z//9Dp///L5///yub//8nm///J5v//yeb//8rm///K5v//yub//8rm///K5v// +yub//8nm///J5v//yeb//8rm///M5///0en//9nt///j8f//7/f///3+//////////////X5/v+m +z/b/P5vv/wiC7v8RiPL/G4/2/xiP+f8YkPr/GJH8/xiR/f8Ykv7/GJL//xiT//8Yk///GJP//xiT +//8Yk///GJP//xiT//8Ykv//GJH//xeQ//8Xjv//GJP//x6v/v8ds///HJrz/hVYzf8oadPu8PX8 +Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD+/v8B////APD1/AcoatTdFVnO/xya8/0ds///Hq/+/xiT +//8Xjv//F5D//xiR//8Ykv//GJP//xiT//8Yk///GJP//xiT//8Yk///GJP//xiT//8Yk///GJP/ +/xiT//8Ykv7/GJL+/xiS/f8Ykfz/GJD6/xmQ+P8aj/X/DYfx/wuE7v87mu//kMT1/93s+/////// +////////////////+fz///H4///q9f//5PP//+Dx///c7///2u7//9nt///Y7f//2O3//9nt///a +7v//3e///+Hx///m8///7Pb///P5///7/f///////////////////////9Xo+/+Fv/T/M5bv/wmD +7v8OiPL/Go/2/xmQ+P8YkPr/GJH8/xiS/f8Ykv7/GJL//xiT//8Yk///GJP//xiT//8Yk///GJP/ +/xiT//8Yk///GJP//xiS//8Ykf//F5D//xeO//8Yk///Hq/+/x2z//8cmvP+FVnO/yhq1O7w9fwR +////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Byhq1N0VWc7/HJrz/R2z//8er/7/GJT/ +/xeP//8Xkf//GJL//xiT//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP// +GJT//xiU//8YlP//GJP+/xiT/f8Ykv3/GJL7/xiR+v8akfj/GpD2/w+J8/8Hg+//G4zu/1Cl8f+U +x/X/z+X6//b5/v////////////////////////////////////////////////////////////// +///////////////////////////////////v9f3/xeD5/4nB9f9IofD/F4ru/weE7/8QivP/GpD2 +/xmR+f8Ykfr/GJL8/xiT/f8Yk/7/GJP+/xiU//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP// +GJT//xiU//8YlP//GJP//xiS//8Xkf//F4///xiU//8er/7/HbP//xya8/4VWc7/KGrU7vD1/BH/ +//8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHKGrV3RVZz/8dmvT9HrP//x+v/v8Zlf// +GJD//xiS//8Zk///GZT//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Z +lP//GZT//xmV//8ZlP//GZT//xmU/v8Zk/3/GZP9/xmS/P8Zkvr/GpL5/xyS9/8WjvX/DIjy/wiE +8P8Uie7/MZfv/1qq8v+BvvT/pM/3/8He+f/X6fv/5fD8/+32/f/v9/3/9fn+//L4/f/v9/3/6/T9 +/+Lu/P/Q5vr/utv5/5zL9v93uPP/UKXx/yuT7/8Qh+7/CITw/w6J8/8Yj/b/HJL4/xqS+f8Zkvv/ +GZP8/xmT/f8ZlP7/GZT+/xmU//8Zlf//GZX//xmU//8ZlP//GZX//xmV//8Zlf//GZX//xmV//8Z +lf//GZX//xmV//8ZlP//GZP//xiS//8YkP//GZX//x+v/v8es///HZr0/hVZz/8oatXu8PX8Ef// +/wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD+/v8B////APD1/Acpa9XdFlrP/x2b9P0etP//H7D+/xmV//8Y +kP//GJP//xmU//8ZlP//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV +//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8ZlP7/GZT+/xmU/f8Zk/z/GZP7/xmS+v8bk/n/HJP4 +/xmQ9v8SjPT/C4jy/weF8P8Jhe//Dofv/xmL7/8hj+//JZLv/yWS7/8wlu//K5Tv/yWS7/8kke// +H47v/xWK7v8Mhu//CYXv/wiG8f8MifP/E430/xqR9v8ck/j/G5P5/xmT+/8Zk/z/GZT8/xmU/f8Z +lP7/GZX+/xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV +//8Zlf//GZX//xmU//8ZlP//GJP//xiQ//8Zlf//H7D+/x60//8dm/T+FlrP/ylr1e7w9fwR//// +AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP7+/wH///8A8PX8Bylr1t0WWtD/HZz0/R61//8fsf7/GZb//xiR +//8YlP//GZX//xmV//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZX//xmV/v8Zlf7/GZX9/xmU/P8ZlPz/ +GZP7/xqT+v8bk/n/HJT4/xuT+P8akvf/F5D2/xWP9f8UjvX/FI71/xKN9P8TjvX/FI71/xWP9f8W +kPb/GJH3/xuT9/8ck/j/HJT5/xuU+v8ak/r/GZP7/xmU/P8ZlPz/GZX9/xmV/v8Zlf7/GZX//xmW +//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZX//xmV//8YlP//GJH//xmW//8fsf7/HrX//x2c9P4WWtD/KWvW7vD1/BH///8A +/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A/v7/Af///wDw9fwHKWvW3RZa0P8dnPT9HrX//x+x/v8Zlv//GJH/ +/xiU//8Zlf//GZX//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv// +GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb+/xmW/v8Z +lf7/GZX9/xmV/f8Zlfz/GZT8/xmU/P8ZlPv/GpT7/xqU+/8alPv/GpT7/xqU+/8alPv/GpT7/xmU ++/8ZlPv/GZT8/xmU/P8Zlfz/GZX9/xmV/f8Zlf7/GZb+/xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv// +GZb//xmW//8Zlf//GZX//xiU//8Ykf//GZb//x+x/v8etf//HZz0/hZa0P8pa9bu8PX8Ef///wD+ +/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD+/v8B////APD1/AcpbNfdFlvR/x2c9P0etf//H7L+/xmX//8Ykv// +GJX//xmW//8Zlv//GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Z +l///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX +//8Zl///GZf//xmX//8Zl/7/GZb+/xmW/v8Zlv7/GZb+/xmW/v8Zlv7/GZb+/xmW/v8Zlv7/GZb+ +/xmW/v8Zlv7/GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl/// +GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Z +l///GZf//xmW//8Zlv//GJX//xiS//8Zl///H7L+/x61//8dnPT+FlvR/yls1+7w9fwR////AP7+ +/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP7+/wH///8A8PX8Byls190WW9H/H5/0/R62//8fsv7/GZf//xiS//8Y +lf//GZb//xmW//8Zl///GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY +//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmX//8Zl///GZf//xmX//8Zl///GZj/ +/xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8Zl///GZf//xmX//8Zl///GZf//xmX//8ZmP// +GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmX//8Zl///GZf//xmX//8Zl///GZj//xmY//8Z +mP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY +//8Zl///GZb//xmW//8Ylf//GJL//xmX//8fs/7/HrX//x6f9P4WW9H/KWzX7vD1/BH///8A/v7/ +Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A/v7/Af///wDw9fwHKmzX3RdZ0f8hpvT9ILr//x6x/v8Zmf//GJT//xiX +//8ZmP//GZj//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn/ +/xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf// +GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Z +mf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ +//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn/ +/xmZ//8ZmP//GZj//xiX//8YlP//GZn//x6y/v8et///Iaf0/hdb0f8qbNfu8PX8Ef///wD+/v8B +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD+/v8B////APD1/AcqbdjdFlrS/yCl9P0kxv//HrP+/xiV//8Xkf//F5T/ +/xiV//8Ylf//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv// +GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Y +lv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW +//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb/ +/xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv// +GJb//xiV//8Ylf//F5T//xeR//8Ylv//HrL+/yLD//8iqvT+FlrS/ypt2O7w9fwR////AP7+/wH/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP7+/wH///8A8PX8Bypt2N0XXNL/H6D0/SbJ//8lwv7/Hqn//x2j//8dpP// +HaX//x2l//8epv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8d +pv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m +//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab/ +/x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv// +Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8e +pv//HaX//x2l//8dpP//HKL//x2n//8kv/7/J8v//yCj9P4XW9L/K23Y7vD1/BL///8A/v7/Af// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A/v7/Af///wDw9fwEKm3Z2hdb0v8emvP9Ib///yfF/f8oyP7/KMf+/ynH/v8p +x/7/Kcj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI +/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+ +/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/ +Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8q +yP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI +/v8pyP7/Kcf+/ynH/v8nx/7/KMf+/yfF/f8jxP//HZrz/hhc0/8pbNnp7/T8DP///wD+/v8B//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD+/v8A////AvD1/AAqbdm9F1zU/hx24Psesf3/IcD//ybJ//8nzf//KM///yrP +//8qz///Ks///yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD/ +/yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P// +KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q +0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ +//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//Ks// +/yrP//8qz///KM///yfN//8myv//I8P//x6y/v8dduD7FFrT/i9w2sD0+P0A/v//Av7+/wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP7+/wD///8D7/T8ACps2UkZYdb/HWDU/h1x3vseiur8H43q/CCQ6/whkuv8IZLr +/CKS6/wikuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8 +IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/wh +kuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS +6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr +/CGS6/whkuv8IZLr/CGS6/whkuv8IpLr/COT6/wikuv8IZLr/CGS6/whkuv8IZLr/CGS6/wikuv8 +IpLr/CGS6/whkuv8IZHr/B+N6vweiur8HXHe+x9i1f4QW9T/krTrSf///wD8/f8D////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A/v7/AP///wD0+P0CMHHaABJc1XMaYtb9GF7V/xha0/8YWtP/GFnT/xdZ0/8XWdP/ +F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8X +WdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ +0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT +/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/ +F1nT/xdZ0/8XWdP/F1nT/xhZ0/8XWdP/EFTS/xdZ0/8YWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8X +WdP/F1nT/xdZ0/8XWdP/GFrT/xha0/8ZX9X/FF7V/SZp2XTy9vwA////Av7+/wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A/P3/AP///wB7pOgCHWTXAC5v2jUrbtqRK27auitv2r0rb9q9K2/avitv2r4r +b9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv +2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/a +vitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+ +K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4r +b9q+K2/avitv2r4rb9q+Km7avi9x275RieG+L3Hbvipu2r4rb9q+K2/avitv2r4rb9q+K2/avitv +2r4rb9q+K2/avitv2r0rb9q9K27auixv2pEnatk1OXfdAPD1/AL///8A/v7/AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////APv9/gDv9PwC8fX9APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1 +/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8 +APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA +8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw +9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1 +/ADw9fwA8PX8APD1/ADw9fwA8vb9AP///wDy9v0A8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8 +APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADx9f0C/v7/AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8C////BP///wL///8C////Av///wL///8C//// +Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C +////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL/ +//8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av// +/wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C//// +Av///wL///8C////Av///wL///8C/v7/Av///wL///8C////Av///wL///8C////Av///wL///8C +////Av///wL///8C////Av///wL///8E////Av///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A +/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+ +/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+ +/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/ +AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A +/v7/AP7+/wD+/v8A/v7/AP7+/wD///8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+ +/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//////////// +//////////////////////////////////////////////////////////////////////////// +////////////////////+AAAAAAAAAAAAAAAAA///wAAAAAAAAAAAAAAAAAAf/wAAAAAAAAAAAAA +AAAAAD/4AAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/wAAAA +AAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAA +AAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAA +AAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAA +B+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAA +AAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfg +AAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAA +AAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAA +AAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAA +AAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAA +AAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAA +AAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAA +AAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH +4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAA +AAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AA +AAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAA +AAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAA +AAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAA +AAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAA +AAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAA +B+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAA +AAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfw +AAAAAAAAAAAAAAAAAAAH8gAAAAAAAAAAAAAAAAAAD/gAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAA +AAAAAAAf/EAAAAAAAAAAAAAAAAADf/8/4AAAAAAAAAAAAAAD/P//wAAAAAAAAAAAAAAAAAP///9A +AAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAA +AAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAA +AAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC +/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAA +AAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv// +//9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAA +AAAAAAL/////oAAAAAAAAAAAAAAF/////6AAAAAAAAAAAAAABf/////QAAAAAAAAAAAAAAv///// +6AAAAAAAAAAAAAAX//////f/////////////7//////4AAAAAAAAAAAAAB////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////8=') + #endregion + $MainForm.Margin = '8, 8, 8, 8' + $MainForm.MaximizeBox = $False + $MainForm.Name = 'MainForm' + $MainForm.StartPosition = 'CenterScreen' + $MainForm.Text = 'HeloCheck.com Mail Tr@cker v1.2' + $MainForm.add_Load($MainForm_Load) + # + # btn_About + # + #region Binary Data + $btn_About.Image = [System.Convert]::FromBase64String(' +iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJN +AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAB3RJTUUH4AIPCw8EdJPFIwAA +CXJJREFUWEe1lgdUVOkVxz8UTFQ8ag5ZFw1FekdmGAaQjgygWOjSFUFgYECai+Wo64qrRF1FbAQL +ums0FlyNrgoYUFSKlKFjW5BiQUHKqkTw5n7vDcgYTfTk5J7z933v4Xu//733K0M+J8Yp+uK/mjJ6 +bgemWC/J4ThGXPIWCHNjnYR5KQ6RVxNslv09mB941kpvfqbiRE3/sQqm37Av/q+h65ZFZlimyjmE +necsTChcv2hlSaFbUnmHc3zFqzlxlYMOsZVD9rEVqPIBB1Hpc/vomxXW4fkZpv7nnFUd0iZZiWoI +maAr+doXxGTeVuZqF/qznmdy0V6PlIongngxzBaKwWx5FZiGs+JS0fvlYuBFiIEfKQZLYRVYC+/0 +WYYVXjD2OSNQMImWU7bfwnzvs0J3/hEybfZOObe4/EDv1eV3nROqgR+BoLBKhFJRIAvmMAbEYIrw +96oGXmQ1mAurwSKi7AUn8Gqq2pztCsYBeRLCfwhjz5+IqmPG790TC9d4rhb3WkfjBz8AM3Ac28TW +wOJvm0CQXAdcCkaoaRQrXlQN8IQ1YIbiR1UNckIKTqi7pCuZhBZLSB8JbdeDZIL+RjkKd18lfm0R +ibBPwO1X1MKl4i7o6R+Eqvu/gdeGJuCgAQplFF0LZlQxEkVXw6ylBWdUnbYp6nqflhBHxR+MvycA +QObH5gd5rqrqZeAMWBpOe22C96Fp96H/9RC+wkbayQ40gBmPhorqgD9KZjE1Q0Yhebv/aLRkgorD +ZglZEoKoy8R26c/62PMmq0+UncJpjzl49VjfBK3PBhj4qzdDkHSgBbhCCTSWqh7M40ZpBVUDmqjq +0/fLCXyFyY5X0GPh6s5ZhCiIZD1X3twjwAknBWeE4OGJJukzLbMw/VfIzu2EddltYJVQD2YI5Y8A +G8A8npUFVULjiLiRJaUqDqnKRksLWQNOkVeIfdgFjntKxWM621kDw/BRsxzhXBTtNS23CVVULZhg +5jzReygDSqRqAsukf5d5Qv1bw6ALMVg8Mm6SIiFlz4AsTLi+3gnXObvUPg23ja+DyJ0PQZTRzGpP +C8TuawHvzQ+AH98IfIQzQjgjCky6CxbJrCxXsjIVFucpGAZOVXXC/UZn3v4puMMVWEahgVGlp3C6 +vCiYlp2LGful3oMXvW9hcOgdDLxlRcdZV56DXmQdGAjrwTC6AQxjGsBI1AhGsY1gHNsExnFNMEsi +kxV3gRMjfqbptt/CNhUIsQk9x6HbK49OtBE4isIl2dOec3FtO69qhMNXOuF2fR+8e8fMQSYO5T4H +3SiEx1CoBIggk3hUwj0pzcJn3LiGIQP/cxEl2AYyJ/IXb7q30/XNwEdlz066GjDCiai9pArUg6pA +JbAKYrD0NPPhOJL/AgwQzkARwkm8B9zk+2C68gEj3jeshu/NkvH/LMvbhlNwLBFE58U64sHCgKWy +rwajMDFoBlWCqn8FqAZUgnqwGGaGiEG095GUgexrXWBMS5vEQnkpCFn1EPirfwXzNai1kiuKPrNY +/RDMo4uOooHxaCA/xR5PtREDCDfBCmgHV4KybzkoLy5n4GrBVaCxtBrUl9ZA3P5WKQNHC7oZOC9F +Al3bDJbrmmH2+haYvaEFrDY8YkTHlvjMGmUVX3yKEHl54hiVm2AvklQAszcOqwI1v3KY4XUHlNCA +CmY/E0uvsaQatJbVgGZYLazIlDZwrPAlmCKcBbNA642tYLOpDWxTpWXzXRvYb2oF26Ti41iBicQm +7GKQvaj8DTVgFIpZ+9yB6Z5l8CfvO5g9wiWl1wytBu3wWtBaXgfxWW1SBn680QP8NZixBExB9t+3 +g8OWDnBIewyOEtGxPT4TbG0Hu8Qbe9DAOGIenGNFf0wYI1zJqwwU3Us/mb1uRB3oRNZD4sF2KQPH +i3qwtI8wu1aw24zgrR0wZ9sTcNr+FAQ/PAXnH54xomP6zG1HO1iLfklGAzLE0P2Qok3kzQpV7zKY +trAEFD2Gsx/uPWaPvafZ6+FS0xU2QNLhDjQgoWP89VYvWGHmNGvHPz9mIM47n4FreifMzXgOc/dI +hGPX3Z3gvqu1lxt0aO5dugwxxnCDcjNUvYrh60WlbPmxDcp+mH0glh9nPc1eB0uvj3B9XG5JR6QN +nLjdBzapLFyw4ym4IHgeAt32vYAFB7pgQWY3o/k4XpTZBR7bmyoUeWGqlqtbCOGF3iDGi3Ocdfxu +9rLlLwMlHyw/GlCTlF8bJ54u3elwlzMQNUFy9mNpA8V9YIe9dRqG72XBi7JegsehHvA43MvIHcf+ +R7rAZe11+ptv3LjJSoRozD1ANJy3TTLyv3Zexft9/1X9sfxoYHT5DXF7NcSdbuVRaQMnS/rBAbOn +ZaeZLzjQDe4HX4JXdi/4HOsHn59Y+R3vh6CstmZ99zRzfI0pP5mi5Ul44RXEwPusQMfv1nPaf8bA +qNmvgwb0JQb0sAJJH1YADdilPQHnXZ0wfx+bOYX7UujJV+B/6jUEoMJP9w3O23A9FbHj5VUdWAM0 +lG2/JV8Zesnp+15O1VhcOkiXH2MA+68ZyvafVsBlwwPY+LenkFPSI3UWlDcPwO78Xkg8/RL73I3l +7gGfH1l44JkBCD73T4i8OABBmffzpvPD1fAVCXlUaC08hj9Odiro+uafUPe7M2JAS2KALj9RZhu8 +HRxF/iDOVr1mDHgeodn/xmQdgvDoq0MQceJJrYlfujWixhAZORb6YRgEFBB1lwwlHd9/nNEIqBga +bYBWYN7Gh7DjfCekX3oBGZe7YG9uN+zP74HMgl44WNQPKed6mNk+YuD0G4i6iNmffFprEX7IBRGy +shOnsbCPxRhZQgxDbpOZgu2K2r65u7WCy/uHDdAlaIj9N6InHp50vFW459NtF7dWuy2PYQ6ufdfd +7ASkLfDHCRd+pm8w+C8P8zgBGTRzWVn56Szov4WaazqZwY+eqOmVE6gdcrtMJ7z6LTVAz3p63NIT +j48nGz1s6H5Pdz66BOeiAXdc5/7Z3e+CD7Y3u20s2qQ0W6iGnxyDfPbjnxvyX6nTi4yK42ZlrcUX +YvSX3c6bJRR3clc0DplhBeiRSk81erA4p7Xj9trxzn1XW6/HjnuVruuKtup7pJmTsVPHuxR8ZMJ9 +ScjKTaAXma85oVM1F2RaGAaej+CGXdtmHnPrqFVC6Snb5NLjdsk399jEXU3mBmfPm8FfrkqI3O+Y +l/+PMRarOp6MnSyPM3oi3o9DyTB/+aIg5F+LXwTsD9KU1QAAAABJRU5ErkJggg==') + #endregion + $btn_About.Location = '584, 422' + $btn_About.Margin = '4, 4, 4, 4' + $btn_About.Name = 'btn_About' + $btn_About.Size = '80, 65' + $btn_About.TabIndex = 24 + $btn_About.UseVisualStyleBackColor = $True + $btn_About.add_Click($btn_About_Click) + # + # btn_Csv + # + #region Binary Data + $btn_Csv.Image = [System.Convert]::FromBase64String(' +iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJN +AAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsMAAALDAE/QCLI +AAAAB3RJTUUH4AIPCg0kfAntXgAAAsVJREFUWEfd10nITWEYwPFrHrIwZChlypQVNhaUFSmlSDay +sFFS5nFhnjOkLMiOlEKiWNjYIFkQIkOGQplCMpfp///cp27X9957zzl9lKd+nc77nuE573mHc0o1 +ogfW4BF+4GeDnmAuOiB3dMNBfENzN6nnPdahE3LFCnyHF7uBnVhbx2bcRSTxtVyWOQmb/gq8yFUM +Q6OxHJFA7iRG4AW8gE+WJWahur9kTmI03sKTF1mQIWYgXl3uJFoiATWcREsloC+wn7RGMookMB31 +hu5LjEUyiiQwECfg6HEkBYdyXFPrkYwiCRi+465VumMq3sDr7kYyiiaQij64g3+WQG/cxh8JTIGL +jnO32wP4DA88i9Wwzve2qWwDnKTcum+d+3GM5XNg80ckE3gHC8NjnClzZassP4YjuFcus0lNMmbO +5+X9B3A0zEREMoG4gVPoYQxGWzhWV8G66xiJNjBmw/I9sMyt+9vQDofK+5WvsG4C19AXA7AL+3EB +1sWasBgm6VNafhGd4QT0EZPRE7dgfaYEdjTtlUp7EWVhO4wJ8OZOq5a/xigMh8n0w0REH8qUgE/X +CqcrysJTTIPN6xNvREy589AFy9ARWxHnZUpgS9Pe75aIskofcBS+pkGIDmpZezjO7fW2RJyTKYHL +6AUv5BeRQ+kcrHN6XQlfT3/Y5M9g3X3Y9MYYVE65mRKwSf0OHAo/KG1Oe7V1zgM2vyPDJPchzrM/ +uAg5Gkw8ytVQAq8QJzgUfd/2/kuwk1nuHH4ep3AT1SveQ1gfrSKXXkdHRDKB8ZgPO+FCWPkJHngS +C7AEdjLX8qXwWJ8uRL1b96335nbOiGQC1fHX14Lq+K8TaGg5LvJZXiuGwA7udZ2sklHkxyQVTlQu +9Q51+f9QM/L8mqX4jXAczqRez4dzNq0ZRX9OU/zTnoSGIu/veXN8ej9wxqEiSqVfFKuTr8uVMRUA +AAAASUVORK5CYII=') + #endregion + $btn_Csv.Location = '496, 422' + $btn_Csv.Margin = '4, 4, 4, 4' + $btn_Csv.Name = 'btn_Csv' + $btn_Csv.Size = '80, 65' + $btn_Csv.TabIndex = 23 + $tooltip1.SetToolTip($btn_Csv, 'Export Results to CSV') + $btn_Csv.UseVisualStyleBackColor = $True + $btn_Csv.add_Click($btn_Csv_Click) + # + # btn_Quit + # + #region Binary Data + $btn_Quit.Image = [System.Convert]::FromBase64String(' +iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAABqZJREFU +WEe9l3tsU3Ubx4+BF2OMeTUB3sT4j5o3GKPGEMUXgW1t18t6Ob33tKdnvdF17KaE5FWE6djYRTYY +uwC7gXMwhA02GJvswsYYg+kGY8B8B4MZX10cipcYjfKPydfntNJqoHoSg0/ySdP2l/P9nOd36Snz +N9d9H4926X6avfBz438TQO8XRD6+93Xfud59T3w1NXjpm6kB3Loxib2vJYkCj0S+vrc1b+5S76Fv +r5/GZ6frcbGex4+zE9j3ukwUWBwZEr/m78/VdLUXaHF0sw4dEjlC41tyk3GuZRNuTvbi89EDmGzO +xkS9GxN1Lvzw2Tje26CQJPDQ4U0peP4lGZatkOOlldJ48WUZnl66Ardm+jHdvgHjFHxhtzfMOHXg ++09GcSBXKUlgURsJLE9QQiZXQq6QRpIsGS8sT8L1vT5cbPDgQoMY7g+/nq/l8d3MWbS+qZIksPhI +nhYrElVQJKugVEpDoVBRJxS41hygYB/ON/xKvRdjNS58M30Kh95SSxM4mqdDglwDpUoDtVoaSqWG +uqbC1X1+nKe2j4nBInUefLjLha/+dwJt+SnSBI7lGiBL1kGt0SIlRRri2JUyDa40+XCOpmCUgsPU +puKDnU58cfk4jtJCFa8fibmz5mvKmHZVJYNjGwyQK1loUvTQaqUhjl0l1+KjRm8ktOZXdgk4W81h +buIYOgr1cQXmJRcx7UWHGSi2k8B6ltpvoguz0OsIveFP0eoMSKSuTb5DLa8RMELBYXa6cabagdmx +NnQWGe4qME9RyLTntTK4MvsGEssYdKwzQ63goFfbwWptYA1msKzxD9EbjJCpWFzaIwZTqBhMDO/g +cbrSgU9HWnC8xHiHQCT8EIMz0zymZvOxfDODhC0M5NsYiNOhf2MRWJ0DRtYCo9EcFwNrhkJjom0n +hINP73BjaIeAwSoBJ7c78fHwfnRvMf1OYL6yiBnKO8JgZMaHmlPUgblqXLphwOSXRnx0U4+57+vD +EiatC2aTHWazNS5GkxVKrQXjdXTX1TyGqngMVgj4tGUFfup+HNOD76Kv1BIV+EdKKfN1fieDD/4f +QP0Ig7qzDGrPEMMRaoirN4uhKGdg0blhtThgtdriYrbYoDFYw3t+qMqFk5U8+ssF3Opdgrp1WkwP +7MGJrdaowMOaUgbdM8+gafx+1I0yqCfEV5HaD0mAGL+RBQVNhZX1wGZzwmZ3xMVicyDF6MDoLidO +VToxUOFC3zYS6FuC5o1GXOmpwUC5PSqwUJ7LnMrvYdB4mUHDBRIYj9AwEaGeGJ4zI5k6YDf64LDz +cDiccbHZndCZnLTnOZys4HCC5r1nK/0K9ixBe4ENl7uq6POYwEPEcwmvM0Ob+hi8O8Vg92SExqsM +mq5FGLj5ItS0BjiLHxzHg3O64mLnXNBbXOEt11/uoLt3oLvMiU8OvoxbPU9ioqMcQ5VcVGA+sZBY +Kkrk95PEdQqdYZBUSDuApieZWq+uoAX45qNwcT64eIFwx4VzuWG0CxiutOHENjt6t9rRU0YSpRx2 +rVVivK0EZ6qcUQGx5hERideoE7QL9n9OBxGFt2ay4KwZ4O1pcHMBuHkv3O7UP8TFp8Ls8GCowkrh +Nrr7GHs3pmCstQBnd/K/ExArKpFInSiihaekk/BgtgYcH4Rb8ENI9RHeP4UXvLA6fRgst6Cn1Irj +W2K0FRgwsj+XfpTcdwiIFZWQbWSGVVUMDmSr4UpNQ6rXD49EBI8fdvdqDGw1UagFXW/H6Cw2Yrhp +PcZqhbsKiHVb4jlC2ZyTDLc3BK8/AJ9EvL4AuNQg+suMeP9tMzpLYnQUsRjcvY6eDTxxBcQSF6a4 +O/7dmCOD4FsDXyAIv0S8/iBcnhB6S1kcKzGhozjGkUID+uqyww8qdP24Ardr8Z7sRHhWZyIQTMfq +YEgSAcLtz0B3iQFHC40UGkNcAz07QpjY45cmsDtrFXyhLARDa5AWSpdEkEhdnYHjxfSEvJlF+29o +y9ejc3sAlxsDUgVWwp+eg7T0TITSMyQhjvUGs9FVpAuHHi6IcWiTHh1lbkw2BaUJNGX/B6GstcjI +ykGmRNZk5iC45lW8X6wlAT0FG9CaH6GFBA4Xc5jaF4oKLBAEAXeDF+hQoYOFp5OP53k6hKQhjuVc +Atamcdj8ihWNG1m05unRQhx8S4eD+SZcbU6PCjxIPEE8exeWEqsIxV9l4T8f0K/nl9VVvyq/2Jyr +w7X3MmIdIMQ/ieKbe8W/iMeIp4hlRBIh3tzf8uf0dokH3APEw8QigsKZBb8AruUp9dfuU5MAAAAA +SUVORK5CYII=') + #endregion + $btn_Quit.Location = '672, 422' + $btn_Quit.Margin = '4, 4, 4, 4' + $btn_Quit.Name = 'btn_Quit' + $btn_Quit.Size = '80, 65' + $btn_Quit.TabIndex = 25 + $btn_Quit.UseVisualStyleBackColor = $True + $btn_Quit.add_Click($btn_Quit_Click) + # + # btn_Track + # + #region Binary Data + $btn_Track.Image = [System.Convert]::FromBase64String(' +iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJN +AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAB3RJTUUH4AIHEC8RWQ1hFAAA +BuRJREFUWEe1lWtMHOcVhmcolSpFatMfTaWmVZU/vahS1biOIlVVXGJZqavWlh27cgWphVMZtapo +kiZK5VqyWqeRHRPSWHGrZhQbm4svGFhsWFhgF/bOrpddDCy77AJmF5b7ApUq9SJVb98zzJgBgxtT +9UiPvtHMOec93/nOzCim5fN5k2+RqqWlJRtp2g737t1rPnnyZBnTfqKoqGhV4L+ZiK+srMhawSTY +LsvLy5icnER5eXl+9+7dpbt27SrYs2ePofIQkwIWFhZUrhrBdlkiU1NTOHHiBA4dOpQ/ePDgsf37 +9xccOHDAUNrCFhcXlVwut76ApRWy/IisIDc9gzNnzuL48eM4evRovqSk5NiRI0cKiouLDbVNjLvX +C2Ah2uJiHotzOSwlWrCctJHmj80KmR1owPnKt/H6G2+i/FevoKysLF9aWnps586dBfv27TMUN5gU +wLNbLSC/hIXxMObrirBweccjM1e1A63vvoh3f/cKzp0qx9m3T7MjZ/KXLl0qEa10Oq1rrrP5+XmF +Z6eyEG2BHZhL+ZH54GuYOPcEJio+/0hkyL2KLyBx7ikkzz+NzGAPspNTMhtNHPTCubk5Q9ViclM6 +wEK0+YVFzCR9SFU8hZHTn8HIW49vj9OfRpo5Zkf8mF/Ig7ltpFA2+4DNzs7qM8BVm5ubx2wmgbGb +P0f68otIXzm8PRgrOSSX5OQmbWTzDszMzCgTExMqV42AhWBW1pnc/8hqLslJbKSQGKoW4+6VbDar +Tk9PawT/J2ykkBiqFuOAKGNjYypXTT4kG8lks8hk1sjya7fRR76AG9ngYyOFxFC1GHevjI6OqgzS +NiboH4yj1mZHTVOrTnVjC+xOj16U+DCWRWX01bwWzByWaxspJIaqxXj+egF01swEJqn0KELRfoT6 +1pCiTDHGgj8gjIyMIB6P32d4eBjMqfuJD31tvC4khqrFmEA+ECpXTZJZSaZSFI2tozcSQzASxdBw +AozD0NCQLij+nCe9czzSdffHx8f1DshmHzA6K9yBylWTQBMGIRK7y9a36K23UsvjCPdFMTg4qAuJ +qPxD5I/ID47+d5QODQwMIJFISDdaotHoJ1mQoWox+TzSSeWqyY5MEskk4tzlYHz4QYbievJoLMb2 +p/iazfJHNI0pdmCar538U6QgEe/v70cymRxgsU/w2lC1GB8qfKiyC5qcpTDEc7x+qw1V9c24XH9r +HXLP1taJvr4+9IbCcAfD8IX74A2RcJTXMcSGEixiUZ+DSCQixc7cvXv36+yCoWoxaUssFlNZrSYV +C3GeXYjnHAhHNqU3fAehUEhngN1IclhH0mNIjY4jPTaO8YmM/hGSzYgPi8iRr4TDYUPVYjxDhZWp +XDWZYDnTtWmOI8FiNiLtDwQC8Pv9+hxked4y8fLKyTzIMMq1dEl86BsmnyWGqsXYGuXOnTsqk2qS +2EpfNAa3P7iGL4AA284YvQC32w2fz6dPu7xuZhHmAHo8HuJGMBBoTo8kC2PRPkPVYmy/EgwGVQ6I +JgNjxe0P3D934dKNZjS0doAxYNHo7u6G0+lET08P2F69MIljPrhcLnS7nLA5fbgZSrRp3tSnKp1J +Q9ViPBuFu1DZLk1atpHIJpjPAgE/Ojs70d7evilVNgd+Vu3DL+2jK791514SveL6lK573zgkClul +cgea7MJE36HHi7qm26hr3AI+c3S59C6IoN1u1+lwOFBzy4G9lU343vkOHKgK4VXH+ORvunPPvx// +q/Kj932GOk0GgwlUtk3r7e2FSYh4fH60dLhw+yGIj/jKTPg5DzJ0wWAATq8fJRea8MyZJuz+oAuH +ayJ4oyvT/2vn5DdOBSy/Za/Xq/AsVR6DJgMl8B7aOrrQ6uiEnevDEJ+W9lVajbW9y6kX0+Bw4YeV +9Xj2HRv2XHCh+Fo/3nRl28od2S8a8qsFEJVoIiz0cLrr+Ak2h+9Rud5s198Av8+Li7c6UXT2Br5T +cRsv/LkHpTeH/v1qx8RHhrwi5684HA4p4kOzAEFa6edbsB18jDXz+FjEO9ft8ztOX1v+bmUr9v7F +gx/XxpZ08ZqaGuXixYsKACnkNQb8jfyD/HMNz7+8HgO53or7z62xeq6/83tx9punrrz27bdu5J97 +rw17P/QOi/jj5FlymJTV1dX9vrGxsZUESNRgqKGhYZSMfUxGJcYSL7nar129+oeqK9W/eP1C7R9f +Ol//0U/+1PK8FPAl8jJ5j1wl9urqajcJkhhJkCRJkTTJktwWyDPxEV+JkVjJIbnckls0rtbWVFyv +rT7aUHflSSmggDxGPke+TL5KnibPkOfIC+T7Bj8gR8hPt0CeiY/pL7GSQ3JJTsktGqJFzZqC/wBJ +muX/OpNzaAAAAABJRU5ErkJggg==') + #endregion + $btn_Track.Location = '408, 422' + $btn_Track.Margin = '4, 4, 4, 4' + $btn_Track.Name = 'btn_Track' + $btn_Track.Size = '80, 65' + $btn_Track.TabIndex = 22 + $tooltip1.SetToolTip($btn_Track, 'Start Message Tracking') + $btn_Track.UseVisualStyleBackColor = $True + $btn_Track.add_Click($btn_Track_Click) + # + # grp_Recipient + # + $grp_Recipient.Controls.Add($txt_ResultsLimit) + $grp_Recipient.Controls.Add($lblResultLimit) + $grp_Recipient.Controls.Add($cmb_Sender) + $grp_Recipient.Controls.Add($cmb_Recipient) + $grp_Recipient.Controls.Add($chk_Detail) + $grp_Recipient.Controls.Add($btn_Sender) + $grp_Recipient.Controls.Add($chk_endDate) + $grp_Recipient.Controls.Add($chk_StartDate) + $grp_Recipient.Controls.Add($btn_Recipient) + $grp_Recipient.Controls.Add($date_end) + $grp_Recipient.Controls.Add($labelEnd) + $grp_Recipient.Controls.Add($lbl_Start) + $grp_Recipient.Controls.Add($date_start) + $grp_Recipient.Controls.Add($txt_reference) + $grp_Recipient.Controls.Add($chk_reference) + $grp_Recipient.Controls.Add($lbl_Reference) + $grp_Recipient.Controls.Add($txt_Subject) + $grp_Recipient.Controls.Add($chk_subject) + $grp_Recipient.Controls.Add($lbl_subject) + $grp_Recipient.Controls.Add($txt_InternalId) + $grp_Recipient.Controls.Add($chk_InternalId) + $grp_Recipient.Controls.Add($lbl_internalMessageId) + $grp_Recipient.Controls.Add($txt_MessageId) + $grp_Recipient.Controls.Add($chk_messageId) + $grp_Recipient.Controls.Add($lbl_MessageId) + $grp_Recipient.Controls.Add($cmb_eventID) + $grp_Recipient.Controls.Add($chk_EventId) + $grp_Recipient.Controls.Add($lbl_eventId) + $grp_Recipient.Controls.Add($txt_Server) + $grp_Recipient.Controls.Add($chk_Server) + $grp_Recipient.Controls.Add($lbl_server) + $grp_Recipient.Controls.Add($chk_Sender) + $grp_Recipient.Controls.Add($lbl_Sender) + $grp_Recipient.Controls.Add($chk_recipient) + $grp_Recipient.Controls.Add($lbl_recipient) + $grp_Recipient.Location = '4, 16' + $grp_Recipient.Margin = '4, 4, 4, 4' + $grp_Recipient.Name = 'grp_Recipient' + $grp_Recipient.Padding = '4, 4, 4, 4' + $grp_Recipient.Size = '748, 400' + $grp_Recipient.TabIndex = 999 + $grp_Recipient.TabStop = $False + # + # txt_ResultsLimit + # + $txt_ResultsLimit.Location = '485, 365' + $txt_ResultsLimit.Margin = '5, 5, 5, 5' + $txt_ResultsLimit.Name = 'txt_ResultsLimit' + $txt_ResultsLimit.Size = '78, 23' + $txt_ResultsLimit.TabIndex = 33 + $txt_ResultsLimit.Text = '100' + $txt_ResultsLimit.TextAlign = 'Center' + $tooltip1.SetToolTip($txt_ResultsLimit, 'Limits the number of results returned. Set to 0 for unlimited') + # + # lblResultLimit + # + $lblResultLimit.Location = '368, 369' + $lblResultLimit.Margin = '5, 0, 5, 0' + $lblResultLimit.Name = 'lblResultLimit' + $lblResultLimit.Size = '189, 25' + $lblResultLimit.TabIndex = 32 + $lblResultLimit.Text = 'Limit Results to:' + # + # cmb_Sender + # + $cmb_Sender.Enabled = $False + $cmb_Sender.FormattingEnabled = $True + $cmb_Sender.Location = '254, 51' + $cmb_Sender.Margin = '4, 4, 4, 4' + $cmb_Sender.Name = 'cmb_Sender' + $cmb_Sender.Size = '309, 25' + $cmb_Sender.TabIndex = 4 + $cmb_Sender.add_TextChanged($cmb_Sender_TextChanged) + $cmb_Sender.add_Leave($cmb_Sender_Leave) + # + # cmb_Recipient + # + $cmb_Recipient.Enabled = $False + $cmb_Recipient.FormattingEnabled = $True + $cmb_Recipient.Location = '254, 16' + $cmb_Recipient.Margin = '4, 4, 4, 4' + $cmb_Recipient.Name = 'cmb_Recipient' + $cmb_Recipient.Size = '309, 25' + $cmb_Recipient.TabIndex = 1 + $cmb_Recipient.add_TextChanged($cmb_Recipient_TextChanged) + $cmb_Recipient.add_Leave($cmb_Recipient_Leave) + # + # chk_Detail + # + $chk_Detail.Location = '214, 362' + $chk_Detail.Margin = '4, 4, 4, 4' + $chk_Detail.Name = 'chk_Detail' + $chk_Detail.Size = '165, 31' + $chk_Detail.TabIndex = 21 + $chk_Detail.Text = ' Detailed Output' + $tooltip1.SetToolTip($chk_Detail, 'Displays All Tracking Fields') + $chk_Detail.UseVisualStyleBackColor = $True + # + # btn_Sender + # + $btn_Sender.Location = '574, 51' + $btn_Sender.Margin = '4, 4, 4, 4' + $btn_Sender.Name = 'btn_Sender' + $btn_Sender.Size = '161, 26' + $btn_Sender.TabIndex = 5 + $btn_Sender.Text = 'Resolve Sender' + $btn_Sender.UseVisualStyleBackColor = $True + $btn_Sender.add_Click($btn_Sender_Click) + # + # chk_endDate + # + $chk_endDate.Location = '214, 323' + $chk_endDate.Margin = '4, 4, 4, 4' + $chk_endDate.Name = 'chk_endDate' + $chk_endDate.Size = '30, 29' + $chk_endDate.TabIndex = 19 + $chk_endDate.UseVisualStyleBackColor = $True + $chk_endDate.add_CheckStateChanged($chk_endDate_CheckStateChanged) + # + # chk_StartDate + # + $chk_StartDate.Location = '214, 288' + $chk_StartDate.Margin = '4, 4, 4, 4' + $chk_StartDate.Name = 'chk_StartDate' + $chk_StartDate.Size = '30, 29' + $chk_StartDate.TabIndex = 17 + $chk_StartDate.UseVisualStyleBackColor = $True + $chk_StartDate.add_CheckStateChanged($chk_StartDate_CheckStateChanged) + # + # btn_Recipient + # + $btn_Recipient.Location = '574, 17' + $btn_Recipient.Margin = '4, 4, 4, 4' + $btn_Recipient.Name = 'btn_Recipient' + $btn_Recipient.Size = '161, 26' + $btn_Recipient.TabIndex = 2 + $btn_Recipient.Text = 'Resolve Recipient' + $btn_Recipient.UseVisualStyleBackColor = $True + $btn_Recipient.add_Click($btn_Recipient_Click) + # + # date_end + # + $date_end.CustomFormat = 'dddd, d. MMMM yyyy HH:mm' + $date_end.Enabled = $False + $date_end.Format = 'Custom' + $date_end.Location = '254, 326' + $date_end.Margin = '5, 5, 5, 5' + $date_end.Name = 'date_end' + $date_end.Size = '309, 23' + $date_end.TabIndex = 20 + # + # labelEnd + # + $labelEnd.Location = '8, 328' + $labelEnd.Margin = '5, 0, 5, 0' + $labelEnd.Name = 'labelEnd' + $labelEnd.Size = '265, 34' + $labelEnd.TabIndex = 31 + $labelEnd.Text = 'End' + # + # lbl_Start + # + $lbl_Start.Location = '8, 294' + $lbl_Start.Margin = '5, 0, 5, 0' + $lbl_Start.Name = 'lbl_Start' + $lbl_Start.Size = '185, 34' + $lbl_Start.TabIndex = 28 + $lbl_Start.Text = 'Start' + # + # date_start + # + $date_start.CustomFormat = 'dddd, d. MMMM yyyy HH:mm' + $date_start.Enabled = $False + $date_start.Format = 'Custom' + $date_start.Location = '254, 290' + $date_start.Margin = '5, 5, 5, 5' + $date_start.Name = 'date_start' + $date_start.Size = '309, 23' + $date_start.TabIndex = 18 + # + # txt_reference + # + $txt_reference.Enabled = $False + $txt_reference.Location = '254, 256' + $txt_reference.Margin = '5, 5, 5, 5' + $txt_reference.Name = 'txt_reference' + $txt_reference.Size = '309, 23' + $txt_reference.TabIndex = 23 + # + # chk_reference + # + $chk_reference.Location = '214, 254' + $chk_reference.Margin = '4, 4, 4, 4' + $chk_reference.Name = 'chk_reference' + $chk_reference.Size = '30, 29' + $chk_reference.TabIndex = 16 + $chk_reference.UseVisualStyleBackColor = $True + $chk_reference.add_CheckStateChanged($chk_reference_CheckStateChanged) + # + # lbl_Reference + # + $lbl_Reference.Location = '8, 260' + $lbl_Reference.Margin = '5, 0, 5, 0' + $lbl_Reference.Name = 'lbl_Reference' + $lbl_Reference.Size = '265, 34' + $lbl_Reference.TabIndex = 25 + $lbl_Reference.Text = 'Reference' + # + # txt_Subject + # + $txt_Subject.Enabled = $False + $txt_Subject.Location = '254, 222' + $txt_Subject.Margin = '5, 5, 5, 5' + $txt_Subject.Name = 'txt_Subject' + $txt_Subject.Size = '309, 23' + $txt_Subject.TabIndex = 15 + # + # chk_subject + # + $chk_subject.Location = '214, 220' + $chk_subject.Margin = '4, 4, 4, 4' + $chk_subject.Name = 'chk_subject' + $chk_subject.Size = '30, 29' + $chk_subject.TabIndex = 14 + $chk_subject.UseVisualStyleBackColor = $True + $chk_subject.add_CheckStateChanged($chk_subject_CheckStateChanged) + # + # lbl_subject + # + $lbl_subject.Location = '8, 226' + $lbl_subject.Margin = '5, 0, 5, 0' + $lbl_subject.Name = 'lbl_subject' + $lbl_subject.Size = '265, 34' + $lbl_subject.TabIndex = 22 + $lbl_subject.Text = 'Subject' + # + # txt_InternalId + # + $txt_InternalId.Enabled = $False + $txt_InternalId.Location = '254, 188' + $txt_InternalId.Margin = '5, 5, 5, 5' + $txt_InternalId.Name = 'txt_InternalId' + $txt_InternalId.Size = '309, 23' + $txt_InternalId.TabIndex = 13 + # + # chk_InternalId + # + $chk_InternalId.Location = '214, 186' + $chk_InternalId.Margin = '4, 4, 4, 4' + $chk_InternalId.Name = 'chk_InternalId' + $chk_InternalId.Size = '30, 29' + $chk_InternalId.TabIndex = 12 + $chk_InternalId.UseVisualStyleBackColor = $True + $chk_InternalId.add_CheckStateChanged($chk_InternalId_CheckStateChanged) + # + # lbl_internalMessageId + # + $lbl_internalMessageId.Location = '8, 192' + $lbl_internalMessageId.Margin = '5, 0, 5, 0' + $lbl_internalMessageId.Name = 'lbl_internalMessageId' + $lbl_internalMessageId.Size = '265, 34' + $lbl_internalMessageId.TabIndex = 19 + $lbl_internalMessageId.Text = 'Internal MessageID' + # + # txt_MessageId + # + $txt_MessageId.Enabled = $False + $txt_MessageId.Location = '254, 154' + $txt_MessageId.Margin = '5, 5, 5, 5' + $txt_MessageId.Name = 'txt_MessageId' + $txt_MessageId.Size = '309, 23' + $txt_MessageId.TabIndex = 11 + # + # chk_messageId + # + $chk_messageId.Location = '214, 152' + $chk_messageId.Margin = '4, 4, 4, 4' + $chk_messageId.Name = 'chk_messageId' + $chk_messageId.Size = '30, 29' + $chk_messageId.TabIndex = 10 + $chk_messageId.UseVisualStyleBackColor = $True + $chk_messageId.add_CheckStateChanged($chk_messageId_CheckStateChanged) + # + # lbl_MessageId + # + $lbl_MessageId.Location = '8, 158' + $lbl_MessageId.Margin = '5, 0, 5, 0' + $lbl_MessageId.Name = 'lbl_MessageId' + $lbl_MessageId.Size = '142, 34' + $lbl_MessageId.TabIndex = 16 + $lbl_MessageId.Text = 'MessageID' + # + # cmb_eventID + # + $cmb_eventID.Enabled = $False + $cmb_eventID.FormattingEnabled = $True + $cmb_eventID.Location = '254, 119' + $cmb_eventID.Margin = '4, 4, 4, 4' + $cmb_eventID.Name = 'cmb_eventID' + $cmb_eventID.Size = '309, 25' + $cmb_eventID.TabIndex = 9 + # + # chk_EventId + # + $chk_EventId.Location = '214, 116' + $chk_EventId.Margin = '4, 4, 4, 4' + $chk_EventId.Name = 'chk_EventId' + $chk_EventId.Size = '30, 29' + $chk_EventId.TabIndex = 8 + $chk_EventId.UseVisualStyleBackColor = $True + $chk_EventId.add_CheckStateChanged($chk_EventId_CheckStateChanged) + # + # lbl_eventId + # + $lbl_eventId.Location = '8, 123' + $lbl_eventId.Margin = '5, 0, 5, 0' + $lbl_eventId.Name = 'lbl_eventId' + $lbl_eventId.Size = '112, 34' + $lbl_eventId.TabIndex = 12 + $lbl_eventId.Text = 'EventID' + # + # txt_Server + # + $txt_Server.Enabled = $False + $txt_Server.Location = '254, 85' + $txt_Server.Margin = '5, 5, 5, 5' + $txt_Server.Name = 'txt_Server' + $txt_Server.Size = '309, 23' + $txt_Server.TabIndex = 7 + $txt_Server.add_Leave($txt_Server_Leave) + # + # chk_Server + # + $chk_Server.Location = '214, 80' + $chk_Server.Margin = '4, 4, 4, 4' + $chk_Server.Name = 'chk_Server' + $chk_Server.Size = '31, 29' + $chk_Server.TabIndex = 6 + $chk_Server.UseVisualStyleBackColor = $True + $chk_Server.add_CheckStateChanged($chk_server_CheckStateChanged) + # + # lbl_server + # + $lbl_server.Location = '8, 89' + $lbl_server.Margin = '5, 0, 5, 0' + $lbl_server.Name = 'lbl_server' + $lbl_server.Size = '112, 34' + $lbl_server.TabIndex = 9 + $lbl_server.Text = 'Server' + # + # chk_Sender + # + $chk_Sender.Location = '214, 48' + $chk_Sender.Margin = '4, 4, 4, 4' + $chk_Sender.Name = 'chk_Sender' + $chk_Sender.Size = '30, 29' + $chk_Sender.TabIndex = 3 + $chk_Sender.UseVisualStyleBackColor = $True + $chk_Sender.add_CheckStateChanged($chk_sender_CheckStateChanged) + # + # lbl_Sender + # + $lbl_Sender.Location = '8, 55' + $lbl_Sender.Margin = '5, 0, 5, 0' + $lbl_Sender.Name = 'lbl_Sender' + $lbl_Sender.Size = '112, 34' + $lbl_Sender.TabIndex = 6 + $lbl_Sender.Text = 'Sender' + # + # chk_recipient + # + $chk_recipient.Location = '214, 14' + $chk_recipient.Margin = '4, 4, 4, 4' + $chk_recipient.Name = 'chk_recipient' + $chk_recipient.Size = '31, 29' + $chk_recipient.TabIndex = 0 + $chk_recipient.UseVisualStyleBackColor = $True + $chk_recipient.add_CheckStateChanged($chk_recipient_CheckStateChanged) + # + # lbl_recipient + # + $lbl_recipient.Location = '8, 21' + $lbl_recipient.Margin = '5, 0, 5, 0' + $lbl_recipient.Name = 'lbl_recipient' + $lbl_recipient.Size = '112, 34' + $lbl_recipient.TabIndex = 3 + $lbl_recipient.Text = 'Recipient' + # + # tooltip1 + # + $grp_Recipient.ResumeLayout() + $MainForm.ResumeLayout() + #endregion Generated Form Code + + #---------------------------------------------- + + #Save the initial state of the form + $InitialFormWindowState = $MainForm.WindowState + #Init the OnLoad event to correct the initial state of the form + $MainForm.add_Load($Form_StateCorrection_Load) + #Clean up the control events + $MainForm.add_FormClosed($Form_Cleanup_FormClosed) + #Store the control values when form is closing + $MainForm.add_Closing($Form_StoreValues_Closing) + #Show the Form + return $MainForm.ShowDialog() + +} +#endregion Source: MainForm.psf + +#region Source: Globals.ps1 +#-------------------------------------------- +# Declare Global Variables and Functions here +#-------------------------------------------- + + +#Sample function that provides the location of the script +function Get-ScriptDirectory +{ + <# + .SYNOPSIS + Get-ScriptDirectory returns the proper location of the script. + + .OUTPUTS + System.String + + .NOTES + Returns the correct path within a packaged executable. + #> + [OutputType([string])] + param () + if ($hostinvocation -ne $null) + { + Split-Path $hostinvocation.MyCommand.path + } + else + { + Split-Path $script:MyInvocation.MyCommand.Path + } +} + +#Sample variable that provides the location of the script +[string]$ScriptDirectory = Get-ScriptDirectory + + + +#endregion Source: Globals.ps1 + +#region Source: About.psf +function Show-About_psf +{ + #---------------------------------------------- + #region Import the Assemblies + #---------------------------------------------- + [void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') + [void][reflection.assembly]::Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089') + [void][reflection.assembly]::Load('System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + [void][reflection.assembly]::Load('System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + [void][reflection.assembly]::Load('System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a') + #endregion Import Assemblies + + #---------------------------------------------- + #region Generated Form Objects + #---------------------------------------------- + [System.Windows.Forms.Application]::EnableVisualStyles() + $form_About = New-Object 'System.Windows.Forms.Form' + $lbl_LinkedIn = New-Object 'System.Windows.Forms.LinkLabel' + $labelLinkedIn = New-Object 'System.Windows.Forms.Label' + $lbl_BlogUrl = New-Object 'System.Windows.Forms.LinkLabel' + $labelBlog = New-Object 'System.Windows.Forms.Label' + $lbl_Twitter = New-Object 'System.Windows.Forms.LinkLabel' + $labelTwitter = New-Object 'System.Windows.Forms.Label' + $labelEmail = New-Object 'System.Windows.Forms.Label' + $labelLbl_AppAuthor = New-Object 'System.Windows.Forms.Label' + $lbl_CreatedWith = New-Object 'System.Windows.Forms.Label' + $labelLbl_AppUpdate = New-Object 'System.Windows.Forms.Label' + $lbl_AppAbout = New-Object 'System.Windows.Forms.Label' + $linklabelEmail = New-Object 'System.Windows.Forms.LinkLabel' + $btn_Ok = New-Object 'System.Windows.Forms.Button' + $InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState' + #endregion Generated Form Objects + + #---------------------------------------------- + # User Generated Script + #---------------------------------------------- + + $form_About_Load = { + + # --- General information about the Script --- # + + # Mail Tr@cker information + $global:appName = "HeloCheck Mail Tr@cker" + $appVersion = "1.2" + $appLastUpdate = "Last Update: 2017/03/05" + + # Author Information + $authorName = "Daniele Catanesi" + $authorMail = "helocheck@helocheck.com" + $authorBlog = "HeloCheck.com" + $global:authorBlogURL = "http://blog.helocheck.com" + $global:authorTwitter = "@HeloCheck" + $global:globalauthorTwitterURL = "http://twitter.com/HeloCheck" + $global:authorLinkedIn = "https://ch.linkedin.com/in/catanesi" + + $labelLbl_AppAuthor.Text = "Author:" + $authorName + $lbl_AppAbout.Text = "$appName $appVersion" + $labelLbl_AppUpdate.Text = $appLastUpdate + $linklabelEmail.Text = $authorMail + $lbl_CreatedWith.Text = "Mail Tr@cker + Created with Sapien PowerShell Studio 2017" + $lbl_Twitter.Text = $authorTwitter + $lbl_BlogUrl.Text = $authorBlogURL + $lbl_LinkedIn.Text = $authorLinkedIn + } + + $btn_Ok_Click = { + $form_About.Close() + } + + $linklabelEmail_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{ + #Event Argument: $_ = [System.Windows.Forms.LinkLabelLinkClickedEventArgs] + # Open MailTo URL to send a message + [System.Diagnostics.Process]::Start("mailto:$authoremail?subject=$appName") + + } + $lbl_Twitter_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{ + # Open URL in default browser + [System.Diagnostics.Process]::Start($authorTwitter) + } + + $lbl_BlogUrl_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{ + #Event Argument: $_ = [System.Windows.Forms.LinkLabelLinkClickedEventArgs] + # Open URL in default browser + [System.Diagnostics.Process]::Start($authorBlogURL) + } + + $lbl_LinkedIn_LinkClicked = [System.Windows.Forms.LinkLabelLinkClickedEventHandler]{ + #Event Argument: $_ = [System.Windows.Forms.LinkLabelLinkClickedEventArgs] + # Open URL in default browser + [System.Diagnostics.Process]::Start($authorLinkedIn) + } + # --End User Generated Script-- + #---------------------------------------------- + #region Generated Events + #---------------------------------------------- + + $Form_StateCorrection_Load = + { + #Correct the initial state of the form to prevent the .Net maximized form issue + $form_About.WindowState = $InitialFormWindowState + } + + $Form_StoreValues_Closing = + { + #Store the control values + } + + + $Form_Cleanup_FormClosed = + { + #Remove all event handlers from the controls + try + { + $lbl_LinkedIn.remove_LinkClicked($lbl_LinkedIn_LinkClicked) + $lbl_BlogUrl.remove_LinkClicked($lbl_BlogUrl_LinkClicked) + $lbl_Twitter.remove_LinkClicked($lbl_Twitter_LinkClicked) + $linklabelEmail.remove_LinkClicked($linklabelEmail_LinkClicked) + $btn_Ok.remove_Click($btn_Ok_Click) + $form_About.remove_Load($form_About_Load) + $form_About.remove_Load($Form_StateCorrection_Load) + $form_About.remove_Closing($Form_StoreValues_Closing) + $form_About.remove_FormClosed($Form_Cleanup_FormClosed) + } + catch { Out-Null <# Prevent PSScriptAnalyzer warning #> } + } + #endregion Generated Events + + #---------------------------------------------- + #region Generated Form Code + #---------------------------------------------- + $form_About.SuspendLayout() + # + # form_About + # + $form_About.Controls.Add($lbl_LinkedIn) + $form_About.Controls.Add($labelLinkedIn) + $form_About.Controls.Add($lbl_BlogUrl) + $form_About.Controls.Add($labelBlog) + $form_About.Controls.Add($lbl_Twitter) + $form_About.Controls.Add($labelTwitter) + $form_About.Controls.Add($labelEmail) + $form_About.Controls.Add($labelLbl_AppAuthor) + $form_About.Controls.Add($lbl_CreatedWith) + $form_About.Controls.Add($labelLbl_AppUpdate) + $form_About.Controls.Add($lbl_AppAbout) + $form_About.Controls.Add($linklabelEmail) + $form_About.Controls.Add($btn_Ok) + $form_About.AutoScaleDimensions = '8, 17' + $form_About.AutoScaleMode = 'Font' + $form_About.ClientSize = '458, 349' + $form_About.FormBorderStyle = 'FixedSingle' + #region Binary Data + $form_About.Icon = [System.Convert]::FromBase64String(' +AAABAAEAgIAAAAEAIAAoCAEAFgAAACgAAACAAAAAAAEAAAEAIAAAAAAAAAABABILAAASCwAAAAAA +AAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD+/v4A/v39AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+ +/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79 +AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A +/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+ +/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+ +/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79 +AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A +/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+ +/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/v0A/v79AP7+/QD+/f0A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP7+/gD///8A59/WANzSxQDf1skA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA +3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe +1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7V +yADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXI +AN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA +3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe +1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7V +yADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXI +AN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADe1cgA3tXIAN7VyADf1skA3NPGAPf18QD///8A +/v7+AP///wDKu6cAtJ+CALmmigC4pYkAuKWJALiliQC4pYkAuKWJALiliQC4pYkAuKWJALiliQG4 +pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbil +iQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJ +AbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkB +uKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4 +pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbil +iQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJ +AbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkBuKWJAbiliQG4pYkB +uKWJAbiliQG4pYkAuKWJALiliQC4pYkAuKWJALiliQC4pYkAuKWJALmmigC1oIMA7ejhAP///wD+ +/v4A////AM/BrgC6qI0Av62UAL6skwC+rJMAvqyTAL6skwG+rJMCvqyTBL6skwS+rJMFvqyTBr6s +kwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyT +Br6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMG +vqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+ +rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6s +kwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyT +Br6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMG +vqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+rJMGvqyTBr6skwa+ +rJMGvqyTBr6skwW+rJMEvqyTAr6skwK+rJMBvqyTAL6skwC+rJMAv62UALupjgDu6uMA////AP7+ +/gD///8AzsCtALmmiwC+rJIAvauRAL2rkQG9q5EDvauRBb2rkQi9q5ELvauRDr2rkRC9q5ERvauR +Eb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ER +vauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9 +q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2r +kRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauR +Eb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ER +vauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9 +q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2rkRG9q5ERvauREb2r +kRG9q5EQvauRD72rkQy9q5EJvauRBr2rkQO9q5EBvauRAL2rkQC+rJIAuqeMAO7q4wD///8A/v7+ +AP///wDOwK0AuaaLAL6skgC9q5EBvauRBL2rkQi9q5ENvauRE72rkRi9q5EdvauRH72rkSG9q5Eh +vauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9 +q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2r +kSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauR +Ir2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5Ei +vauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9 +q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2r +kSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EivauRIr2rkSK9q5EhvauR +Ib2rkSC9q5EevauRGr2rkRS9q5EPvauRCb2rkQS9q5ECvauRAL6skgC6p4wA7urjAP///wD+/v4A +////AM7ArQC5posAvqySAb2rkQS9q5EIvauRD72rkRi9q5EivauRK72rkTK9q5E1vauROL2rkTm9 +q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2r +kTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauR +Ob2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5 +vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9 +q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2r +kTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauR +Ob2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E5vauROb2rkTm9q5E4 +vauRNr2rkTO9q5EsvauRJL2rkRu9q5ESvauRCr2rkQW9q5EBvqySALqnjADu6uMA////AP7+/gD/ +//8AzsCtALmmiwC+rJICvauRB72rkQ+9q5EavauRJ72rkTe9q5FBvauRR72rkUu9q5FNvauRTr2r +kU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauR +Tr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FO +vauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69 +q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2r +kU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauR +Tr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FO +vauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU69q5FOvauRTr2rkU29 +q5FLvauRR72rkUK9q5E5vauRK72rkR29q5ERvauRCL2rkQO+rJIAuqeMAO7q4wD///8A/v7+AP// +/wDOwK0AuaaLAL6skgS9q5ELvauRFr2rkSa9q5E5vauRPr2rkU+9q5FlvauRa72rkW29q5FuvauR +br2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5Fu +vauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69 +q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2r +kW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauR +br2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5Fu +vauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69 +q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbr2rkW69q5FuvauRbb2r +kWy9q5FovauRV72rkUO9q5E7vauRKr2rkRq9q5ENvauRBb6skgG6p4wA7urjAP///wD+/v4A//// +AM7ArQC5posBvqySBr2rkQ+9q5EdvauRMb2rkTy9q5CFvauQ072rkPC8qo/1vKqP9ryqkPa8q5D2 +vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8 +q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryr +kPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ +9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2 +vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8 +q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryr +kPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryrkPa8q5D2vKuQ9ryqkPa8qo/2vKqP +9ryqkPS8q5LivKuSpb2rkUa9q5E0vauRI72rkRK9q5EIvqySArqnjADu6uMA////AP7+/gD///8A +zsCtALmmiwG+rJIIvauRE72rkSa9q5EwvauQm72rkP+6qI3/uqiM/7qrk/+9q5D/u6mN/7qojP+6 +qI3/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qo +jP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM +/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/ +uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6 +qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qo +jP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM +/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojP+6qIz/uqiM/7qojf+6qIz/u6mN/72rkP+6q5T/ +u6uT/7moj/+8q5H/vauQw72rkTy9q5EovauRF72rkQq+rJICuqeMAO7q4wD///8A/v7+AP///wDO +wK0AuaaLAr6skgm9q5EYvauRIb2rkWS+rJH/u6iM/si5o/va0cP6vbKh/sG1o/3d1Mf54NjL+d3V +x/ne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ ++d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn5 +3tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne +1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7W +yfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ ++d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn5 +3tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53tbJ+d7Wyfne1sn53dXH+eDYy/nd1Mb5wbWk/buvnf29 +sJ/+xrmm/L2rj/u9q5D/vauRkr2rkSK9q5EcvauRDL6skgO6p4wA7urjAP///wD+/v4A////AM7A +rQC5posCvqySCr2rkRq9q5EfvauSrrypjv/FtZ366ubd+Ovm3/jAtaX+u7Cf/si/sfzo49v48Ozl +9+zo4fjt6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL3 +7eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft +6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p +4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni +9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL3 +7eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+3p4vft +6eL37eni9+3p4vft6eL37eni9+3p4vft6eL37eni9+zo4fjw7OX36OPb+Mi/sfy8saD+vLCf/tbO +w/vu6uP3zsGu+rqojP2+rJHcvauRL72rkRy9q5EOvqySBLqnjADu6uMA////AP7+/gD///8AzsCt +ALmmiwK+rJILvauRGr2rkSS+rZLLuqiL/9TJt/nu6+T36+be99zVyfm/tKT/vLCf/8O5qf3k39X4 +7uri9evm3vfs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs +59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn +3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off +9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/3 +7Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs +59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn3/fs59/37Off9+zn +3/fs59/37Off9+zn3/fs59/37Off9+zn3/fr5t737uri9eTf1fjDuan9vLCe/7+0pP/c1cr57uri +9+7q4vff18r5u6mN/72skvK9q5E+vauRGb2rkQ6+rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0A +uaaLAr6skgu9q5EavauRJL+tk827qIz/1cq5+e/s5vbt6eH56OTX5dXQveDAtqT5vLCe/MG2pPnY +0cHm49/O2eDcy9zg3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+Hc +zNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM +2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb +4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh +3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+Hc +zNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM2+HczNvh3Mzb4dzM +2+HczNvh3Mzb4dzM2+HczNvg3Mzb4NzL3OPfztnY0cHmwbak+bywnvzAtqT51tC+4eXh0t/s5+D3 +7+vl9+DYzPi8qY7/vq2S872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5 +posCvqySC72rkRq9q5Ekv62TzbunjP/Wy7r58u/p9u/s5vnk4dPh4N3K0NrVxNvDuaf0vrGf+7+0 +offTzbvg4d7N0d/bydTf28rU39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK +09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT +39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf +28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/b +ytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK +09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT39vK09/bytPf28rT +39vK09/bytPf28rT39vK1N/bydTh3s3R08274L+0ofe+sZ/7w7mn9NrVxNrg3cvQ4d3N2u7r5Pfx +7un34drO+Lypjv++rZPzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmm +iwK+rJILvauRGr2rkSS/rpPNuqiM/9fMvPn08u328e/p+ejl2ODf3MvS4+HQ0d/bytXHvqzwv7Sh ++sC1ovjRyrnl5OHR0eLeztPh3s7T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T +4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi +387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+Lf +ztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O +0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T +4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi387T4t/O0+LfztPi +387T4t/O0+HeztPi3s7T5OHR0dHKueXAtaL4v7Sh+se+rPDf28rV4+HQ0eDdzNLk4dLZ8e7o9vPx +7Pfj3ND4vKqO/76tk/O9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaL +Ar6skgu9q5EavauRJL+ulM27qI3/2M6++fb18fX08u346ujb3+Lgz8/j4dHS5ePTz+Pg0NHMw7Lr +wbWj+MG2pPjQybjm5OPTz+Ti0tDj4dDR5OLR0OPi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj +4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi +0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR +0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR +4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj +4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0ePi0dHj4tHR4+LR0eTi +0dDj4dDR5OLS0OTj08/Qybjmwbak+MG1o/jMw7Lr4+DQ0eXj08/j4dHS4uDQ0Obk1dfz8ev29vTw +9uXe0/i9qo//v66U872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posC +vqySC72rkRq9q5Ekv66Vzbupjf/a0MD4+fj09ff08Pjs6t7e5OPSzebl1dDl5NTQ5uXVz+fl1c7R +yrnmw7il9sK4pffPx7Xp5uTUz+fm1s7l5NTQ5uXVz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl +1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXU +z+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP +5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m +5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl +1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dTP5uXUz+bl1M/m5dXP5eTU +0Ofm1s7m5NTPz8e16cK4pffDuKX20cq55ufl1c7m5dXP5eTU0Obl1c/l49PO6OfY1vb07vb49/P2 +5+DV972rkP+/rpXzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+ +rJILvauRGr2rkSS/rpXNu6mN/9rQwfj6+vb1+Pby+e3s4d7l5dTL5+bXz+fm187n5tbO5+fXzuno +2czW0L/hxbqo9MS5p/XNxbTq5OPT0Ono2czn5dbO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bX +zufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO +5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n +5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm +187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bX +zufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufm187n5tfO5+bXzufl1s7p6NnM +5OPT0M3FtOrEuaf1xbqo9NbQv+Hp6NnM5+fXzufm1s7n5tfO5+bXzubl1c3p6NvV9/bx9vn59fbo +4tf3vauQ/7+ulfO9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6s +kgu9q5EavauRJMCvls28qo7/29LC+Pz7+PX5+PT47u7j3efm1sro6NnO6OjYzejo2c3o59jN6OfY +zerr3Mrb18Xcx72q8cW7qPTMw7Ds4+LS0erq28vo59jO6OjZzejo2M3o6NjN6OjYzejo2M3o6NjN +6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o +6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo +2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjY +zejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN +6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2M3o6NjN6OjYzejo2c3o59jO6urby+Pi0tHM +w7Dsxbuo9Me9qvHb18Xc6uvcyujn2M3o59jN6OjZzejo2M3o6NjN5+fXzOrq3dT4+PP1+/v39unj +2fe+rJH/wK+W872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqyS +C72rkRq9q5EkwK+Wzbyqj//c0sP4/Pz59fr59vjv7+Xc5+fYyujp283o6NvM6OjbzOjp28zo6NrM +6Ojazevs3srf3MzXysGu7se9qvLMw7Dt4d/P1evr3sro6NrN6OjazOjo28zo6NvM6OjbzOjo28zo +6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo +28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6Ojb +zOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM +6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo +6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NvM6OjbzOjo28zo6NrM6Ojazevr3srh38/VzMOw7ce9 +qvLKwa7u39zM1+vs3sro6NrN6OjazOjp28zo6NvM6OjbzOjp28zn59nL6+vf1Pr59fX8/Pn16ePZ +976skv/Ar5fzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJIL +vauRGr2rkSTBsJbNvauP/9zTxPj9/fv0+/r3+PDw5tzo6NnJ6urbzerq28zq6tvM6urbzOrq28zq +6tvM6enazezt3snj4dLTzsWy68m/rPHLwq/u4N3N1+zt3snq6dvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM +6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq +6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrp28zs7d7J4N3N18vCr+7Jv6zxzsWy +6+Ph0tPs7d7J6enazerq28zq6tvM6urbzOrq28zq6tvM6urbzOnp2svs7N/T+vn29fz9+vXp5dr3 +v62S/8Gwl/O9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9 +q5EavauRJMGwl829q5D/3NPE+P39/PT7+vj48PDn3Onp2snq6tvN6urbzOrq28zq6tvM6urbzOrq +28zq6tzM6unbzOzs3srm5dbQ0cm36MvBru/Mw7Du3dnJ2uzs3srq6tvM6urbzOrq3Mzq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM +6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq +6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq +28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6urb +zOrq28zq6tvM6urbzOrq28zq6tvM6urczOrq28zq6tvM7Ozeyt3ZydrMw7Duy8Gu79HJt+jm5dbQ +7Ozeyurp28zq6tzM6urbzOrq28zq6tvM6urbzOrq28zq6tvM6enby+3t4NP6+ff1/f379erl3Pe/ +rZP/wbCX872rkT+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72r +kRq9q5EkwbGYzb2skf/c1MX4/f789Pv6+Pjw8Ofc6enayerq3M3q6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6unbzOzs3srp6NrO1c685MzDsO3Nw7Ht29bG3Ovs3srq6t3M6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urdzOvs3srb1sbczcOx7czDsO3Vzrzk6ejazuzs3srq +6dvM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzp6dvL7e3h0/r59/X9/fv16uXc97+u +lP/BsZjzvauRP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauR +Gr2rkSTCsZjNvqyS/93Uxfj9/vz0+/r4+PDw59zp6drJ6urczerq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6unbzOvr3cvq6tzM2NLC4M7FsuzOxbLs2dPC3+rq3Mzr693L6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urc +zOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM +6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq +6tzM6urczOrq3Mzq6tzM6urczOvr3cvq6tzM2dPC387FsuzOxbLs2NLB4Orq3Mzr693L6unbzOrq +3Mzq6tzM6urczOrq3Mzq6tzM6urczOrq3Mzq6tzM6urczOnp28vt7eHT+vr39f39+/Xq5dz3wK6U +/8KxmPO9q5E/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5Ea +vauRJMKxmM2+rZP/3dTG+P3+/PT7+vj48fHo3Orq28nr693N6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+rdzOvr3cvs7N/L3dfH3NHIterQxrTr2NLA4erq3M3s7N7L6urczOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vd +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOrq3Mzs7N7L6urczdjSwOHQxrTr0ci16t3Xx9zs7N/L6+vdy+vq3czr693M6+vd +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6urcy+3t4dP6+vf1/f379erm3PfAr5X/ +wrKZ872rkj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9 +q5EkwrKYzb+tk//d1cb4/f789Pv6+Pjx8ejc6urbyevr3c3r693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czt7d/K4NzM2NPKuOjSybbp2NC/4ujn2c/s7d/K6+vc +zOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr +693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr +3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czr693M6+vd +zOvr3czr69zM7O3fyujn2c/Y0L/i0sm26dPKuOjg3MzY7e3fyuvr3czr693M6+vdzOvr3czr693M +6+vdzOvr3czr693M6+vdzOvr3czr693M6+vdzOvr3czq6tzL7e3h0/r69/X9/fv16+bc98Gwlf/D +s5nzvauSP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2r +kSTCspnNv66U/93Vxvj9/vz0+/r4+PHx6Nzq6tzJ6+vezevr3szr697M6+vezOvr3szr697M6+ve +zOvr3szr697M6+vezOvr3szr697M6+vdzOvr3czt7eDK4+DR1dXNu+XUy7jn19C+4+fm19Ds7eDK +6+vdzOvr3czr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr +697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOrq +3czq6t3M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+ve +zOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr693M +6+vdzOzt4Mrn5tfQ19C+49TLuOfVzbvl4+DR1e3t4Mrr693M6+vdzOvr3szr697M6+vezOvr3szr +697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOrq3cvt7eLT+vr39f39+/Xr5tz3wbCW/8Oz +mvO9rJI/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavauR +JMOzms3Ar5X/3tXH+P3+/PT7+vj48fHo3Orq3Mnr697N6+vezOvr3szr697M6+vezOvr3szr697M +6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szt7uHK5uTV0tjQvuPVzLrm18+94+Xj1NPt +7eDK6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr +3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szq6t3M7+/h +y+/v4cvq6t3M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M +6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr3szt +7eDK5ePU09fPvePVzLrm2NC+4+bk1dLt7uHK6+vezOvr3szr697M6+vezOvr3szr697M6+vezOvr +3szr697M6+vezOvr3szr697M6+vezOvr3szr697M6urdy+3t4tP6+vf1/f379evm3ffCsZf/xLSb +872skj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9q5Ek +w7ObzcCvlv/e1cf4/f789Pv6+Pjy8ujc6+vcyezs3s3s7N7M7OzezOzs3szs7N7M7OzezOzs3szs +7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szt7uDK6ejZ0NvUwuDXz7zk2NG/4+Xi +09Tt7uDK7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7Oze +zOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M6uvdzPLy5cvR0sbR +0dLG0fLy5cvq6t3M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs +7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7e7gyuXi +09TY0b/j18+85NvUwuDp6NnQ7e7gyuzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szs7N7M7Oze +zOzs3szs7N7M7OzezOzs3szs7N7M7OzezOzs3szr693L7u7i0/r69/X9/fv16+bd98KxmP/EtJzz +vaySP72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2rkSTE +tJvNwbGW/97WyPj9/fz0+/r4+PLy6dzr693J7Ozfzezs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs +38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zt7uHL6urczt3Yxt3Y0b/j2dLA +4uPf0Nbt7eHL7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M +7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOrq3s3z8+TK1dTFz5uajdqb +mo3a1dTFz/Tz5Mnr697M6+zfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs +38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzO3t4cvj39DW2dLA +4tjRv+Pd2Mbd6urczu3u4cvs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M +7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOvr3svu7uPT+vr39f39+/Xr5t33w7KY/8W1nPO+ +rJI/vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavauQJMS0 +m83Cspf/3tbI+P39+/T7+vj48vLp3Ovr3cns7N/N7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7Ozf +zOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zt7eDL7OvfzODby9ra08Hh +2tPB4eLeztjs7ODM7OzgzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs +7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOvr38zr69/M9fPkycrMx9aNkpTknKGm4pyh +puKOkpTjx8rH2PPy48rt7N/L6+vfzezs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7Ozf +zOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs4Mzs7ODM4t7O2NrTweHa08Hh +4NvL2uzr38zt7eDL7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs +7N/M7OzfzOzs38zs7N/M7OzfzOzs38zs7N/M6+vey+7u49P6+vf1/f379evn3ffDs5n/xbad876s +kj+9q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9q5AkxbSc +zcOymP/f1sj4/f379Pv6+Pjy8unc7Ozeye3t4M3t7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM +7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzOzs4Mzt7eHM7e3hy+Pfz9fc +1cTf3NXE4OHdzdns7ODM7e3hy+zs4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7OzgzO3t4Mzr7ODN7e3gy/Ly5Mq0vsrlbYCp/niOwP+fu/r/n7v6 +/3iPwP9rf6n/p7XJ7ezt5M/v7+DK6+vgze3t4Mzt7ODM7OzgzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM +7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzOzs4Mzt7eHL7OzgzOHdzdnc1cTg3NXE3+Pfz9ft +7eHL7e3hzOzs4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzs7N/L7+/k0/r69/X9/fv16+fe98Szmv/Gtp7zvqyS +P72rkRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2qkCTFtZ3N +w7OZ/9/Xyfj9/fv0+/r4+PLy6dzs7N7J7e3gze3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt +7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7u7iy+bj +09Xe2Mfe3tfG3+Ldzdns697N7u7hy+3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3g +zO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7OzgzfDv4Mru7uPOpLPK8Gp/rP99k8P/or/4/6bF//+mxf// +or/4/36Uw/9qf6z/mKrK9+Pn4tXy8eDJ7Ozgze3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt +7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7u7hy+zr3s3i3c3Z3tfG397Yx97m49PV7u7iy+3t +4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3g +zO3t4Mzt7eDM7e3gzO3t4Mzt7eDM7e3gzO3t38vv7+TT+vr39f39+/Xr5973xLSb/8a3n/O+rJI/ +vauRGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavaqQJMW2ns3E +tJr/39fJ+P39+/T7+vj48vLq3Ozs38nt7eHN7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t +4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7u7j +y+fl19Lg2src39nI3eHczdrr6t7O7u7iy+3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzOzs4c3y8OHJ5+rk0pmry/Znfq3/e5TE/py9+f+fvv//nLr+/5y6/v+f +vv//m7z5/3qTxP5nfq3/i6LL/Nff49vx8ODI7e3hze3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t +4czt7eHM7e3hzO3t4czt7eHM7e3hzO7u4svr6t7O4dzN2t/ZyN3g2src5+XX0u7u48vt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3gy+/v5dP6+vf1/f379ezn3vfFtJz/x7ig876skj+9 +q5EavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq9qpAkxbaezcS0 +nP/f18r4/f379Pv6+Pjy8urc7Ozfye3t4c3t7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM +7u/jy+ro29Di3c3a4dvL2+Ldztrr6dzP7u7iy+3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt +7eHM7e3hzO3t4czt7eHN7+7gydrh49eNo8v6aoCt/4edxv6yzPv/utH//7zO/v/C0v//xNP//8PS +/v/C1///vNL7/5Okxv53ia3/kKXL/9Td5eHu7eDI7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3h +zO3t4czt7eHM7e3hzO3t4czu7uLL6+nc0OLdztrh28vb4t3N2uro29Du7+PL7e3hzO3t4czt7eHM +7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt +7eHM7e3hzO3t4czt7eHM7e3hzO3t4czt7eDL7+/l0/r69/X9/fv17Ofe98W1nf/HuKHzvqySP72r +kRq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGr2qkCTGtp7NxbWc +/+DYyvj9/fv0+/r4+PPz69zt7eDJ7u7ize7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM +7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu +7uLM7+/jy+zr3s7k39DY493N2uPeztnr6dzQ7+/jy+7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7e7ize7u4Mjr7urbqrfM/ZKbrP+8wMj+9ff8//////////////////////////////// +///////////////8/8rKyP+oqaz/ys7W//f49ebr6+DJ6+zgzO7u4szu7uLM7u7izO7u4szu7uLM +7u7izO7u4szu7uLM7+/jy+vp3NDj3s7Z493N2uTf0Njs697O7+/jy+7u4szu7uLM7u7izO7u4szu +7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7u7izO7u4szu7uLM7u7izO7u4cvw8ObT+vr39fz9+/Xs5973xrae/8i5ofO+rJI/vauR +Gr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavaqQJMa2n83Ftp3/ +4NjK+P39+/T7+vj48/Pr3O3t4Mnu7uLN7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu +7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7+/jy+3s4M3m4tPW5N/P2eTf0Njp6NrS7+/jy+7u4szu7uLM7u7izO7u4szu7uLM7u7i +zO3t4s3q6+DI9vfx39fY1/+6ta7/4NnM/////v///////fz+//H0/f/n7fz/4Ob7/9zk+//c5Pv/ +4un8/+jt/P/x9P3/+/v8/9LPyv+6tKz+5ODa////+Or49uXK7e3izO3t4s3u7uLM7u7izO7u4szu +7uLM7u7izO/v48vp6NrS5N/Q2OTfz9nm4tPW7ezgze/v48vu7uLM7u7izO7u4szu7uLM7u7izO7u +4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7izO7u4szu7uLM7u7i +zO7u4szu7uLM7u7izO7u4szu7uLM7u7hy/Dw5tP6+vf1/P379ezn3/fGtp//yLqi876skj+9q5Ea +vauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkx7egzca2nv/g +2Mv4/f389Pv6+fjz8+zc7e3hye7u483u7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u +48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7j +zO7u48zu7uPM7u/ky+7u4szo5NbU5eDS1+Xg0tfp59rS7+/kzO7u48zu7uPM7u7jzO7u48zq6+LN +8PDiyf//9OPe29f/p6ap/5urx/+Yt/b/bZHy/0h47/80ae7/H1zt/xZX7f8TVO3/EVPt/xFS7f8U +Ve3/F1bu/x5Z7f8zae//Q4Hx/0Zzwf9Wb6L+lqnQ/87X5u3w7+LL8vHly+zs4s3u7uPM7u7jzO7u +48zv7+TM6efa0eXg0tfl4NLX6OTW1O3u4szu7+TL7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7j +zO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM7u7jzO7u48zu7uPM +7u7jzO7u48zu7uPM7u7jzO7u48zu7uLL8PDn0/r6+PX8/Pz17Ojg98e3oP/JuqPzvqySP72rkRq9 +q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTHuKHNxref/+DZ +zPj9/fz0+/r5+PPz7Nzu7uHJ7+/jze/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/j +zO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM +7+/jzO/v48zv7+PM7+/kzO/v48zq59rS5+PU1ufj1Nbq6NrS7+/jzO/v5Mzu7uPM7u7jzP765snt +7+/mmKrN/z9hn/4fXMD/DF/u/wBG7f8AQ+7/AEjw/wJM8f8HUPP/CVLz/wpU9P8LVPT/C1T1/wpT +9f8JUfT/B0/0/wJJ8v8ASvP/BFzz/wBLwv8AOKD+BUCr/0t0tvDq6+DM9fTmy+zs4s3v7+TM7+/j +zOro2tLn49TW5+PU1urn2tLv7+PM7+/kzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM +7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv7+PM7+/jzO/v48zv +7+PM7+/jzO/v48zv7+PM7+/jzO/v4svx8efT+vr49fz8+/Xs6OD3x7mh/8m7pPO+rJI/vauRGr2r +kQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMe5oc3Ht6D/4dnM ++P39/PT7+vn48/Ps3O7u48nv7+TN7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM +7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5czr6t3Q6OXX1Ojk1tXr6NvS7e3jzfDx5cz29OTKhaDK6SJW +t/8AN53+Ak7D/wph8v8MUfL/DlLy/w9W9P8PV/T/D1j1/w5Y9v8OWPf/Dln4/w5Z+P8OWfn/Dln6 +/w5Y+v8OV/n/Dlb5/w5U+P8PVPj/EWj6/w9Zyv8MRqj+ADyw/zpptfPl59/O9/Xnyuvs4s3r6NvS +6OTW1ejl19Tr6t3Q7+/lzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/jy/Hx59P6+vj1/Pz79ezo4PfIuqL/yruk876skj+9q5EavauR +D76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkyLmizci4of/h2c34 +/f389Pv6+fjz8+zc7u7jye/v5M3v7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv +7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/w5czs69/P6ufZ0+fk2NTs6dzS9vPjzF+Fv+sAOKr/BT+i +/hBZyP8RZvT/DFLy/wtR8f8MVPL/DFXz/wxX9P8NWPT/DVn1/w1a9v8NW/j/DVv5/w1b+v8NW/v/ +DVv8/w1a/P8NWPz/DVj8/wxU+v8MVPr/D2f8/w1ZzP8OSan+AD+y/zFjtPXe4dzQ9O/e0OXj19Tq +59nT7Ovfz+/w5czv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v +5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/kzO/v5Mzv7+TM7+/k +zO/v5Mzv7+TM7+/kzO/v5Mzv7+PL8fHn0/r6+PX8/Pv17ejg98m6o//Ku6XzvqySP72rkRq9q5EP +vqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTIuaPNyLqh/+Lazfj9 +/vz0/Pv5+PT07dzv7+TJ8PDlzfDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw +5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5szs6+HO7uvd0fDs29FVfLrvADyt/w5Ipv4OWsn/ +DmP0/wtQ8P8LUe//DFTv/wxV7/8MVu//DFfv/wxZ8P8MWvH/DFrz/w1b9P8NXPb/DV34/w1d+v8N +Xfv/DVz8/w1b/f8NWvz/DVn8/wxV+f8MU/j/Dmb6/wxZy/8OSKb+AECv/ylbrvjV2NTW9vHg0Orq +4c/w8ObM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5Mvy8ujT+/v49f39+/Xt6eH3ybuj/8u8pfO+rJI/vauRGr2rkQ++ +rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMi5o83JuqP/4tvO+P3+ +/PT8+/n49PTt3O/v5Mnw8OXN8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDl +zPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM7e7kzfX06Mvx7+HNTXa58AA9rf8OSKb+DlrL/w5j8/8M +UO3/DFHr/wxT6v8NU+j/DVXn/w1W5/8NVuf/DVfo/w1Y6f8OWuv/Dlvt/w5b8P8OXPP/Dl32/w5d ++P8OXfn/Dl36/w5b+v8OWfn/DVj4/wxU9P8MUfL/D2Pz/w1YyP8ORqH+AT6p/yJUqPnT2djT/Prq +yezs483w8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM +8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw8OXM8PDlzPDw5czw +8OXM8PDlzPDw5czw8OXM8PDky/Ly6NP7+/j1/f379e3p4ffJu6X/y7yn876skz+9q5EavauRD76s +kgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkybqjzcm7pP/i2874/f78 +9Pz7+fj09O7c7+/lyfDw5s3w8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM +8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw +8ObM8PDmzPDw5szw8ObM8PDmzO3t5c319OjL7u7jzEdzuvAAPaz/D0qm/g5czf8PY/L/DU/p/wxR +5v8NUuP/DVLg/w1S3v8NU93/DVTc/w1U3P8OVt3/Dlff/w5Y4f8OWeT/Dlvo/w9c7P8PXfD/D17z +/w5e9f8OXvf/Dlz3/w5b9f8OWPP/DVbw/w1T6/8NT+f/D1/o/w1WwP8ORJr+Ajyf/x5Qo/nP1tjT +/frryevs5M3w8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw +8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw5szw8ObM8PDmzPDw +5szw8ObM8PDmzPDw5szw8OXL8vLp0/v7+PX9/fv17unh98q8pf/LvafzvqyTP72rkRq9q5EPvqyS +BLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTKu6TNyryl/+Pbzvj9/vz0 +/Pv5+PX17tzw8OXJ8fHmzfHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx +8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx +5szx8ebM8PDmzPHx5szt7uXN9vXoy+3t481CcbvyAECs/w9Lpv4OX8//EGPw/w1P5v8NUeL/DVHd +/w1Q2f8OUdX/D1LT/w9T0v8QU9H/EFTR/xBV0f8QVtT/D1bW/w5X2v8OWd7/D1vj/w9c6P8PXez/ +D17v/w9e8v8OXfL/Dlvv/w5Z7P8OVuj/DVPj/wxP3f8NS9j/EFva/w9Vt/8QRJH+BTyX/xxNnPrN +1NfU//3tyu3u5c3x8ebM8PDmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx +5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHmzPHx5szx8ebM8fHm +zPHx5szx8ebM8fHmzPHx5cvy8unT+/v49f39+/Xu6eH3y72m/8y+qPO+rJM/vauRGr2rkQ++rJIE +uqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMq8pc3Lvab/49zP+P3+/PT7 ++/n49fXu3PDw5snx8efN8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx +58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zy8efM7u/mzff26cvr7OPNP2+88wBBrf8QTaf+D2HR/xFk7/8NT+P/DVDe/w5Q2P8QUdP/ +Dk7N/wdJx/8DRsL/AkXA/wJFv/8CRcD/A0fC/wVKxf8KT8r/D1XQ/xFY1f8PWNr/D1rg/w9c5f8Q +Xej/EF3q/w9c6v8PWuj/D1jk/w5V3v8PUtn/EE/T/wtKzf8FQMb/BE3G/wFHqP8CNoP+ADKL/xFC +kvvAydHV+vnqye7v5s3x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzPHx58zx8efM8fHmy/Ly6tP7+/j1/f379e7q4ffLvqf/zL+p876skz+9q5EavauRD76skgS6 +p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkyrymzcy9p//j3M/4/f789Pv7 ++fj19e7c8PDmyfHx583x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHn +zPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzO7u5s349urK6Orjzjttu/MAQq3/EU6o/hFj0/8RY+7/Dk/i/w5Q3P8QUdb/DU7O/wNFxP8M +S8L/J2DK/0V31P9Yh9z/XYzf/1yL3/9UhNv/PnPT/yFey/8JTsb/BU3K/xBX0/8RWdj/D1rc/xBb +4P8QW+L/EFvi/xBZ4P8PVtr/EVXW/wxOzv8DRMT/EkzE/y1gzP9HcdT/WY3g/1iIx/9FaJz+Nl2d +/0ltqfvX3d/V9/Xoye/v583x8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM +8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx8efM8fHnzPHx58zx +8efM8fHnzPHx58zx8ebL8vLq0/v7+PX9/fv17uri98y+qP/Nv6rzvqyTP72rkRq9q5EPvqySBLqn +jADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTKvKbNzL6o/+Pc0Pj9/fz0+/v5 ++Pb279zx8efJ8fHozfHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM +8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPLy6Mzu +7+fN+Pfqyufp4844arj0AEOr/xFPqf4SZdf/EmTv/w9R4v8PUtz/ElPV/wdJyf8RT8X/UYDa/4+x +8f+syf7/tdL//7fT//+41P//uNT//7bT//+00f//p8f8/4Sr7v9Fe9r/DFLL/wtTz/8TWtX/EVrY +/xFa2v8RWtr/EFjX/xJX0/8HTcn/GVbJ/2GM4P+Yt/X/r8r//7XO//+2zf//utf//6PB6P+Cl7X+ +hJu//6S73/vm6unW9fTnyfDx6M3y8ujM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx +8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx6Mzx8ejM8fHozPHx +6Mzx8ejM8fHozPHx58vz8+vT+/v49f39+/Xu6uL3zL+p/83Aq/O+rJM/vauRGr2rkQ++rJIEuqeM +AO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMu9p83Nv6n/5N3Q+P39/PT7+/n4 +9vbv3PHx58ny8ujN8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy +8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM7+/nzfn4 +68rm6eTON2m29AFEqf8SUqr+Emna/xRm8f8RUuT/EFTe/xNU1v8HScf/MmnQ/5a28/+40///s9D/ +/63L//+ryf7/qsn+/6vJ/v+syv7/rMr+/6zL/v+vzf//ttP//7fT//+JrvD/JWXS/whRy/8UWtH/ +EVjS/xFY0f8TV87/CU3F/zVr0P+gv/f/udP//7HN//+syP//q8f+/6rG/v+qxP3/sM///5295v96 +kLH+fpa6/6e94Pzm6unW9fToyfHx6M3y8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy +6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLozPLy6Mzy8ujM8vLo +zPLy6Mzy8ujM8vLny/Pz69P7+/j1/f379e7q4vfNwKr/zsGs876tkz+9q5EavauRD76skgS6p4wA +7urjAP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkzL6nzc7Aqf/k3dH4/f399Pv7+fj2 +9vDc8fHoyfLy6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy +6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzO/w6M36+OzK5ejk +zjdos/UCRKb/FFOr/hRs3f8VafT/E1Xn/xJW4f8VVtj/B0nJ/0V31/+xzf7/tND//6vJ/v+ty/7/ +rsv//6/M//+wzP//rMr//6nJ//+qyf//qsn//6nI//+qyf7/rcv+/7nU//+pxvv/N3DU/wpQx/8V +WMv/FFbJ/wxPwv8vZ8v/rcn7/7TQ//+qyP7/rcr//67K//+uyf//r8n//6rG//+nwv7/rc3//5u9 +6v94kLP+fZW4/6e83/zm6uvX9fTpyfHx6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLp +zPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM +8vLpzPLy6czy8ujL8/Ps0/v7+fX9/fz17+rj987Bqv/Pwqzzvq2TP72rkRq9q5EPvqySBLqnjADu +6uMA////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTMvqnNzsCr/+Td0fj9/fz0+/v5+Pb2 +8Nzx8ejJ8vLpzfLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLp +zPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czx8enN9/bryuTn5M83ZrD1 +AkSj/xRVrP4Vb+D/F2v2/xRX6/8UWeX/Flnc/wpNzP9AdNf/tM///7DN//+syv7/r8z//6/M//+w +zf//rsz//6vK//+91v//0eL//9zp///b6P//0OL//77W//+szP//qcn+/7XS//+ryfv/LGjO/w1S +wv8VVsP/EVC9/5G08f+30///rMr+/6/M//+vzP//rsv//6/L//+qx///vtT//9fk///a5f3/1ej/ +/7HK7P95krT+epO2/6a83fzm6uvX9vTpyfHx6c3y8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM +8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy8unM8vLpzPLy6czy +8unM8vLpzPLy6Mvz8+zT+/v59f39/PXv6uP3zsGs/8/CrvO+rZM/vauRGr2rkQ++rJIEuqeMAO7q +4wD///8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJMy+qc3Owav/5d7S+P39/PT7+/n49vbw +3PLy6cnz8+rN8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM +8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8vLqzfLz6cr19evPRG6w9QBCnv8V +Vq3+FnHi/xdt9/8VWu7/FVzq/xZb4f8QVNT/J2LR/6vI+/+z0P//r8z+/7HO//+wzv//sc///6/O +//+y0P//3ur//////////////////////////////////+Xv//+81v//qcr+/7nW//+WufT/F1fB +/wpNuf9Ed83/udX//67N/v+xzv//sM7//7DN//+xzv//rcr//8ze//////////////////////// +/////9Le7v+Bl7b+epG0/6e73Pzn7O3X9vXqyfLy6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz +8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz +6szz8+rM8/Ppy/T07dP7+/n1/f389e/r4/fPwqz/z8Ou876tkz+9q5AavauRD76skgS6p4wA7urj +AP///wD+/v4A////AM7ArQC5posCvqySC72rkRq8qpAkzb6pzc/CrP/l3tL4/f389Pv7+fj29vDc +8vLpyfPz6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz +8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPLz6s3w8ejK///0zqm71fUAQJf/Fler/hd0 +4/8Yb/f/Flzw/xZf7v8WXub/F13c/w5Szv+Eqe//u9b//67M/v+xz///sc///7HP//+xz///sc// +/+ry/////////Pz+/8zc+P+wyfX/s8v1/8/e+f/7+/7///////z9///E2///qcr+/77Z//9jkd3/ +AEOw/3eg4/+81///r83+/7HP//+xz///ss///6/N//+40v////////j5/f+8z/T/r8Xz/8va+P/7 ++/3//////+Po8P+Emrf+eZKz/6a62vzo7O3X9vXqyfLy6s3z8+rM8/PqzPPz6szz8+rM8/PqzPPz +6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/PqzPPz6szz8+rM8/Pq +zPPz6szz8+nL9PTt0/v7+fX9/fz17+vk99DCrf/QxK/zvq2TP72rkBq9q5EPvqySBLqnjADu6uMA +////AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqkCTNv6rN0MOu/+Xe0/j9/fz0+/v5+Pb28dzy +8urJ8/PrzfPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz +68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zy8uvN9fTqyu3x7s7V2uD1QWqi/whOpf4bd+P/GW/3 +/xZf8v8WYvL/F2Ls/xlh4/8OV9X/P3fZ/7jT//+vzf7/sc///7HP//+xz///stD//6vM///b6f// +/////6jD8v85eub/EmTm/wxh6P8MYun/FGbo/zZ76f+PtPH/+/z+//////+91v//sdD//6nI+/8h +XLz/kLTu/7jV//+wzv7/sc///7HP//+y0P//qcr//+Ht///X4fb/NXLf/wtY3v8KWOL/EVvj/zhy +5v+mvfH//////+Ho8v9/mbn+epKx/6S52Pzo7e7W9vXryfLy683z8+vM8/PrzPPz68zz8+vM8/Pr +zPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM8/PrzPPz68zz8+vM +8/PrzPPz6sv09O3T+/v59f39/PXv6+T30cOu/9HEsPO+rZM/vauQGr2rkQ++rJIEuqeMAO7q4wD/ +//8A/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqQJM3Aq83QxK//5d/T+P39/PT7+/n49/fx3PPz +6sn09OvN9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTr +zPT068z09OvM9PTrzPT068z09OvM8/PrzfX06sr09e/OssHY9Y2Zq/8YVaL+GXXf/xtw9P8YYPL/ +GGX0/xlm8f8ZZOr/GmPh/xBX0v+Eq+7/vdb//7HO/v+z0P//s9D//7TR//+xz///u9X///////+H +rOz/B1zg/xJm6f8cbvD/HXDz/x1w8/8bb/H/E2jt/wde5/9Ihej/6fD7//b6//+vzf7/vdj//2SQ +2f+XufH/utb//7LP//+z0P//s9D//7PQ//+z0P//6O/8/zx43f8HWd7/HWno/xxo7P8bZ+3/EV7q +/wdS4/91m+v//////8/f8/9/mbv+e5Kw/6S41vzq7u7W9/bryfPz68309OvM9PTrzPT068z09OvM +9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z09OvM9PTrzPT068z0 +9OvM9PTqy/X17dP7+/n1/f389e/r5PfRxK//0cWx876tkz+9q5AavauRD76skgS6p4wA7urjAP// +/wD+/v4A////AM7ArQC5posCvqySC72rkRq8qo8kzsGrzdHFsP/m39T4/f389Pv7+fj39/Lc8/Pr +yfT07M309OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM +9PTszPT07Mz09OzM9PTszPPz7M319OvK9vbwzau81PSHmbH/cIuv/hJu2P8ecO7/GmDw/xpm9f8a +aPT/G2jw/xtm6P8WYN3/KmvX/67L/P+30v//tdH//7XR//+10f//ttL//63M///p8v//ssnx/whb +3f8ebur/H3Hx/xxx9f8cc/f/HHP4/xxy9/8ecvX/IXLx/wxi5/8+f+b/9Pf8/9ro//+y0P//pMP2 +/6rI+f+40///tdH//7XR//+10f//sc7+/8je//+gvO7/DFra/yBs5/8bau3/G2vy/xtq9P8davL/ +Hmjt/wVR4v+Dpez//////7PS9f+Fnr7+e5Gu/6S31Pzr7+/W9/bryfPz7M309OzM9PTszPT07Mz0 +9OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT0 +7Mz09OvL9fXu0/v7+fX9/fz17+vk99LFsP/SxrHzvq2TP72rkBq9q5EPvqySBLqnjADu6uMA//// +AP7+/gD///8AzsCtALmmiwK+rJILvauRGryqjyTOwavN0cWx/+bf1Pj9/fz0+/v5+Pf38tzz8+vJ +9PTszfT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz0 +9OzM9PTszPT07Mzz8+zN9fTsy/b38M2vvtLzcoys/6Kwwf5dmt3/EGbk/x1i7P8bZ/P/G2r1/xxr +9P8cau//Hmrn/xFe2f9SiOH/wNj//7TR//+20///ttP//7bT//+20///t9T//+Xt/P82d97/Fmjl +/yBx7v8dcvT/HnX5/x52/P8edvz/Hnb7/x50+P8dcvX/I3Tw/wph5P9rm+n//////7fT//+31P// +uNT//7bT//+20///ttP//7bT//+x0P//y+D//1+R4v8PYN7/IG7q/x1u8f8cb/b/HW/4/xxu9/8d +a/P/Hmjs/w1X4f/B0PP/7Pj//6nN9v+KosD+epCt/6S20vvt8PDV9/bsyfPz7M309OzM9PTszPT0 +7Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTszPT07Mz09OzM9PTs +zPT07Mv19e7T+/v59f39/PXv6+X30sWy/9LGsfO+rZM/vauQGr2rkQ++rJIEuqeMAO7q4wD///8A +/v7+AP///wDOwK0AuaaLAr6skgu9q5EavKqPJM/BrM3SxrH/5uDU+P39/PT7+/n4+Pjy3PT07Mn1 +9e3N9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX1 +7cz19e3M9PTtzPb17Mv4+PHNr7zQ83mPq/+Jo8P+x974/z5/4f8UWuP/Hmju/x1q9P8dbfb/HW70 +/x1s7v8fa+f/El7X/3ik7P/B2///tdL+/7fU//+31P//t9T//7LR/v/L4f//ob7u/w9g3P8icer/ +HnLx/x519/8fd/v/H3j+/x94/v8feP3/H3f7/x92+P8ec/P/InLt/w9i4f/A0/T/4O7//6/P//+4 +1f//t9T//7fU//+31P//t9T//7XT///A2P3/O3rc/xdm4f8fcOz/HnHz/x5z+P8ec/r/HnH6/x1u +9v8gbPD/FGDn/0B54//09v3/vdr//7LU+P+MpML+epCr/6S1z/rv8fHU9/btyfP07c319e3M9fXt +zPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M +9fXty/b279P7+/n1/f389fDs5ffTxrL/08ey87+tkz+9q5AavauRD76skgS6p4wA7urjAP///wD+ +/v4A////AM7ArQC5posCvqySC72rkRq8qo8kz8GtzdPHsv/m4NX4/f389Pv7+fj4+PLc9PTsyfX1 +7c319e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXt +zPT07cz29e3L+fnxzK+8zvJ4jqr/jqbE/rnb+v+30vr/IV/a/xxk5/8eau//H270/x5w9v8ecPT/ +H2/t/x9t5f8ZZNj/kbfz/7/a//+10/7/t9T//7fU//+31P//stH//87i//9gk+L/EmXf/yJz6/8g +dPP/IHf5/yB5/P8gev//IHr//yB6/v8gef3/IXj6/x929v8idfD/FGjm/06J5P/v9f7/tNL//7fU +//+31P//t9T//7fU//+21P//udb//7HN+v8pb9n/HGvj/yBx7P8fc/T/IHX5/yB2/P8fdfv/H3L5 +/x5v8/8ibe3/DVng/6vC8P/a5v//stP//7bY+f+Op8T+epCq/6O0zvrw8/HT9/ftyvP07M319e3M +9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz19e3M9fXtzPX17cz1 +9e3L9vbv0/v7+fX9/fz18Ozl99PHs//TyLPzv62TP72rkBq9q5EPvqySBLqnjADu6uMA////AP7+ +/gD///8AzsCtALmmiwK+rJILvauRGryqjyTPwq7N1Mez/+fg1fj9/fz0+/v5+Pj489z09O3J9fXu +zfX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz09O7M +9fXuzPn58suvu83wd46q/5Cqxv622Pr/w+D//5u38P8XXdv/IGno/x9t8P8fcPX/H3P2/x9z9P8g +cO3/Hm3k/yJr2f+jxPj/vdn//7fU//+41f//uNX//7jV//+31f//v9j9/zl63P8aa+L/InTs/yB2 +9P8hePn/IXv8/yF7//8he///IXv//yF7/f8hevv/IXj4/yB18v8idOv/Fmff/7/U9P/L4v//tNL/ +/7jV//+41f//uNX//7fU//+82f//pcX2/yRs2P8ebuP/IHPs/yB19P8hd/n/IXj8/yF3/f8gdfr/ +H3L2/yJv7/8UYuX/UIXj/+Xt/v+xy/7/u9r//7fa+v+Qqsb+eY+q/6SzzPny9fLS+vnvyvLz7c31 +9e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX17sz19e7M9fXuzPX1 +7sv29vDT+/v59fz9+/Xw7OX31Me0/9TItPO/rZM/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+ +AP///wDOwK0AuaaLAr6skgu9q5EavKqPJM/Drs3UyLT/5+HW+P7+/PT7+/n4+Pjz3PX17cn29u7N +9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPX17sz29u7M9PXuzPX17sz6 ++vLKsbvK73iOqf+TrMn+utz8/7nY///E2f//hans/xRe2/8jben/IXDw/yF09f8hdvb/InX0/yNz +7v8fbuT/KXHb/63M+/+92P//udX//7rW//+61v//udX//73Z//+uy/n/KHDZ/x9v4/8ide3/Inj0 +/yN6+f8jfP3/I3z//yN8//8jfP//I3z//yN8/P8jevn/Inj0/yV37f8SZ+H/dqPn/9zr//+z0v// +u9b//7rW//+61v//udX//8Db//+ewPX/IGrX/yFx4/8idOz/Inf0/yN5+f8jevz/I3r9/yN4+/8h +dff/IXLx/yBt6f8gZt7/xdb3/8LZ//+3z/7/vNr//7rc/P+SrMn+fJGq/5mpw/jj6OnR/fzxyvPz +7c329u7M9fXuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbuzPb27sz29u7M9vbu +y/f38NP7+/n1/f379fHt5ffUyLX/1Mi187+tkz+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A +////AM7ArQC5posCvqySC72rkRq8qo8k0MSvzdXJtf/o4db4/v789Pz8+fj4+PPc9vbuyfb27832 +9u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPX17sz29+/M/v30yrG7 +yO15jqj/lrDL/rzd/f+82///t9D+/8fd//9wnOj/FWDc/yVw6v8jc/H/Inb2/yN39/8kd/T/JXbu +/yBw5P8sdNz/stD8/7/Z//+71///vNf//7zX//+71v//wdv//6TD9f8kbdj/InHk/yR27f8kePT/ +JXz5/yV+/f8lfv//JX7//yV+//8lfv//JX79/yV8+/8kevb/JXjv/xxv5v8+f9//1OT8/7rW//+8 +1///vNf//7zX//+71v//wdv//6PE9v8jbNf/InHj/yR27P8kefP/JXr5/yV9/P8lff7/JXv8/yR4 ++f8idfP/JXPs/xNh3/+Psez/0OP//7bR/v+70v7/vNv//7zd/f+XsMz+d42m/09tnPbj6OrP/fzy +yvP07c329u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/L +9/fx0/z8+fX9/vv18e3m99XJtv/VyLbzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD/ +//8AzsCtALmmiwK+rJILvauRGryqjyTQxK/N1cm2/+jh1vj+/vz0/Pz5+Pj489z29u7J9vbvzfb2 +78z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z19e7M9/fwzPn58cmZpbfre4+o +/5iyzv6+3/7/vtz//7vT/v+60///xtz//2aU5P8YZNz/JXPq/yN18f8kePb/JXn3/yR59P8ld+7/ +IXHl/y523f+10vz/v9n//7zX//+91///vdf//7zW///D2///o8P1/yRt1/8jc+P/JHfs/yR69P8m +ffn/Jn78/yZ///8mf///Jn///yZ///8mf/7/Jn77/yR89/8lefD/JHXp/yFv3f+40PX/xNz//7zW +//+91///vdf//7zW///B2///rcv5/yhx2P8iceL/JHfs/yR68/8lfPn/Jn78/yZ//v8mffz/JXv5 +/yR49P8mde3/Fmfi/1+R4//T4///t9L//7zV//+70/7/vtz//7ze/f+iutL+NlyN/zldlPXu8e/O ++vrxy/T07s329u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278z29u/M9vbvzPb278v3 +9/HT/Pz59f3++/Xx7eb31cm2/9XJtvO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP// +/wDOwK0AuaaLAr6skgu9q5EavKqPJNHEsM3Wyrf/6OLX+P7+/fT8/Pr4+Pj03Pb278n29vDN9vbw +zPb28Mz29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9fXvzPX28Mz///XJa4Kk6Tpejf+kvNT+ +vd/+/77c//+80/7/vNX//7rU///F3P//XpDj/xln3f8mder/JXjx/yV69v8lfPf/JXv1/yZ57/8h +c+X/MXre/7rV/P++2f//vdj//73Y//+92P//vNf//8Pd//+hw/X/I23W/yR04v8leOz/JXvz/yZ+ ++f8mgPz/JoD//yaB//8mgf//JoH//yaB/v8mf/v/JX34/yV78f8oeer/GGvd/5G37f/L4v//utb+ +/73Y//+92P//vNj//8Da//+00vz/L3bZ/yFx4f8meOv/JXvz/yV++P8mgPz/Jn/+/yZ//f8mffr/ +JXr1/yZ37v8dbeT/PXze/8fb/f+71f//vNX//7zV//+80/7/vNz+/8Xl//92ncb+AjyB/0xrnPPz +9fHM+fnyy/T078z29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9vbwzPb28Mz29vDM9vbwy/f3 +8tP8/Pr1/f389fHt5vfWyrf/1sq387+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A//// +AM7ArQC5posCvqySC72rkRq8qo8k0cSxzdbLuP/o4tf4/v799Pz8+vj5+fTc9/fvyff38M339/DM +9/fwzPf38Mz39/DM9/fwzPf38Mz39/DM9/fwzPb278z29/DN///0yX2PqugAO4P/Y5PE/sjn//+8 +3P7/vNT//73V//+91v//vNb//8Td//9NiOD/G2rd/yd26v8mevH/Jnz2/yZ9+P8mfPX/J3vw/yF0 +5v8ze97/vNf7/77b//+92v//vdr//73a//+82f//wt7//6TG9v8mb9X/JHPh/yZ56v8mfPH/Jn74 +/yaA/P8mgv7/JoL//yaC//8mgv//JoL+/yaB/P8mfvj/Jnzy/yh76/8YbN7/caHm/83k//+52P// +vdr//73a//+92v//vdr//7zX/f89ftv/H3Df/yd56/8mfPL/Jn74/yaB/P8mgv7/JoD9/yZ/+v8m +fPb/J3nv/yNz5v8qcdv/ts/4/8Da//+81v//vdb//73V//+70/7/xeL//5/L9/8garX+CkSJ/1Rx +oPH3+PLL+PnxzPX178z39/DM9/fwzPf38Mz39/DM9/fwzPf38Mz39/DM9/fwzPf38Mz39/DL+Pjy +0/z8+vX9/fz18e3n99bLuP/Wyrjzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8A +zsCtALmmiwK+rJILvauRGrypjyTSxbHN18y5/+ni2Pj+/vz0/Pz6+Pn59dz39/DJ9/fxzff38cz3 +9/HM9/fxzPf38cz39/HM9/fwzPf38cz29vDM9vfxzP//9MmCkarmD0eL/xhrvP53su3/y+b//7vV +/v++1///vtf//77Y//+91///xt7//06K4P8ebNz/KXnp/yd88f8nfvb/J4D4/yd/9v8ofvH/JHfp +/zJ73/+91vr/wNz//77a//+/2v//v9r//77a///D3v//r875/yxz1f8ldN7/KHrp/yd98f8ngPf/ +KIL7/yiE/v8ohP//KIT//yiE//8ohP//KIP8/yiB+f8nfvP/KXzs/x1w4f9XkeL/yuH//7zZ//+/ +2v//v9r//7/a//++2v//wtz//0aH3v8fcd//KXvq/yd+8f8ngPj/KIL8/yiE/v8og/7/KIH7/yd/ +9v8nfPD/J3jo/yFt2/+iw/T/xt///73X//++2P//vtf//73W///C2f//r9T6/y2H3P8kdML+C0aM +/194ou/7+/PL+PjyzPX278z39/HM9/fwzPf38cz39/HM9/fxzPf38cz39/HM9/fxzPf38cv4+PLT +/Pz69f39/PXx7ef318y5/9fLufO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDO +wK0AuaaLAr6skgu9q5EavKmPJNLFsc3XzLr/6ePY+P7+/PT8/Pr4+fn13Pf38Mn39/HN9/fxzPf3 +8cz39/HM9/fxzPf38cz39/HM9vbwzPb38sz///TJhZKo5AI9hv8YccX+DHjb/2qn4//D1/z/tM75 +/7fR+v+30vr/t9L6/7XS+v/A2fv/Tofb/wxf1P8YbeL/F3Hq/xdz8P8XdfP/F3Xy/xhy7P8WbuX/ +GGnY/67J8f+92fv/ttP6/7jU+v+41Pr/t9T6/7nW+v+y0Pf/KG/P/xFl1P8XbeH/F3Hp/xd08P8Y +d/b/GHj4/xh5+f8Yefr/GHn6/xh5+v8YePj/GHb0/xdz7v8Zcef/Dmbb/zt82P++1vn/ttT6/7jU ++v+41Pr/uNT6/7bT+v+/2fv/Q4Pa/w1i1v8Zb+P/F3Lr/xd18v8Yd/f/GHn5/xh5+f8Yd/b/F3Px +/xdx6v8YbeL/DmDT/4mw6f+/2vz/tdH5/7fS+v+30vr/ttH6/7jR+/+xyvf/KYDV/xN93/8Ycsb+ +ADuH/2B4oOz9/PTK9/jyzPb28Mz39/HM9/fxzPf38cz39/HM9/fxzPf38cz39/HM9/fxy/j48tP8 +/Pr1/f389fHu5/fXzLr/18u687+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7A +rQC5posCvqySC72rkRq8qY8k0sayzdjNuv/p49j4/v789Pz8+vj5+fXc9/fwyff38c339/HM9/fx +zPf38cz39/HM9/fxzPb28Mz39/PM//7zyo6YqOECPYj/D3HM/haF6P8EcNb/YJDf/8LX+v+xzff/ +tND4/7TQ+P+00Pj/stD4/77Y+/9PiNn/BFrQ/xNp3v8RbOf/EXDu/xFx8f8RcfD/EW/r/xNs5P8G +X9f/nr7r/8Ha+/+z0fj/tdL4/7XS+P+10vj/tNL4/7vW+f83edD/BlzN/xJo2/8Ra+T/EW/s/xFx +8f8Rc/X/EXX3/xF1+P8Rdfj/EXX4/xF09v8RcvL/EW/s/xFs5P8LY9n/JG7S/7LP9f+20/n/tdL4 +/7XS+P+10vj/s9H4/7/Z+/9Qitv/BFzR/xNr4P8Rbej/EXDv/xFz9f8Rdff/EXX3/xFz9P8RcO// +EWzo/xJp4P8FW9D/eaTk/7/Z+/+yz/f/tND4/7TQ+P+00Pj/tND4/7XN9/8tbdX/CnTY/xOD6P8T +c8z+ADiG/2p+oen+/fPK9/jzzPb28Mz39/HM9/fxzPf38cz39/HM9/fxzPf38cz39/HL+Pjy0/z8 ++vX9/fz18u7n99jNuv/YzLrzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCt +ALmmiwK+rJILvauRGrypjyTSxrPN2M67/+nj2fj+/vz0/Pz6+Pr69tz4+PHJ+Pjyzfj48sz4+PLM ++PjyzPj48sz39/HM+Pn0zP788suYn6nfCD2F/g900/8UifL/FYTl/wNZ2f9Vhd7/xdv7/7LO+f+1 +0fn/tdL5/7XS+f+y0fj/wtv8/1uT3f8EW87/FGve/xJu5v8Sce3/EnPx/xJz8f8Sce3/FXDn/wJf +2v+Fruf/zeH8/7HQ+P+20/n/ttP5/7bT+f+00fj/wtv9/1iP2f8EWsj/E2jY/xFr4f8Sb+n/EnLw +/xN19f8Tdvf/E3f4/xN3+f8Td/j/E3b2/xJ08v8Scez/Em3k/w5m2v8aatD/rMv0/7nV+v+10/n/ +ttP5/7bT+f+z0fj/wtv8/1yV3/8EXdH/FGzg/xJv6P8Scu//E3X1/xN3+P8Td/j/E3X1/xJy7/8S +b+j/FGzg/wVc0f9onOP/wtz9/7LR+P+10vn/tdL5/7XR+f+00Pn/utP5/zRw2P8HXNn/FIPk/xSI +8f8Td9T+ADWD/3eHo+b//fPK+Pn0zPf38cz4+PLM+PjyzPj48sz4+PLM+PjyzPj48sv4+fPT/Pz6 +9f39/PXy7uf32M67/9jNu/O/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0A +uaaLAr6skgu9q5EavKmPJNPGs83Zzrz/6uPZ+P3+/PT8/Pr4+vr23Pj48cn4+PLN+PjyzPj48sz4 ++PLM9/fxzPn69Mz5+PDLpKaq3BJChf0Nddb/FY/6/xOK7f8TbOf/A1Tb/0SA3P/H2/r/stD5/7bS ++f+20/n/ttP5/7TS+P/C3P3/bJ/h/wVby/8Ua9z/Em7l/xJx7P8SdPD/EnTy/xJz7/8Vcun/AmPe +/12W4f/Y5/r/sND5/7jU+f+31Pn/t9T5/7XS+P/B2/3/gq3m/wdaw/8SZ9P/EWrd/xJu5v8Scez/ +EnTy/xN29f8Td/f/E3j4/xN4+P8TdvX/EnTx/xJx6/8SbeP/DmfY/xhozv+myPP/utf6/7bT+f+3 +1Pn/t9T5/7TS+P/C3Pz/ap/i/wVe0P8UbeD/EnDo/xJz7v8TdvT/E3f3/xN39/8TdvT/EnPv/xJw +6P8UbeD/BF3Q/2KZ4f/E3f3/s9L4/7bT+f+20/n/ttL5/7TR+f+91vn/OXja/wZU2f8Sa+X/E4ns +/xSN+P8Sedf+ATaC/4aSpOP9+/DL+Pn0zPf38cz4+PLM+PjyzPj48sz4+PLM+Pjyy/j589P8/Pr1 +/f389fLu5/fZzrz/2M2887+tlD+9qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5 +posCvqySC72rkRq8qY8k08ezzdnPvP/q5Nn4/f389Pz8+vj6+vbc+Pjyyfj48834+PPM+PjzzPb2 +8cz7+/bM9PPtza+vrdkiSYX5CnLV/xaT//8UjvP/EXPv/xFh6P8HXd7/NXba/8fa9/+10fn/uNP5 +/7jU+f+41Pn/ttL4/8Lb/P+Er+j/B13J/xNr2v8RbuT/EnHr/xJ08P8TdvL/EnTw/xRy7P8KauP/ +MHvd/9zn9/+21Pn/uNX5/7jV+f+41fn/t9T5/7vX+v+tzPT/HGfF/w1iy/8Radf/EW3h/xJw6f8S +c+7/E3Xy/xN39f8Td/b/E3f2/xN28/8Sc+//EnDp/xJt4P8OZ9X/F2fL/6bJ8/+82Pr/t9T5/7jV ++f+41fn/ttP4/8Lc/P98quf/B1/P/xNu3/8Sb+j/EnPv/xN29P8Td/f/E3f3/xN29P8Sc+7/EnDo +/xRu3/8EXs//Zpzh/8Td/f+10vj/uNT5/7jU+f+40/n/ttL5/8DX+f88e9r/Blra/xFg5v8Rc+3/ +E47x/xWS/v8Qdtb/CTeA/pefqd759+7L+vr2zPf38cz4+PPM+PjzzPj488z4+PPL+Pn00/z8+vX9 +/fz18u7o99nPvP/Yzrzzv62UP72qkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmm +iwK+rJILvauRGrypjyTTx7TN2c+9/+rk2vj9/fz0/Pz6+Pr69tz4+PLJ+Pjzzfj488z39/LM/Pz3 +y/Dw7M23ta7XO1WG9Adnzf8Xlv//FJD2/xN99f8QY/D/Emjq/wxj4f8ga9j/wtb0/7rW+v+51fn/ +utX5/7rV+f+41Pj/wNr7/57B7/8RY8r/EWnW/xJu4f8Tcur/E3Xw/xN28/8TdvL/E3Tu/xRx5/8L +aNz/wdXw/8zh+/+10/n/utb5/7rW+f+61vn/t9T4/8Xe/f9RitT/BVnC/xRo0f8Sa9r/Em7i/xNx +6f8TdO7/E3Xx/xN28v8TdvL/E3Xw/xNy6/8ScOT/Emzc/w9l0f8ZZ8n/q8v0/73Y+/+51fn/utb5 +/7rW+f+41Pj/w9z8/4m06/8IYc//FG7e/xNx5/8TdO7/FHf0/xR59/8Ueff/E3b0/xN07v8TcOf/ +FG7e/wZfzf90pOP/xd78/7fU+P+61fn/utX5/7nV+f+30/n/wNf4/zp72f8HXdz/E2fn/xBi7v8T +fPP/FZD1/xaV//8Mas3/Gj+B+6irq9rz8uzN+/v3y/f38sz4+PPM+PjzzPj488v4+fTT/Pz69f39 +/PXy7uj32c+9/9nOvfO/rZQ/vaqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaL +Ar6skgu9q5EavKmPJNTItM3a0L3/6uTa+P39/PT8/Pr4+vr23Pn58sn5+fPN9/fxzP3998vu7urO +t7Sr12B0kesGQqn/Foby/xSU+/8Vkvf/Em/2/xFp8f8Ra+z/Emnk/w9j2P+yy+//wdv7/7jU+f+6 +1fr/utb6/7rW+v+71/v/tNH3/yNuzP8NZdH/E27e/xNy5/8Tdu7/E3fy/xN38/8TdvD/F3br/wFl +3/99q+f/7PP8/7LR+v+71vr/utb6/7rW+v+51fn/wdv9/5q+7f8NXb//EWTK/xFo0v8SbNr/Em/h +/xNy5/8TdOr/E3Ts/xN07P8TdOr/EnHl/xJu3v8Ta9b/C2LL/ydvyf+20vj/u9f7/7rW+v+61vr/ +utb6/7jV+v/C2/3/l73u/w5kz/8Sbdz/E3Lm/xN17v8Td/T/FHr3/xR59/8TePP/E3Xt/xNy5f8U +btv/B2DM/4Cs5//F3f3/uNT5/7rW+v+61fr/utX5/7fV+v/B2Pj/NXjZ/wlh3v8Tauj/EWjv/xJu +9P8Vkvb/FJT7/xaF8P4GQqn/O1qL87Oyqtjv7+rO/f33y/f38cz5+fPM+fnzy/n59NP8/Pr1/f38 +9fLv6Pfa0L7/2c+987+tlD+8qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posC +vqySC72rkRq8qY8k1Mi1zdrQvv/q5Nr4/f389Pz8+vj6+vfc+fnzyff38s3+/vnL7e3pzqurp9qg +oJzcKF+0+QtPwf8VhOv/FZj8/xWV+P8Tc/b/Emzz/xFu7v8Vbuf/Al/a/5W56v/O4v3/ttP5/7rX ++v+61vr/utf6/7jW+f/D3fz/RIXU/wdgzP8Ubtv/EnLk/xN27P8TePH/E3nz/xN48f8Vd+3/DG/l +/yt84P/u8vj/w937/7jW+v+62Pr/utf6/7rX+v+31vn/xN79/02I0P8EWL3/FGfM/xFo0v8SbNn/ +Em/f/xJx4v8ScuT/EnLk/xJw4v8Sbt3/EmvY/xNp0f8HXcT/Pn7N/8Hc/P+41/n/utf6/7rX+v+6 +1/r/udb6/7/b/P+kx/L/FWnQ/xFs2/8TcuX/E3bt/xN58/8Ue/f/FHr2/xN48v8Tduv/EnHj/xNt +2v8KYcv/jrns/8Pe/f+41vn/utf6/7rX+v+61vr/utb7/7/X9/8mctj/DGbg/xJs6v8Ra/D/E3L1 +/xWV9/8VmPz/FILp/xBSwv8YU7D/lZeY4a6tqNnt7ejO/v75y/f38sz5+fTL+fn10/z8+vX9/fz1 +8u/o99rQvv/Zz73zv62UP7yqkBq9q5EPvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+ +rJILvauRGrypjyTVybbN29HA/+vl2/j9/fz0/Pz6+Pr699z39/LJ/v75zOzs586oqKTao6Od29nb +29Ixbc34C0y8/xaG7P8Vm/z/Fpj4/xN29/8Sb/X/EnHw/xVx6v8BY93/ap7j/9zq/P+01Pr/vNj6 +/7vY+v+72Pr/uNb5/8bh/v9ypOH/BV3H/xRt2P8SceH/E3Xq/xN47/8TevP/E3rz/xN48P8XeOv/ +Amjg/6XE7v/x9/z/stT6/7zZ+v+72Pr/u9j6/7rY+f+/2/z/rMzz/xpkvv8MXsD/E2bJ/xBoz/8R +a9X/EmzZ/xJu2/8Sbtv/Em3Z/xFr1v8RaND/E2fK/wNYvP9rntv/x+H+/7jW+f+72Pr/u9j6/7vY ++v+61/r/vtv7/63P9f8abdH/EGzb/xNz5f8Td+3/E3ry/xR79v8Ue/X/E3nw/xN16v8TceL/EGvW +/xRny/+kx/H/wNz8/7nX+v+72Pr/u9j6/7rX+v+92vv/u9P0/xtu2P8Pa+L/E2/r/xFt8v8Tdfb/ +Fpj3/xWb/P8Vhev/DU69/yNhxv7O0tPZpqWf2qeopNvs7OfO/v75zPf38sv5+fXT/Pz69f39/PXy +7+j329G//9rQvvO/rpQ/vKqQGr2rkQ++rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6s +kgu9q5EavKmPJNXJts3b0cD/6+Xb+P39/PT8/Pr4+fn13P7++cjq6uXQp6ej26Kin9zm5eDP+Pr5 +zC5px/gLTb7/Fojt/xWe/P8Wm/n/E3j5/xJy9v8Sc/H/FHPr/wlq4v82gN3/3ur5/7nX+v+82fr/ +vNn6/7zZ+v+72Pr/wt38/6LG8f8SZMf/EGrS/xJw3f8TdOf/E3ju/xN68v8Te/T/E3ry/xV57v8L +cOf/NIPj//v6+v/Q5Pv/uNb7/77Z+/+82fv/vdn7/7rX+f/G4P7/jLXm/wlYt/8OX8D/E2bH/xBm +yv8RaM7/EWjQ/xFp0P8RaM//EGfM/xJmyf8NYML/FmK+/6zM8//B3Pz/vNj6/73Z+/+92fv/vdn7 +/7zZ+/++2vv/uNX4/yd10/8Ma9n/FHTl/xN37f8TevP/FHz1/xN79P8TePD/E3Xo/xNx3/8MZ9L/ +JnLN/7jV+P++2vv/vNn6/7zZ+v+82fr/utj6/8Xf/P+tyvD/DGfZ/xJv5f8Scez/EnDy/xN49/8W +mvj/FZ78/xWH6/8PUL//HlzB/+zv8dPp6OLOoqKe3Keno9vp6eXP/v75yvj49NP8/Pr1/f389fLv +6Pfb0b//2tC+87+ulD+8qpAavauRD76skgS6p4wA7urjAP///wD+/v4A////AM7ArQC5posCvqyS +C72rkRq8qY8k1cm2zdzSwf/r5dv4/f389Pv7+fj///3b5+fjzaWlotylpaLb5ufj0P//+8vx8/LO +LmrK+AtNv/8Xiu3/GKD9/xed+f8Ue/n/FHT3/xR28/8Tde3/E3Pn/xBs3P/G2fL/zOL8/7vY+v++ +2fv/vtn7/77a+/+92fv/xN38/zp+0P8JYsv/FG/Z/xNy4v8Ud+v/FHnw/xR78/8Ue/T/FHrx/xd6 +7f8DauL/lb3t/////f/A2vv/vNn7/7/a+/++2vv/vtr7/7zY+v/J4v//ha/j/w5buP8GWbn/EWLB +/xNlxf8TZcb/E2XH/xNlx/8UZcX/DWDA/wNWuP96qOD/yeH//7vY+v++2vv/vtr7/77a+/++2vv/ +vtr7/77a+//A2vr/NH7X/wpp2P8VdeX/FHfs/xR68v8Ue/T/FHrz/xR47f8Tc+T/FXDc/wdizP9I +idb/x+D9/7zY+v++2vv/vtn7/77Z+/+61/r/0eX9/5O56v8EZdv/FnPo/xN07v8Tc/T/FHr4/xec ++f8YoP3/Fonr/w5QwP8fXcP/5ejq1P///cnm5uLPpaWi26Wlotvn5+PO///80vv7+fX9/fz18u/p +99vSwP/a0b/zv66UP7yqkBm9q5EOvqySBLqnjADu6uMA////AP7+/gD///8AzsCtALmmiwK+rJIK +vauRGbypjyLVybfN3dLC/+vl3Pj9/fv0/v799+bm49+jo6DZp6ej2+rq5c////rL+/r0zPP1880v +a8r4DE6//xiL7f8Yof3/GJ35/xR9+f8Ud/j/FHn1/xN48P8YeOv/Amfe/4m06f/m8f3/uNb7/8Db ++/+/2/v/v9v7/7zZ+v/K4///fKrj/wZexP8VbdT/E3He/xR15/8Uee7/FHvy/xR89P8UfPP/FXvw +/xF26v8ad+T/3+n3//T4/P++2vv/vtr7/8Dc+/+/2/v/v9v7/73Z+v/K4///ncHs/zh5xv8KWrf/ +BFe3/wVZuf8FWrr/BFm6/wRZuf8YZb//e6nh/8nh/v+72fr/yuH7/8ff+/+92vv/v9v7/7/b+/+/ +2/v/vdr7/8bf/P89htv/CWrZ/xV25v8UeOz/FHvx/xR78v8UevD/FHfq/xNz4f8Vb9j/Bl/H/3Wn +4v/L4///vNn6/7/b+/+/2/v/wNr7/7nW+//e7P3/bKLk/wNn3v8Xduv/E3fw/xR29f8UfPj/GJ35 +/xih/f8Xiuz/D1HB/yBew//n6uzU/fz2yv7/+szq6uXPp6ej26OjoNrl5eLW////9Pz8+/Xz7+n3 +3NLB/9vRwPO/rpQ9vKqQGL2rkQ6+rJIEuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAr6skgm9 +q5EXvKmPH9XKt8vd08L/6+Xc+P7+/fT6+vn3q6yq56eno9js7OjP///6y/j488z8+/XM8/X0zS9r +y/gMTsD/GIzu/xii/f8Yn/n/FH/6/xR4+P8Ue/f/FHrz/xZ57f8Kb+T/PYnh/+/0+v++2/v/v9v7 +/7/b+/+/2/v/v9v7/8Hd/P+41fj/Im/I/w1nzP8Tb9n/E3Pi/xR46v8Ue/D/FHz0/xR99P8UfPP/ +F3zw/whx6f9Ikej//f38/+30/P/A3Pz/vdr8/8Dc/P+/3Pz/v9v8/73a+//H4f//xN79/5e96/9n +nNn/UIvQ/0yJz/9XkdT/eanh/7DQ9v/K4///vtv7/7vZ+//v9vz/0ub8/7vZ/P/A2/z/v9v8/7/b +/P+92vz/yeL+/1CT4f8Iatv/Fnfn/xR57f8Ue/D/FHvx/xR57f8Tdub/E3Hd/xFs0v8TZ8b/pcjy +/8Xg/v++2vv/v9z7/7/c+//A3Pv/vNr7/+Hs+v87ht//Cm7j/xV47f8TefL/FHf2/xV++f8Yn/n/ +GKL9/xeL7P8PUcL/IF7E/+fq7NT//vfK9/fzzf//+svs7OjPqKil2aOjoeDo6Ob1////9PHu6Pfc +08L/29LA87+ulDq8qpAVvauRDL6skgO6p4wA7urjAP///wD+/v4A////AM7ArQC5posBvqySB72r +kRS8qY8Z1cq3yt3Tw//r5dz4////9Pb19Pe2trXk6+vnzf//+8z4+PTN+fn2zPz79szz9fXOL2zL ++AxPwP8Yju7/GKT9/xmh+f8Vgfr/FHr6/xR9+P8VfPX/FHvw/xV56v8Jbt//wtfy/93t/v+52fz/ +wd38/8Dc/P/A3Pz/vdr7/83l//9sod7/BF7D/xVv0/8Sctz/E3bl/xR67P8UfPH/FH70/xR+9P8U +ffP/GH7v/wRv5/9wqe3////9/+/2/P/J4fz/vNr8/8Dc/P/B3fz/wd38/7/b+//A3Pz/yeP//83l +///L5P//yuP//8zk///M5f//xeD+/77b+/+62fz/4O79/+7w9f/C2vf/wd79/8Hd/P/B3fz/wd38 +/77b/P/M5P7/Xp7n/wZs3f8Weef/FHrs/xR77v8Ue+3/FHjp/xN14v8Ucdj/CGTK/z6D0f/H4f3/ +v9z8/8Dc/P/A3Pz/wNz8/73b/P/M5P3/y930/xFw3f8Tduf/FHnu/xR79P8Uefj/FYD6/xmh+f8Z +pP3/F4zs/w9Swv8gX8X/5+rt1P/++Mr5+fbM+Pj0zf//+8zt7erOra2r36ysq/jm5ub1+PTv99zS +wf/b0sDyv66UNbyqkBK9q5ELvqySArqnjADu6uMA////AP7+/gD///8AzsCtALmmiwG+rJIFvauR +D7ypjxPWyrjI3tPD/+zm3vj+/v71+Pj39vX09PH////u/Pz77/39/e/8/Pzv/v787/f4++8ybc39 +DU/B/xmO7v8ZpP3/GqH5/xWC+/8UfPv/FH/5/xV+9/8UffP/F33u/wRv4/9joOb//fz9/7zb/P/C +3fz/wd78/8He/P/B3fz/w+D9/7vY+f8kccf/DGfK/xNw1v8TdN//E3nn/xR87v8UfvL/FID0/xSA +9P8Uf/P/F3/w/wRx6P97sO/////9//z9/f/b6/z/w9/8/73c/P+/3fz/wd78/8Le/P/A3fv/v9z7 +/8Dd+//A3fv/wN37/7/c+/+92/z/v938/9vr+///////e6je/6XH7//H4///v9z8/8Dd/P/A3fz/ +vdv7/83l/v9rqOz/Bm7g/xZ76f8Ue+z/FHzt/xR76v8TeOT/E3Pd/xRw0/8GYMP/hbLm/8zm//+/ +3Pv/wd78/8Hd/P/D3vz/utj8/+nz/v+OuOv/AWvg/xd77P8Ue/H/FH32/xR7+f8Vgvv/GqH6/xmk +/f8Yje3/EFLD/yFgxf/q7fPy///+7vz8/O/9/f3v/Pz77////+7z8/Lwurm5966urvfc2NP44NbF +/9vRwPG/rpQvvKqQDb2rkQi+rJICuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAL6skgO9q5IN +u6mOB9XKt6/e1sX95d/S+f39/fX+/v71////9v7+/vb////2////9v7+//b////2+fv99jNuzv4N +UML/GY7u/xml/f8bofr/FoP7/xR9+/8Uf/r/FYD4/xV/9v8VfvH/E3rq/xN14P/a5vb/4e/+/7zb +/P/E3/3/wt/9/8Pf/f/A3Pz/zuf//4Ox5f8GX8D/FG7P/xJx2P8TduH/FHrp/xV97/8Uf/P/FYD1 +/xSA9f8Vf/T/F3/x/wZz6v9rqO//+Pr8/////v/z+P3/3ez9/8zj/f/C3v3/v939/77c/f++3P3/ +v9z9/77c/f+/3P3/wt79/9Dl/f/r8/z//////77W8v8JaNL/psnx/9Lo///H4Pz/yeL9/8ni/f/G +4P3/1Oj//4S38v8JcuT/Fnzq/xR76/8Ue+r/FHjm/xN13/8Ucdb/C2fK/yx3yv/B3Pv/w9/9/8Le +/f/D3/3/w9/8/8Le/P/D3vz/9Pf7/z2L4v8Kc+b/F3zv/xR+9P8Ufvf/FHz5/xaD+/8aofr/GaX9 +/xmN7f8QU8T/IWDG/+zv9fj////2/v7+9v////b////2/v7+9v////b39/b1vb6+9rOvqfnh2Mf9 +29HA4L+tkxu8qpALvauRBb6skgG6p4wA7urjAP///wD+/v4A////AM7ArQC5posAvqySAr2skgm7 +qI0AyrymXeHZyv/c0sL66+bc+fr59/b7+vn1/Pv69fz7+vX8+/r1+/r69f38+vX29/n1M27O/g1Q +w/8akO7/Gqf9/xuk+v8WhPv/FX/7/xWB+/8Wgvr/FoH4/xWA8/8YgO//BXHl/2ml6f////7/w9/9 +/8Le/f/D3/3/w9/9/8Pf/f/B3v3/y+T+/0eI0P8GYsP/FnHR/xNz2v8UeOL/FXzq/xV+7/8VgPP/ +FYH1/xWB9f8VgfT/GIHy/wZ17P89ke3/xdz4/////v////7//f39//D2/f/l8f3/3u39/9rr/f/Z +6v3/2+v9/+Lv/f/v9v3////+//////+81vX/HHvh/wNr3v+gxfH//P7///b5/f/6+/3/+vv9//f5 +/f/8/v7/s9L2/whz5/8Xfer/FHvp/xR55f8UduD/E3LY/xVvz/8GYMD/hrPm/87n///A3fz/w9/9 +/8Pf/f/E3/3/vNv9/+fz/v/A1/P/CXHh/xd+6/8VfvH/FoD3/xWA+f8Vfvr/FoT7/xuj+v8ap/7/ +GY/t/xBTxP8hYMb/6ezx9/38+/X7+vn1/Pv69fz7+vX8+/r1+vr59f39+/Xn5N34vrao+eLZyv/T +xrOXu6mOAb2skgu9q5EDvqySALqnjADu6uMA////AP7+/gD///8AzsCtALmmiwC+rJIAvauRA72r +kQm9q5AH1su4quLZyv/d08L/39bG/eHYyPzg2Mj84NjI/ODYyPzf18j84tnI/NvUyPwwasj/D1HF +/xqQ7v8ap/7/G6T6/xaF+/8Vf/z/FYL8/xaD+/8Wg/n/FoP2/xWB8v8Wfuz/DnXi/87g9f/w9/// +vNv9/8Tf/f/D3/3/w9/9/8Le/P/I4v7/tdT2/x5uxP8NZ8f/FHHT/xN02v8UeeL/FHzp/xV/7/8V +gfP/FYL1/xWC9f8VgvT/GYPz/wt77/8Qe+z/Y6bw/8Lb+P/7+/3////+/////v////7////+//// +/v////7////+/////v/s8/z/gbbx/xB45/8UfOn/BXTo/4C28f////7///79/////f////3///79 +/////v/N4fj/DXfo/xV86P8UeuX/FHff/xNz2P8VcdL/B2PE/0GFz//K4/7/wt/9/8Pf/f/D3/3/ +w9/9/8Lf/f/E4P3////+/1eb5/8Hc+b/GIHv/xWA9P8Wgfj/FYH6/xV++/8Whfv/G6T6/xqn/v8Z +j+3/EVTF/yJgx//Szsf85NnI/N/XyPzg2Mj84NjI/ODXyPzh2Mj839bH/N/VxP/j2sr/3NPC2cGw +lxy8qpAFvauSBb2rkQG+rJIAuqeMAO7q4wD///8A/v7+AP///wDOwK0AuaaLAL6skgC9q5EBvauR +BLyqkAW+rZMK08azid/Wx+rg2Mj/4NfH/+DXx//g18f/4NfH/9/Xx//i2Mf/29TH/zFqyP8PUsb/ +GpDv/xqn/v8bpfr/Fob7/xWA/P8Vg/z/FoT7/xaF+v8Whfj/FYP1/xiC8P8Jduj/R5Tn/////f/X +6v3/wN79/8bh/f/F4f3/xeH9/8Lf/P/P6P//lr/r/wxjv/8SbMr/FHHT/xN02/8UeeP/FHzp/xV/ +7v8VgfL/FYL0/xWD9f8Vg/X/GIT0/xaC8/8HeO//Dnvt/zuS7/9zsPP/osn2/8Hb+P/R4/n/1+b6 +/8zh+f+v0ff/dbHz/ymI7v8Gd+3/FYDw/xeC8f8Rf/D/KYrx/0uc8v9ImvH/SJrx/0iZ8P9ImO// +SZnv/z+T7f8Ue+j/FHrl/xN34P8TdNn/FHDS/w9pyP8XasP/r9D0/8rl/v/D3/z/xeH9/8Xg/f/G +4P3/vdz9//H4///H3PT/C3Tj/xd/7f8VgvP/FoP2/xaD+f8Vgvv/FYD8/xaG/P8bpfr/Gqf+/xmP +7v8RVMb/ImHI/9LOxv/k2cf/39bH/+DXx//g18f/4NfH/+DXx//g18f/4NfH9dfMuqvCsZkgu6mP +Ab2rkga9q5EBvauRAL6skgC6p4wA7urjAP///wD+/v4A////AM/BrgC6p4wAv62TAL6skgC+rJIB +vqyTA72rkgO8qo8Aw7KaG8e4oULHt6BKx7egSse3oEvHt6BLxregTMm4n0m+s6NPJWLG5xFUx/8Z +ke/+Gqj+/xum+v8Wh/z/FYH8/xWE/P8Whvz/Fob7/xaH+v8Whfj/FYP0/xmC7/8Ec+X/mcLw//// +///L5P3/w+D+/8bi/v/F4v7/xuL+/8Lf/f/S6v//fa7i/wVgvf8Tbcv/E3HT/xN12v8UeeH/FHzo +/xV/7f8VgfH/FYLz/xWE9f8VhPb/FYT2/xiF9v8WhPT/DX7y/wV58P8Hee//DHvv/xR/7/8YgfD/ +EX7w/wh68P8FevH/EIH0/xmF9v8WhPb/FYT3/xaE9v8RgfX/Cn3z/wp88f8Ke/D/Cnru/wp57P8J +eOr/C3fn/xR65P8TeN//E3TZ/xNx0f8Tbcr/BmC9/4q35//R6v//wt/9/8bi/v/F4v7/x+L+/8Hf +/f/X6v3///79/0aU5/8Jd+j/GIPw/xWE9f8Whfn/FoX6/xWE/P8Vgfz/Fof8/xum+/8aqf7/GZDu +/hJUx/8gYMfzu7GjVsq4n0jGt6BMx7egS8e3oEvHt6BKx7egSse3oUfFtZ0ovauRALyqkQC+rZMF +vqySAL6skgC+rJIAv62TALuojQDu6uMA////AP7+/gD///8Ay72pALWhhQC6qIwAuaaLALmmiwC5 +posAuqeLAbqnjAS4pYkAuKSIALikiAC4pIgAuKSIALikiAC3pIgAu6WHAK2fjQAgX8bcE1XI/xmS +7/0aqv7/G6f7/xaI/P8Vgvz/FYb8/xaI/f8WiP3/Foj7/xaH+v8Vhff/FoTy/xN/7f8VfOX/1ub4 +//r9///G4v7/xeH+/8fi/v/G4v7/xuL+/8Tg/f/T6v//bqTd/wRfvP8Tbsr/E3LR/xN12P8Ued// +FHvl/xR/6v8VgO7/FYLx/xWD9P8VhPX/FYX2/xWF9v8Xhvb/GIf2/xiG9v8XhfX/FYT1/xSE9f8V +hfb/GIb2/xmH9/8Xhvj/Fob4/xaG+P8Whvj/FYb4/xaF9v8YhfX/F4Tz/xeD8f8Xge//F4Dr/xd+ +6P8WfOP/E3je/xN01/8TcdD/FG/K/wRgvP9vpd3/0+r//8Tg/f/G4v7/xuL+/8fi/v/E4f7/y+T+ +//////+Zw/D/BHTl/xmD7/8VhPT/Fob4/xaH+v8Wh/z/FYb8/xWC/P8WiPz/G6f7/xqq/v8Zke7+ +E1XI/yBgxu6toI0Iu6WHALekiAC4pIgAuKSIALikiAC4pIgAuKSIALiliQC6p4sEuqeMArmmiwC5 +posAuaaLALmmiwC6qIwAtqKGAO3p4QD///8A/v7+AP///wDb0MIAy72pAM/BrgDOwa0AzsGtAM7B +rQDOwa0AzsGtAM7BrgHOwa4CzsGuA87BrgPOwa4DzsGuA83BrgTRwq0DwruwCiJhyd0TVcj/GpLv +/Ruq/v8cp/v/F4n8/xaD/P8Wh/z/F4n9/xeJ/f8Xifz/F4n7/xeH+f8Vhvb/GYXy/wx76/85j+j/ +9fj8//H4///G4v7/xuL+/8jj/v/H4/7/x+P+/8Xi/f/S6v//cKbd/wZhvP8Rbcf/FXLP/xN11f8U +eNz/FHvh/xV+5/8VgOv/FYHu/xaC8P8Wg/L/FoT0/xaF9f8WhfX/FoX2/xaG9v8Whvb/Fob2/xaG +9/8Whvf/Fob3/xaG9/8Whvf/Fob3/xaG9/8Whfb/FoX0/xaD8v8WgvD/FYHt/xWA6v8Vfeb/FHvh +/xR42/8TddX/FHLP/xRvyf8EYLv/ZJ3Y/9Lq///F4v3/x+P+/8fj/v/I4/7/xuL+/8jj/v/5/f// +2uj5/xd95v8UgO3/F4Xy/xaG9/8XiPr/F4j7/xeI/f8Wh/z/FoP8/xeJ/P8cp/v/G6r+/xqR7v4T +Vcj/ImHJ7sK6sBPRwq0DzcGuBM7BrgPOwa4DzsGuA87BrgPOwa4DzsGuAs7BrQDOwa0AzsGtAM7B +rQDOwa0AzsGtAM/BrgDMvqkA8u/qAP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////Af///wDz+P8HJWbP3RJVx/8ak/D9 +G6v+/xyn+/8Xivz/FoT8/xaH/f8Xif3/F4n9/xeL/f8Xifz/F4n6/xeI+f8Whvb/Goby/wZ46f9g +pez////+/+z1/v/G4v7/xuL+/8jj/v/H4/7/xuP+/8Xi/f/T6///gLHi/w5mvf8MacP/FnLM/xNz +0f8Tdtf/FHnd/xV84v8Vf+b/FYDp/xaC7P8Wg+7/FoTw/xaF8f8WhfL/FoXz/xaG8/8WhvT/Fob0 +/xaG9P8WhvT/Fobz/xaF8/8WhfL/FoXy/xaE8P8Wg+7/FoLs/xWA6f8Vf+b/FXzi/xR63f8Ud9j/ +E3PS/xZyzf8Qa8X/B2K8/26l3P/R6f//xuL+/8bj/v/H4/7/yOP+/8Xi/v/H4/7/8vj///f5/f8/ +kun/C3vr/xmG8v8Wh/b/F4j5/xeJ+/8Xifz/F4n9/xaH/f8WhPz/F4r8/xyn+/8brP7/GpLv/hJV +x/8lZs/u8/j/Ef///wD///8B////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A//7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A +/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD9/f4B/v7+AO/z+wclZs/dElXI/xqU8P0b +rP7/HKj7/xeK/P8Whfz/Foj9/xeK/f8Xiv3/F4v9/xeL/f8Xivz/F4r7/xeJ+f8Wh/b/Gobx/wR3 +6P98te///////+z2/v/I4/7/xeL+/8jk/v/H4/7/x+P+/8Xh/f/U7P//nsbs/yV1w/8EY73/FHDI +/xVzzv8TdNL/E3fX/xR53P8UfOD/FX7j/xV/5v8VgOj/FYLq/xaC6/8Wg+z/FoPt/xaD7f8WhO7/ +FoTu/xaD7f8Wg+3/FoPs/xWC6/8Vger/FYDo/xV/5f8UfuP/FHzg/xR53P8Ud9j/E3XT/xVzzv8V +ccn/B2W//xdsv/+Kt+X/1Ov//8bi/f/H4/7/yOP+/8nk/v/F4v7/yeT+/+/3/v////7/Yaft/wZ5 +6f8ah/L/FYf2/xeJ+f8Xivr/F4r8/xeK/f8Xiv3/Foj9/xaF/P8Xivz/HKj7/xus/v8ak+/+ElXI +/yVmz+7v8/sR/v7+AP39/gH+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A/v7+AP7+/gD+/v4A +/v7+AP7+/gD+/v4A/v7+AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PT8ByZn0N0TVsn/GpXw/Rut +/v8cqfz/F4v8/xaG/P8Wif3/F4v9/xeL/f8XjP3/F4z9/xeL/f8Xi/3/F4r7/xaJ+f8Wh/X/GYfw +/wZ66P+JvPD///////D3/v/M5v7/xeL+/8nk/v/I5P7/yOT+/8Xi/f/S6///v935/1aW0/8LZbv/ +CWfA/xRxyP8Vc83/E3TR/xN21P8UeNj/FHrb/xR73v8UfeD/FH7i/xV+4/8Vf+T/FX/k/xV/5f8V +f+X/FX/k/xV/5P8UfuL/FX7h/xR84P8UfN7/FHrb/xR42P8UdtX/FHTR/xVzzf8Vccn/C2nB/wZj +u/9CiMz/sdPz/9Ts///F4v3/yOT+/8jk/v/J5P7/xeL+/87n/v/z+f7//////3ez7/8Eeen/Gofy +/xaH9f8Xifn/F4v7/xeL+/8XjP3/F4v9/xeL/f8Wif3/Fob8/xeL/P8cqfz/G63+/xqU7/4TVsn/ +JmfQ7vD0/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A/v7/Af///wDw9PwHJmfR3RNWyv8alfD9G63+ +/xyp/P8XjPz/Fob8/xaK/f8XjP3/F4z9/xeN/f8Xjf3/F439/xeM/f8XjPz/F4v7/xaK+P8XifX/ +GIjw/wZ76P+IvPH///////f7/v/U6v7/xeL+/8nk/v/J5P7/yeT+/8bi/f/M5v//1Ov//5vE6/87 +hMn/CWW7/wdmv/8RbsX/FnPK/xV0zf8UddD/E3bS/xR31P8UeNb/FHnX/xR52f8Uetn/FHra/xR6 +2v8Uetn/FHnZ/xR51/8UeNX/FHfU/xR20v8UddD/FXTN/xVzyv8Sbsb/CGa//wdku/8xfsf/irnl +/9Do///P6P//xuL9/8nk/v/K5P7/yeT+/8Xi/v/W6v7/+vz+//////97tfD/Bnrp/xmH8f8WiPX/ +F4r5/xeL+/8XjPz/F4z8/xeN/f8XjP3/F4z9/xaK/f8Whvz/F4z8/xyp/P8brf7/GpTv/hNWyv8m +Z9Hu8PT8Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD+/v8B////APD0/AcmZ9HdE1bK/xqW8f0br/// +HKv9/xeO/v8WiP7/Fov+/xeO/v8Xjf7/F47+/xeO/v8Xjv7/F47//xeN/v8Xjf3/F438/xaM+f8X +ivb/GIny/wZ86v92s/D////////////h8P//yeX//8jk///L5v//y+b//8nl/v/I5P7/1Oz//8/o +//+WwOr/R43O/xRsv/8FY7z/CGfA/w9txf8Tccj/FXPK/xV0zP8VdM3/FXXO/xV1z/8Vdc//FXXP +/xV1zv8Vdc7/FnXN/xV0zP8Vcsr/EnDH/w5sw/8HZr//BWO8/xRsv/9Cis3/jLvn/8vl/f/W7f// +yeX//8nk/v/L5v//y+b//8fk///K5f//4/H////////9/f7/aa3v/wV86/8ZifL/For2/xaL+f8X +jfz/F439/xeN/v8Xjv//F47+/xeN/v8Xjv7/Fov+/xaI/v8Xjv7/HKv9/xuv//8alvH+E1bK/yZn +0e7w9PwR////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Bydo0t0UV8v/G5jz/Ryx//8d +rf7/GJD//xeK//8Xjf//GI7//xiP//8YkP//GJD//xiQ//8YkP//GI///xiP/v8Yj/3/GI78/xeM ++v8Xi/f/Gorz/wV97P9YpO//7fT9///////w+P//1uv//8jl///K5f//zOf//8zm///J5f7/yub/ +/9bt///U7P//sdP0/3it4P9Cis7/HnPC/wxovf8GZb3/BWW9/wZmv/8IZ8D/CWnB/wppwf8JaMH/ +CGjA/wdnwP8FZb7/BWS9/wdlvf8Qar7/JXfE/0iO0P96r+H/r9L0/9Pr///X7v//y+b//8nk/v/M +5v//zOb//8nl///I5f//1+z///L5////////5/H8/06f7v8Gfez/Gov0/xeL9/8Xjfr/GI78/xiP +/f8Yj/7/GI///xiQ//8YkP//GI///xiO//8Xjf//F4r//xiQ//8drf7/HLH//xuY8/4UV8v/J2jS +7vD1/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHJ2jS3RRXy/8cmPP9HbH//x6t +/v8YkP//F4v//xeN//8Yjv//GI///xiQ//8YkP//GJD//xiQ//8YkP//GJD//xiP/v8Yj/3/GI/8 +/xiO+v8XjPj/G4z0/wiA7v8yku3/w934////////////6PT//9Lq///J5f//yub//8zn///M5/// +yuX+/8rm///S6///2fD//9Ps//++3fn/oMju/4K04/9notv/VpfU/0SMz/89iM3/O4bM/z2Izf9C +i8//UZPS/2Ce2P9zq9//jrzo/6rP8f/E4Pv/1e3//9nw///S6///yub//8rl/v/M5///zOf//8rm +///J5v//0+r//+n1/////////////7rZ9/8rj+z/CYHu/xuM9f8XjPj/GI76/xiP/P8Yj/3/GI/+ +/xiQ//8YkP//GJD//xiQ//8Yj///GI7//xeN//8Xi///GJD//x6t/v8dsf//HJjz/hRXy/8naNLu +8PX8Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD+/v8B////APD1/AcnadPdFFjM/xyY8/0dsf//Hq3+ +/xiR//8XjP//F47//xiP//8YkP//GJH//xiR//8Ykf//GJH//xiR//8Ykf//GJH//xiQ//8YkP3/ +GJD8/xiO+/8Xjfj/G472/w+G8f8Sg+z/f7rz//P4/f///////P7//+bz///U6///yub//8nl///L +5v//zOf//8vm/v/K5f7/y+b//9Dq///V7f//2fD//9nw///X7v//1e3//9Lq///Q6f//0ur//9Xt +///W7f//2O///9nw///X7///1Oz//87p///K5v7/yuX+/8vm/v/M5///y+b//8jl///L5v//1uz/ +/+n1///+/////////+/1/f91tfL/D4Ls/xGG8f8bjfb/F435/xiP+/8YkPz/GJD9/xiQ//8Ykf// +GJH//xiR//8Ykf//GJH//xiQ//8Yj///F47//xeM//8Ykf//Hq3+/x2x//8cmPP+FFjM/ydp0+7w +9fwR////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Bydp090UWMz/HJnz/R2y//8erv7/ +GJL//xeN//8Xj///GJD//xiR//8Ykv//GJL//xiS//8Ykv//GJL//xiS//8Ykv//GJL//xiR//8Y +kf7/GJD9/xiQ+/8Xjvn/GY73/xeL9P8HgO7/NJXu/6rR9////v/////////////t9///3e///9Dp +///K5v//yeX//8rm///L5v//y+b//8vm/v/K5f7/yuX+/8rm/v/L5v7/y+f//8zn///L5///y+b+ +/8rm/v/K5f7/yuX+/8rm/v/L5v7/y+b//8vm///J5v//yOX//8rm///S6v//3/D//+/3//////// +//////z8/v+hzPb/LpLt/weB7v8YjPT/GY73/xeO+f8YkPz/GJD9/xiR/v8Ykf//GJL//xiS//8Y +kv//GJL//xiS//8Ykv//GJH//xiQ//8Xj///F43//xiS//8erv7/HbL//xyZ8/4UWMz/J2nT7vD1 +/BH///8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHKGnT3RVYzf8cmvP9HbP//x6v/v8Y +k///F47//xeQ//8Ykf//GJL//xiT//8Yk///GJP//xiT//8Yk///GJP//xiT//8Yk///GJP//xiS +//8Ykv7/GJH9/xiR/P8YkPr/GI/5/xuP9v8QiPL/CoLt/0Oe7/+v1Pf/+/z+////////////+/3/ +/+32///h8f//1+z//9Dp///L5///yub//8nm///J5v//yeb//8rm///K5v//yub//8rm///K5v// +yub//8nm///J5v//yeb//8rm///M5///0en//9nt///j8f//7/f///3+//////////////X5/v+m +z/b/P5vv/wiC7v8RiPL/G4/2/xiP+f8YkPr/GJH8/xiR/f8Ykv7/GJL//xiT//8Yk///GJP//xiT +//8Yk///GJP//xiT//8Ykv//GJH//xeQ//8Xjv//GJP//x6v/v8ds///HJrz/hVYzf8oadPu8PX8 +Ef///wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD+/v8B////APD1/AcoatTdFVnO/xya8/0ds///Hq/+/xiT +//8Xjv//F5D//xiR//8Ykv//GJP//xiT//8Yk///GJP//xiT//8Yk///GJP//xiT//8Yk///GJP/ +/xiT//8Ykv7/GJL+/xiS/f8Ykfz/GJD6/xmQ+P8aj/X/DYfx/wuE7v87mu//kMT1/93s+/////// +////////////////+fz///H4///q9f//5PP//+Dx///c7///2u7//9nt///Y7f//2O3//9nt///a +7v//3e///+Hx///m8///7Pb///P5///7/f///////////////////////9Xo+/+Fv/T/M5bv/wmD +7v8OiPL/Go/2/xmQ+P8YkPr/GJH8/xiS/f8Ykv7/GJL//xiT//8Yk///GJP//xiT//8Yk///GJP/ +/xiT//8Yk///GJP//xiS//8Ykf//F5D//xeO//8Yk///Hq/+/x2z//8cmvP+FVnO/yhq1O7w9fwR +////AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP7+/wH///8A8PX8Byhq1N0VWc7/HJrz/R2z//8er/7/GJT/ +/xeP//8Xkf//GJL//xiT//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP// +GJT//xiU//8YlP//GJP+/xiT/f8Ykv3/GJL7/xiR+v8akfj/GpD2/w+J8/8Hg+//G4zu/1Cl8f+U +x/X/z+X6//b5/v////////////////////////////////////////////////////////////// +///////////////////////////////////v9f3/xeD5/4nB9f9IofD/F4ru/weE7/8QivP/GpD2 +/xmR+f8Ykfr/GJL8/xiT/f8Yk/7/GJP+/xiU//8YlP//GJT//xiU//8YlP//GJT//xiU//8YlP// +GJT//xiU//8YlP//GJP//xiS//8Xkf//F4///xiU//8er/7/HbP//xya8/4VWc7/KGrU7vD1/BH/ +//8A/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A/v7/Af///wDw9fwHKGrV3RVZz/8dmvT9HrP//x+v/v8Zlf// +GJD//xiS//8Zk///GZT//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Z +lP//GZT//xmV//8ZlP//GZT//xmU/v8Zk/3/GZP9/xmS/P8Zkvr/GpL5/xyS9/8WjvX/DIjy/wiE +8P8Uie7/MZfv/1qq8v+BvvT/pM/3/8He+f/X6fv/5fD8/+32/f/v9/3/9fn+//L4/f/v9/3/6/T9 +/+Lu/P/Q5vr/utv5/5zL9v93uPP/UKXx/yuT7/8Qh+7/CITw/w6J8/8Yj/b/HJL4/xqS+f8Zkvv/ +GZP8/xmT/f8ZlP7/GZT+/xmU//8Zlf//GZX//xmU//8ZlP//GZX//xmV//8Zlf//GZX//xmV//8Z +lf//GZX//xmV//8ZlP//GZP//xiS//8YkP//GZX//x+v/v8es///HZr0/hVZz/8oatXu8PX8Ef// +/wD+/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD+/v8B////APD1/Acpa9XdFlrP/x2b9P0etP//H7D+/xmV//8Y +kP//GJP//xmU//8ZlP//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV +//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8ZlP7/GZT+/xmU/f8Zk/z/GZP7/xmS+v8bk/n/HJP4 +/xmQ9v8SjPT/C4jy/weF8P8Jhe//Dofv/xmL7/8hj+//JZLv/yWS7/8wlu//K5Tv/yWS7/8kke// +H47v/xWK7v8Mhu//CYXv/wiG8f8MifP/E430/xqR9v8ck/j/G5P5/xmT+/8Zk/z/GZT8/xmU/f8Z +lP7/GZX+/xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV//8Zlf//GZX//xmV +//8Zlf//GZX//xmU//8ZlP//GJP//xiQ//8Zlf//H7D+/x60//8dm/T+FlrP/ylr1e7w9fwR//// +AP7+/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP7+/wH///8A8PX8Bylr1t0WWtD/HZz0/R61//8fsf7/GZb//xiR +//8YlP//GZX//xmV//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZX//xmV/v8Zlf7/GZX9/xmU/P8ZlPz/ +GZP7/xqT+v8bk/n/HJT4/xuT+P8akvf/F5D2/xWP9f8UjvX/FI71/xKN9P8TjvX/FI71/xWP9f8W +kPb/GJH3/xuT9/8ck/j/HJT5/xuU+v8ak/r/GZP7/xmU/P8ZlPz/GZX9/xmV/v8Zlf7/GZX//xmW +//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZX//xmV//8YlP//GJH//xmW//8fsf7/HrX//x2c9P4WWtD/KWvW7vD1/BH///8A +/v7/Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A/v7/Af///wDw9fwHKWvW3RZa0P8dnPT9HrX//x+x/v8Zlv//GJH/ +/xiU//8Zlf//GZX//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv// +GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb+/xmW/v8Z +lf7/GZX9/xmV/f8Zlfz/GZT8/xmU/P8ZlPv/GpT7/xqU+/8alPv/GpT7/xqU+/8alPv/GpT7/xmU ++/8ZlPv/GZT8/xmU/P8Zlfz/GZX9/xmV/f8Zlf7/GZb+/xmW//8Zlv//GZb//xmW//8Zlv//GZb/ +/xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv//GZb//xmW//8Zlv// +GZb//xmW//8Zlf//GZX//xiU//8Ykf//GZb//x+x/v8etf//HZz0/hZa0P8pa9bu8PX8Ef///wD+ +/v8B////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD+/v8B////APD1/AcpbNfdFlvR/x2c9P0etf//H7L+/xmX//8Ykv// +GJX//xmW//8Zlv//GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Z +l///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX +//8Zl///GZf//xmX//8Zl/7/GZb+/xmW/v8Zlv7/GZb+/xmW/v8Zlv7/GZb+/xmW/v8Zlv7/GZb+ +/xmW/v8Zlv7/GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl/// +GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Zl///GZf//xmX//8Z +l///GZf//xmW//8Zlv//GJX//xiS//8Zl///H7L+/x61//8dnPT+FlvR/yls1+7w9fwR////AP7+ +/wH///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP7+/wH///8A8PX8Byls190WW9H/H5/0/R62//8fsv7/GZf//xiS//8Y +lf//GZb//xmW//8Zl///GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY +//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmX//8Zl///GZf//xmX//8Zl///GZj/ +/xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8Zl///GZf//xmX//8Zl///GZf//xmX//8ZmP// +GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmX//8Zl///GZf//xmX//8Zl///GZj//xmY//8Z +mP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY//8ZmP//GZj//xmY +//8Zl///GZb//xmW//8Ylf//GJL//xmX//8fs/7/HrX//x6f9P4WW9H/KWzX7vD1/BH///8A/v7/ +Af///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A/v7/Af///wDw9fwHKmzX3RdZ0f8hpvT9ILr//x6x/v8Zmf//GJT//xiX +//8ZmP//GZj//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn/ +/xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf// +GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Z +mf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ +//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn//xmZ//8Zmf//GZn/ +/xmZ//8ZmP//GZj//xiX//8YlP//GZn//x6y/v8et///Iaf0/hdb0f8qbNfu8PX8Ef///wD+/v8B +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD+/v8B////APD1/AcqbdjdFlrS/yCl9P0kxv//HrP+/xiV//8Xkf//F5T/ +/xiV//8Ylf//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv// +GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Y +lv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW +//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb/ +/xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv//GJb//xiW//8Ylv// +GJb//xiV//8Ylf//F5T//xeR//8Ylv//HrL+/yLD//8iqvT+FlrS/ypt2O7w9fwR////AP7+/wH/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP7+/wH///8A8PX8Bypt2N0XXNL/H6D0/SbJ//8lwv7/Hqn//x2j//8dpP// +HaX//x2l//8epv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8d +pv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m +//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab/ +/x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv// +Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8dpv//Hab//x2m//8e +pv//HaX//x2l//8dpP//HKL//x2n//8kv/7/J8v//yCj9P4XW9L/K23Y7vD1/BL///8A/v7/Af// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A/v7/Af///wDw9fwEKm3Z2hdb0v8emvP9Ib///yfF/f8oyP7/KMf+/ynH/v8p +x/7/Kcj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI +/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+ +/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/ +Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8q +yP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI/v8qyP7/Ksj+/yrI +/v8pyP7/Kcf+/ynH/v8nx/7/KMf+/yfF/f8jxP//HZrz/hhc0/8pbNnp7/T8DP///wD+/v8B//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD+/v8A////AvD1/AAqbdm9F1zU/hx24Psesf3/IcD//ybJ//8nzf//KM///yrP +//8qz///Ks///yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD/ +/yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P// +KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q +0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ +//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//KtD//yrQ//8q0P//Ks// +/yrP//8qz///KM///yfN//8myv//I8P//x6y/v8dduD7FFrT/i9w2sD0+P0A/v//Av7+/wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP7+/wD///8D7/T8ACps2UkZYdb/HWDU/h1x3vseiur8H43q/CCQ6/whkuv8IZLr +/CKS6/wikuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8 +IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/wh +kuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS +6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr/CGS6/whkuv8IZLr +/CGS6/whkuv8IZLr/CGS6/whkuv8IpLr/COT6/wikuv8IZLr/CGS6/whkuv8IZLr/CGS6/wikuv8 +IpLr/CGS6/whkuv8IZHr/B+N6vweiur8HXHe+x9i1f4QW9T/krTrSf///wD8/f8D////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A/v7/AP///wD0+P0CMHHaABJc1XMaYtb9GF7V/xha0/8YWtP/GFnT/xdZ0/8XWdP/ +F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8X +WdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ +0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT +/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8XWdP/ +F1nT/xdZ0/8XWdP/F1nT/xhZ0/8XWdP/EFTS/xdZ0/8YWdP/F1nT/xdZ0/8XWdP/F1nT/xdZ0/8X +WdP/F1nT/xdZ0/8XWdP/GFrT/xha0/8ZX9X/FF7V/SZp2XTy9vwA////Av7+/wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A/P3/AP///wB7pOgCHWTXAC5v2jUrbtqRK27auitv2r0rb9q9K2/avitv2r4r +b9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv +2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/a +vitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+ +K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4rb9q+K2/avitv2r4r +b9q+K2/avitv2r4rb9q+Km7avi9x275RieG+L3Hbvipu2r4rb9q+K2/avitv2r4rb9q+K2/avitv +2r4rb9q+K2/avitv2r0rb9q9K27auixv2pEnatk1OXfdAPD1/AL///8A/v7/AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////APv9/gDv9PwC8fX9APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1 +/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8 +APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA +8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw +9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1 +/ADw9fwA8PX8APD1/ADw9fwA8vb9AP///wDy9v0A8PX8APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8 +APD1/ADw9fwA8PX8APD1/ADw9fwA8PX8APD1/ADx9f0C/v7/AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8C////BP///wL///8C////Av///wL///8C//// +Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C +////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL/ +//8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av// +/wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C////Av///wL///8C//// +Av///wL///8C////Av///wL///8C/v7/Av///wL///8C////Av///wL///8C////Av///wL///8C +////Av///wL///8C////Av///wL///8E////Av///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A +/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+ +/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+ +/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/ +AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A +/v7/AP7+/wD+/v8A/v7/AP7+/wD///8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+ +/v8A/v7/AP7+/wD+/v8A/v7/AP7+/wD+/v8A/v7/AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD/ +//8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// +/wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//// +AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A +////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//////////// +//////////////////////////////////////////////////////////////////////////// +////////////////////+AAAAAAAAAAAAAAAAA///wAAAAAAAAAAAAAAAAAAf/wAAAAAAAAAAAAA +AAAAAD/4AAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/wAAAA +AAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAA +AAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAA +AAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAA +B+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAA +AAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfg +AAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAA +AAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAA +AAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAA +AAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAA +AAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAA +AAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAA +AAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH +4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAA +AAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AA +AAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAA +AAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAA +AAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAA +AAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAA +AAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAA +B+AAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAA +AAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAfw +AAAAAAAAAAAAAAAAAAAH8gAAAAAAAAAAAAAAAAAAD/gAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAA +AAAAAAAf/EAAAAAAAAAAAAAAAAADf/8/4AAAAAAAAAAAAAAD/P//wAAAAAAAAAAAAAAAAAP///9A +AAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAA +AAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAA +AAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC +/////0AAAAAAAAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAA +AAAAAAAAAv////9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv// +//9AAAAAAAAAAAAAAAL/////QAAAAAAAAAAAAAAC/////0AAAAAAAAAAAAAAAv////9AAAAAAAAA +AAAAAAL/////oAAAAAAAAAAAAAAF/////6AAAAAAAAAAAAAABf/////QAAAAAAAAAAAAAAv///// +6AAAAAAAAAAAAAAX//////f/////////////7//////4AAAAAAAAAAAAAB////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////8=') + #endregion + $form_About.Margin = '8, 8, 8, 8' + $form_About.MaximizeBox = $False + $form_About.Name = 'form_About' + $form_About.StartPosition = 'CenterParent' + $form_About.Text = 'About HeloCheck Mail Tr@cker' + $form_About.add_Load($form_About_Load) + # + # lbl_LinkedIn + # + $lbl_LinkedIn.Location = '106, 258' + $lbl_LinkedIn.Margin = '5, 0, 5, 0' + $lbl_LinkedIn.Name = 'lbl_LinkedIn' + $lbl_LinkedIn.Size = '448, 33' + $lbl_LinkedIn.TabIndex = 12 + $lbl_LinkedIn.TabStop = $True + $lbl_LinkedIn.Text = 'LinkedIn' + $lbl_LinkedIn.add_LinkClicked($lbl_LinkedIn_LinkClicked) + # + # labelLinkedIn + # + $labelLinkedIn.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelLinkedIn.ImageAlign = 'MiddleLeft' + $labelLinkedIn.Location = '16, 251' + $labelLinkedIn.Margin = '4, 0, 4, 0' + $labelLinkedIn.Name = 'labelLinkedIn' + $labelLinkedIn.Size = '82, 30' + $labelLinkedIn.TabIndex = 11 + $labelLinkedIn.Text = 'LinkedIn' + $labelLinkedIn.TextAlign = 'MiddleLeft' + # + # lbl_BlogUrl + # + $lbl_BlogUrl.Location = '106, 228' + $lbl_BlogUrl.Margin = '5, 0, 5, 0' + $lbl_BlogUrl.Name = 'lbl_BlogUrl' + $lbl_BlogUrl.Size = '448, 33' + $lbl_BlogUrl.TabIndex = 10 + $lbl_BlogUrl.TabStop = $True + $lbl_BlogUrl.Text = 'Blog' + $lbl_BlogUrl.add_LinkClicked($lbl_BlogUrl_LinkClicked) + # + # labelBlog + # + $labelBlog.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelBlog.ImageAlign = 'MiddleLeft' + $labelBlog.Location = '16, 221' + $labelBlog.Margin = '4, 0, 4, 0' + $labelBlog.Name = 'labelBlog' + $labelBlog.Size = '68, 30' + $labelBlog.TabIndex = 9 + $labelBlog.Text = 'Blog:' + $labelBlog.TextAlign = 'MiddleLeft' + # + # lbl_Twitter + # + $lbl_Twitter.Location = '106, 196' + $lbl_Twitter.Margin = '5, 0, 5, 0' + $lbl_Twitter.Name = 'lbl_Twitter' + $lbl_Twitter.Size = '448, 33' + $lbl_Twitter.TabIndex = 8 + $lbl_Twitter.TabStop = $True + $lbl_Twitter.Text = 'Twitter' + $lbl_Twitter.add_LinkClicked($lbl_Twitter_LinkClicked) + # + # labelTwitter + # + $labelTwitter.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelTwitter.ImageAlign = 'MiddleLeft' + $labelTwitter.Location = '14, 194' + $labelTwitter.Margin = '4, 0, 4, 0' + $labelTwitter.Name = 'labelTwitter' + $labelTwitter.Size = '82, 24' + $labelTwitter.TabIndex = 7 + $labelTwitter.Text = 'Twitter:' + $labelTwitter.TextAlign = 'MiddleLeft' + # + # labelEmail + # + $labelEmail.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelEmail.ImageAlign = 'MiddleLeft' + $labelEmail.Location = '16, 162' + $labelEmail.Margin = '4, 0, 4, 0' + $labelEmail.Name = 'labelEmail' + $labelEmail.Size = '82, 29' + $labelEmail.TabIndex = 6 + $labelEmail.Text = 'Email:' + $labelEmail.TextAlign = 'MiddleLeft' + # + # labelLbl_AppAuthor + # + $labelLbl_AppAuthor.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelLbl_AppAuthor.ImageAlign = 'MiddleLeft' + $labelLbl_AppAuthor.Location = '16, 128' + $labelLbl_AppAuthor.Margin = '4, 0, 4, 0' + $labelLbl_AppAuthor.Name = 'labelLbl_AppAuthor' + $labelLbl_AppAuthor.Size = '426, 25' + $labelLbl_AppAuthor.TabIndex = 5 + $labelLbl_AppAuthor.Text = 'lbl_AppAuthor' + $labelLbl_AppAuthor.TextAlign = 'MiddleLeft' + # + # lbl_CreatedWith + # + $lbl_CreatedWith.Location = '16, 77' + $lbl_CreatedWith.Margin = '5, 0, 5, 0' + $lbl_CreatedWith.Name = 'lbl_CreatedWith' + $lbl_CreatedWith.Size = '442, 67' + $lbl_CreatedWith.TabIndex = 4 + $lbl_CreatedWith.Text = 'lbl_CreatedWith' + $lbl_CreatedWith.TextAlign = 'TopCenter' + # + # labelLbl_AppUpdate + # + $labelLbl_AppUpdate.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $labelLbl_AppUpdate.Location = '16, 37' + $labelLbl_AppUpdate.Margin = '4, 0, 4, 0' + $labelLbl_AppUpdate.Name = 'labelLbl_AppUpdate' + $labelLbl_AppUpdate.Size = '426, 25' + $labelLbl_AppUpdate.TabIndex = 3 + $labelLbl_AppUpdate.Text = 'lbl_AppUpdate' + $labelLbl_AppUpdate.TextAlign = 'TopCenter' + # + # lbl_AppAbout + # + $lbl_AppAbout.Font = 'Microsoft Sans Serif, 8.25pt, style=Bold' + $lbl_AppAbout.Location = '16, 12' + $lbl_AppAbout.Margin = '4, 0, 4, 0' + $lbl_AppAbout.Name = 'lbl_AppAbout' + $lbl_AppAbout.Size = '426, 25' + $lbl_AppAbout.TabIndex = 2 + $lbl_AppAbout.Text = 'lbl_AppAbout' + $lbl_AppAbout.TextAlign = 'TopCenter' + # + # linklabelEmail + # + $linklabelEmail.Location = '106, 167' + $linklabelEmail.Margin = '5, 0, 5, 0' + $linklabelEmail.Name = 'linklabelEmail' + $linklabelEmail.Size = '448, 31' + $linklabelEmail.TabIndex = 1 + $linklabelEmail.TabStop = $True + $linklabelEmail.Text = 'Email:' + $linklabelEmail.add_LinkClicked($linklabelEmail_LinkClicked) + # + # btn_Ok + # + $btn_Ok.Dock = 'Bottom' + $btn_Ok.Location = '0, 319' + $btn_Ok.Margin = '4, 4, 4, 4' + $btn_Ok.Name = 'btn_Ok' + $btn_Ok.Size = '458, 30' + $btn_Ok.TabIndex = 0 + $btn_Ok.Text = 'OK' + $btn_Ok.UseVisualStyleBackColor = $True + $btn_Ok.add_Click($btn_Ok_Click) + $form_About.ResumeLayout() + #endregion Generated Form Code + + #---------------------------------------------- + + #Save the initial state of the form + $InitialFormWindowState = $form_About.WindowState + #Init the OnLoad event to correct the initial state of the form + $form_About.add_Load($Form_StateCorrection_Load) + #Clean up the control events + $form_About.add_FormClosed($Form_Cleanup_FormClosed) + #Store the control values when form is closing + $form_About.add_Closing($Form_StoreValues_Closing) + #Show the Form + return $form_About.ShowDialog() + +} +#endregion Source: About.psf + +#Start the application +Main ($CommandLine) diff --git a/powershell/exchange/HealthChecker.ps1 b/powershell/exchange/HealthChecker.ps1 new file mode 100644 index 0000000..8b46d05 --- /dev/null +++ b/powershell/exchange/HealthChecker.ps1 @@ -0,0 +1,5839 @@ +<# +.NOTES + Name: HealthChecker.ps1 + Original Author: Marc Nivens + Author: David Paulson + Contributor: Jason Shinbaum, Michael Schatte, Lukas Sassl + Requires: Exchange Management Shell and administrator rights on the target Exchange + server as well as the local machine. + Major Release History: + 11/10/2020 - Initial Public Release of version 3. + 1/18/2017 - Initial Public Release of version 2. - rewritten by David Paulson. + 3/30/2015 - Initial Public Release. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +.SYNOPSIS + Checks the target Exchange server for various configuration recommendations from the Exchange product group. +.DESCRIPTION + This script checks the Exchange server for various configuration recommendations outlined in the + "Exchange 2013 Performance Recommendations" section on Microsoft Docs, found here: + + https://docs.microsoft.com/en-us/exchange/exchange-2013-sizing-and-configuration-recommendations-exchange-2013-help + + Informational items are reported in Grey. Settings found to match the recommendations are + reported in Green. Warnings are reported in yellow. Settings that can cause performance + problems are reported in red. Please note that most of these recommendations only apply to Exchange + 2013/2016. The script will run against Exchange 2010/2007 but the output is more limited. +.PARAMETER Server + This optional parameter allows the target Exchange server to be specified. If it is not the + local server is assumed. +.PARAMETER OutputFilePath + This optional parameter allows an output directory to be specified. If it is not the local + directory is assumed. This parameter must not end in a \. To specify the folder "logs" on + the root of the E: drive you would use "-OutputFilePath E:\logs", not "-OutputFilePath E:\logs\". +.PARAMETER MailboxReport + This optional parameter gives a report of the number of active and passive databases and + mailboxes on the server. +.PARAMETER LoadBalancingReport + This optional parameter will check the connection count of the Default Web Site for every server + running Exchange 2013/2016 with the Client Access role in the org. It then breaks down servers by percentage to + give you an idea of how well the load is being balanced. +.PARAMETER CasServerList + Used with -LoadBalancingReport. A comma separated list of CAS servers to operate against. Without + this switch the report will use all 2013/2016 Client Access servers in the organization. +.PARAMETER SiteName + Used with -LoadBalancingReport. Specifies a site to pull CAS servers from instead of querying every server + in the organization. +.PARAMETER XMLDirectoryPath + Used in combination with BuildHtmlServersReport switch for the location of the HealthChecker XML files for servers + which you want to be included in the report. Default location is the current directory. +.PARAMETER BuildHtmlServersReport + Switch to enable the script to build the HTML report for all the servers XML results in the XMLDirectoryPath location. +.PARAMETER HtmlReportFile + Name of the HTML output file from the BuildHtmlServersReport. Default is ExchangeAllServersReport.html +.PARAMETER DCCoreRatio + Gathers the Exchange to DC/GC Core ratio and displays the results in the current site that the script is running in. +.PARAMETER Verbose + This optional parameter enables verbose logging. +.EXAMPLE + .\HealthChecker.ps1 -Server SERVERNAME + Run against a single remote Exchange server +.EXAMPLE + .\HealthChecker.ps1 -Server SERVERNAME -MailboxReport -Verbose + Run against a single remote Exchange server with verbose logging and mailbox report enabled. +.EXAMPLE + Get-ExchangeServer | ?{$_.AdminDisplayVersion -Match "^Version 15"} | %{.\HealthChecker.ps1 -Server $_.Name} + Run against all Exchange 2013/2016 servers in the Organization. +.EXAMPLE + .\HealthChecker.ps1 -LoadBalancingReport + Run a load balancing report comparing all Exchange 2013/2016 CAS servers in the Organization. +.EXAMPLE + .\HealthChecker.ps1 -LoadBalancingReport -CasServerList CAS01,CAS02,CAS03 + Run a load balancing report comparing servers named CAS01, CAS02, and CAS03. +.LINK + https://docs.microsoft.com/en-us/exchange/exchange-2013-sizing-and-configuration-recommendations-exchange-2013-help + https://docs.microsoft.com/en-us/exchange/exchange-2013-virtualization-exchange-2013-help#requirements-for-hardware-virtualization + https://docs.microsoft.com/en-us/exchange/plan-and-deploy/virtualization?view=exchserver-2019#requirements-for-hardware-virtualization +#> +[CmdletBinding(DefaultParameterSetName="HealthChecker")] +param( +[Parameter(Mandatory=$false,ParameterSetName="HealthChecker")] +[Parameter(Mandatory=$false,ParameterSetName="MailboxReport")] + [string]$Server=($env:COMPUTERNAME), +[Parameter(Mandatory=$false)] + [ValidateScript({-not $_.ToString().EndsWith('\')})][string]$OutputFilePath = ".", +[Parameter(Mandatory=$false,ParameterSetName="MailboxReport")] + [switch]$MailboxReport, +[Parameter(Mandatory=$false,ParameterSetName="LoadBalancingReport")] + [switch]$LoadBalancingReport, +[Parameter(Mandatory=$false,ParameterSetName="LoadBalancingReport")] + [array]$CasServerList = $null, +[Parameter(Mandatory=$false,ParameterSetName="LoadBalancingReport")] + [string]$SiteName = ([string]::Empty), +[Parameter(Mandatory=$false,ParameterSetName="HTMLReport")] +[Parameter(Mandatory=$false,ParameterSetName="AnalyzeDataOnly")] + [ValidateScript({-not $_.ToString().EndsWith('\')})][string]$XMLDirectoryPath = ".", +[Parameter(Mandatory=$false,ParameterSetName="HTMLReport")] + [switch]$BuildHtmlServersReport, +[Parameter(Mandatory=$false,ParameterSetName="HTMLReport")] + [string]$HtmlReportFile="ExchangeAllServersReport.html", +[Parameter(Mandatory=$false,ParameterSetName="DCCoreReport")] + [switch]$DCCoreRatio, +[Parameter(Mandatory=$false,ParameterSetName="AnalyzeDataOnly")] + [switch]$AnalyzeDataOnly, +[Parameter(Mandatory=$false)][switch]$SaveDebugLog +) +$healthCheckerVersion = "3.1.1" + +$VirtualizationWarning = @" +Virtual Machine detected. Certain settings about the host hardware cannot be detected from the virtual machine. Verify on the VM Host that: + + - There is no more than a 1:1 Physical Core to Virtual CPU ratio (no oversubscribing) + - If Hyper-Threading is enabled do NOT count Hyper-Threaded cores as physical cores + - Do not oversubscribe memory or use dynamic memory allocation + +Although Exchange technically supports up to a 2:1 physical core to vCPU ratio, a 1:1 ratio is strongly recommended for performance reasons. Certain third party Hyper-Visors such as VMWare have their own guidance. + +VMWare recommends a 1:1 ratio. Their guidance can be found at https://www.vmware.com/files/pdf/Exchange_2013_on_VMware_Best_Practices_Guide.pdf. +Related specifically to VMWare, if you notice you are experiencing packet loss on your VMXNET3 adapter, you may want to review the following article from VMWare: http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2039495. + +For further details, please review the virtualization recommendations on Microsoft Docs at the following locations: +Exchange 2013: https://docs.microsoft.com/en-us/exchange/exchange-2013-virtualization-exchange-2013-help#requirements-for-hardware-virtualization. +Exchange 2016/2019: https://docs.microsoft.com/en-us/exchange/plan-and-deploy/virtualization?view=exchserver-2019. + +"@ + +#this is to set the verbose information to a different color +if($PSBoundParameters["Verbose"]){ + #Write verose output in cyan since we already use yellow for warnings + $Script:VerboseEnabled = $true + $VerboseForeground = $Host.PrivateData.VerboseForegroundColor + $Host.PrivateData.VerboseForegroundColor = "Cyan" +} + +try { + #Enums and custom data types + Add-Type -TypeDefinition @" + using System; + using System.Collections; + namespace HealthChecker + { + public class HealthCheckerExchangeServer + { + public string ServerName; //String of the server that we are working with + public HardwareInformation HardwareInformation; // Hardware Object Information + public OperatingSystemInformation OSInformation; // OS Version Object Information + public ExchangeInformation ExchangeInformation; //Detailed Exchange Information + public string HealthCheckerVersion; //To determine the version of the script on the object. + } + + // ExchangeInformation + public class ExchangeInformation + { + public ExchangeBuildInformation BuildInformation = new ExchangeBuildInformation(); //Exchange build information + public object GetExchangeServer; //Stores the Get-ExchangeServer Object + public ExchangeNetFrameworkInformation NETFramework = new ExchangeNetFrameworkInformation(); + public bool MapiHttpEnabled; //Stored from organization config + public string ExchangeServicesNotRunning; //Contains the Exchange services not running by Test-ServiceHealth + public Hashtable ApplicationPools; + public ExchangeRegistryValues RegistryValues = new ExchangeRegistryValues(); + public ExchangeServerMaintenance ServerMaintenance; + public System.Array ExchangeCertificates; //stores all the Exchange certificates on the servers. + } + + public class ExchangeBuildInformation + { + public ExchangeServerRole ServerRole; //Roles that are currently set and installed. + public ExchangeMajorVersion MajorVersion; //Exchange Version (Exchange 2010/2013/2019) + public ExchangeCULevel CU; // Exchange CU Level + public string FriendlyName; //Exchange Friendly Name is provided + public string BuildNumber; //Exchange Build Number + public string ReleaseDate; // Exchange release date for which the CU they are currently on + public bool SupportedBuild; //Determines if we are within the correct build of Exchange. + public object ExchangeSetup; //Stores the Get-Command ExSetup object + public System.Array KBsInstalled; //Stored object IU or Security KB fixes + } + + public class ExchangeNetFrameworkInformation + { + public NetMajorVersion MinSupportedVersion; //Min Supported .NET Framework version + public NetMajorVersion MaxSupportedVersion; //Max (Recommended) Supported .NET version. + public bool OnRecommendedVersion; //RecommendedNetVersion Info includes all the factors. Windows Version & CU. + public string DisplayWording; //Display if we are in Support or not + } + + public class ExchangeServerMaintenance + { + public System.Array InactiveComponents; + public object GetServerComponentState; + public object GetClusterNode; + public object GetMailboxServer; + } + + //enum for CU levels of Exchange + public enum ExchangeCULevel + { + Unknown, + Preview, + RTM, + CU1, + CU2, + CU3, + CU4, + CU5, + CU6, + CU7, + CU8, + CU9, + CU10, + CU11, + CU12, + CU13, + CU14, + CU15, + CU16, + CU17, + CU18, + CU19, + CU20, + CU21, + CU22, + CU23 + } + + //enum for the server roles that the computer is + public enum ExchangeServerRole + { + MultiRole, + Mailbox, + ClientAccess, + Hub, + Edge, + None + } + + //enum for the Exchange version + public enum ExchangeMajorVersion + { + Unknown, + Exchange2010, + Exchange2013, + Exchange2016, + Exchange2019 + } + + public class ExchangeRegistryValues + { + public int CtsProcessorAffinityPercentage; //Stores the CtsProcessorAffinityPercentage registry value from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\Search\SystemParameters + } + // End ExchangeInformation + + // OperatingSystemInformation + public class OperatingSystemInformation + { + public OSBuildInformation BuildInformation = new OSBuildInformation(); // contains build information + public NetworkInformation NetworkInformation = new NetworkInformation(); //stores network information and settings + public PowerPlanInformation PowerPlan = new PowerPlanInformation(); //stores the power plan information + public PageFileInformation PageFile; //stores the page file information + public LmCompatibilityLevelInformation LmCompatibility; // stores Lm Compatibility Level Information + public bool ServerPendingReboot; // determines if the server is pending a reboot. TODO: Adjust to contain the registry values that we are looking at. + public TimeZoneInformation TimeZone = new TimeZoneInformation(); //stores time zone information + public Hashtable TLSSettings; // stores the TLS settings on the server. + public InstalledUpdatesInformation InstalledUpdates = new InstalledUpdatesInformation(); //store the install update + public ServerBootUpInformation ServerBootUp = new ServerBootUpInformation(); // stores the server boot up time information + public System.Array VcRedistributable; //stores the Visual C++ Redistributable + public OSNetFrameworkInformation NETFramework = new OSNetFrameworkInformation(); //stores OS Net Framework + public bool CredentialGuardEnabled; + public OSRegistryValues RegistryValues = new OSRegistryValues(); + public Smb1ServerSettings Smb1ServerSettings = new Smb1ServerSettings(); + } + + public class OSBuildInformation + { + public OSServerVersion MajorVersion; //OS Major Version + public string VersionBuild; //hold the build number + public string FriendlyName; //string holder of the Windows Server friendly name + public object OperatingSystem; // holds Win32_OperatingSystem + } + + public class NetworkInformation + { + public double TCPKeepAlive; // value used for the TCP/IP keep alive value in the registry + public double RpcMinConnectionTimeout; //holds the value for the RPC minimum connection timeout. + public string HttpProxy; // holds the setting for HttpProxy if one is set. + public object PacketsReceivedDiscarded; //hold all the packets received discarded on the server. + public double IPv6DisabledComponents; //value stored in the registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\DisabledComponents + public bool IPv6DisabledOnNICs; //value that determines if we have IPv6 disabled on some NICs or not. + public System.Array NetworkAdapters; //stores all the NICs on the servers. + public string PnPCapabilities; //Value from PnPCapabilities registry + public bool SleepyNicDisabled; //If the NIC can be in power saver mode by the OS. + } + + public class PowerPlanInformation + { + public bool HighPerformanceSet; // If the power plan is High Performance + public string PowerPlanSetting; //value for the power plan that is set + public object PowerPlan; //object to store the power plan information + } + + public class PageFileInformation + { + public object PageFile; //store the information that we got for the page file + public double MaxPageSize; //holds the information of what our page file is set to + } + + public class OSRegistryValues + { + public int CurrentVersionUbr; // stores SOFTWARE\Microsoft\Windows NT\CurrentVersion\UBR + public int LanManServerDisabledCompression; // stores SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\DisabledCompression + } + + public class LmCompatibilityLevelInformation + { + public int RegistryValue; //The LmCompatibilityLevel for the server (INT 1 - 5) + public string Description; //description of the LmCompat that the server is set to + } + + public class TimeZoneInformation + { + public string CurrentTimeZone; //stores the value for the current time zone of the server. + public int DynamicDaylightTimeDisabled; // the registry value for DynamicDaylightTimeDisabled. + public string TimeZoneKeyName; // the registry value TimeZoneKeyName. + public string StandardStart; // the registry value for StandardStart. + public string DaylightStart; // the registry value for DaylightStart. + public bool DstIssueDetected; // Determines if there is a high chance of an issue. + public System.Array ActionsToTake; //array of verbage of the issues detected. + } + + public class ServerRebootInformation + { + public bool PendingFileRenameOperations; //bool "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\" item PendingFileRenameOperations. + public object SccmReboot; // object to store CimMethod for class name CCM_ClientUtilities + public bool SccmRebootPending; // SccmReboot has either PendingReboot or IsHardRebootPending is set to true. + public bool ComponentBasedServicingPendingReboot; // bool HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending + public bool AutoUpdatePendingReboot; // bool HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired + public bool PendingReboot; // bool if reboot types are set to true + } + + public class InstalledUpdatesInformation + { + public System.Array HotFixes; //array to keep all the hotfixes of the server + public System.Array HotFixInfo; //object to store hotfix information + public System.Array InstalledUpdates; //store the install updates + } + + public class ServerBootUpInformation + { + public string Days; + public string Hours; + public string Minutes; + public string Seconds; + } + + //enum for the dword values of the latest supported VC++ redistributable releases + //https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads + public enum VCRedistVersion + { + Unknown = 0, + VCRedist2012 = 184610406, + VCRedist2013 = 201367256 + } + + public class SoftwareInformation + { + public string DisplayName; + public string DisplayVersion; + public string InstallDate; + public int VersionIdentifier; + } + + public class OSNetFrameworkInformation + { + public NetMajorVersion NetMajorVersion; //NetMajorVersion value + public string FriendlyName; //string of the friendly name + public int RegistryValue; //store the registry value + public Hashtable FileInformation; //stores Get-Item information for .NET Framework + } + + //enum for the OSServerVersion that we are + public enum OSServerVersion + { + Unknown, + Windows2008, + Windows2008R2, + Windows2012, + Windows2012R2, + Windows2016, + Windows2019, + WindowsCore + } + + //enum for the dword value of the .NET frame 4 that we are on + public enum NetMajorVersion + { + Unknown = 0, + Net4d5 = 378389, + Net4d5d1 = 378675, + Net4d5d2 = 379893, + Net4d5d2wFix = 380035, + Net4d6 = 393295, + Net4d6d1 = 394254, + Net4d6d1wFix = 394294, + Net4d6d2 = 394802, + Net4d7 = 460798, + Net4d7d1 = 461308, + Net4d7d2 = 461808, + Net4d8 = 528040 + } + + public class Smb1ServerSettings + { + public object RegistryValue; + public object SmbServerConfiguration; + public object WindowsFeature; + public int Smb1Status; + } + // End OperatingSystemInformation + + // HardwareInformation + public class HardwareInformation + { + public string Manufacturer; //String to display the hardware information + public ServerType ServerType; //Enum to determine if the hardware is VMware, HyperV, Physical, or Unknown + public double TotalMemory; //Stores the total memory available + public object System; //object to store the system information that we have collected + public ProcessorInformation Processor; //Detailed processor Information + public bool AutoPageFile; //True/False if we are using a page file that is being automatically set + public string Model; //string to display Model + } + + //enum for the type of computer that we are + public enum ServerType + { + VMWare, + AmazonEC2, + HyperV, + Physical, + Unknown + } + + public class ProcessorInformation + { + public string Name; //String of the processor name + public int NumberOfPhysicalCores; //Number of Physical cores that we have + public int NumberOfLogicalCores; //Number of Logical cores that we have presented to the os + public int NumberOfProcessors; //Total number of processors that we have in the system + public int MaxMegacyclesPerCore; //Max speed that we can get out of the cores + public int CurrentMegacyclesPerCore; //Current speed that we are using the cores at + public bool ProcessorIsThrottled; //True/False if we are throttling our processor + public bool DifferentProcessorsDetected; //true/false to detect if we have different processor types detected + public bool DifferentProcessorCoreCountDetected; //detect if there are a different number of core counts per Processor CPU socket + public int EnvironmentProcessorCount; //[system.environment]::processorcount + public object ProcessorClassObject; // object to store the processor information + } + + //HTML & display classes + public class HtmlServerValues + { + public System.Array OverviewValues; + public System.Array ActionItems; //use HtmlServerActionItemRow + public System.Array ServerDetails; // use HtmlServerInformationRow + } + + public class HtmlServerActionItemRow + { + public string Setting; + public string DetailValue; + public string RecommendedDetails; + public string MoreInformation; + public string Class; + } + + public class HtmlServerInformationRow + { + public string Name; + public string DetailValue; + public string Class; + } + + public class DisplayResultsLineInfo + { + public string DisplayValue; + public string Name; + public int TabNumber; + public object TestingValue; //Used for pester testing down the road. + public string WriteType; + + public string Line + { + get + { + if (String.IsNullOrEmpty(this.Name)) + { + return this.DisplayValue; + } + + return String.Concat(this.Name, ": ", this.DisplayValue); + } + } + } + + public class DisplayResultsGroupingKey + { + public string Name; + public int DefaultTabNumber; + public bool DisplayGroupName; + public int DisplayOrder; + } + + public class AnalyzedInformation + { + public HealthCheckerExchangeServer HealthCheckerExchangeServer; + public Hashtable HtmlServerValues = new Hashtable(); + public Hashtable DisplayResults = new Hashtable(); + } + } +"@ -ErrorAction Stop +} +catch +{ + Write-Warning "There was an error trying to add custom classes to the current PowerShell session. You need to close this session and open a new one to have the script properly work." + exit +} + + +function Write-Red($message) +{ + Write-DebugLog $message + Write-Host $message -ForegroundColor Red + $message | Out-File ($OutputFullPath) -Append +} + +function Write-Yellow($message) +{ + Write-DebugLog $message + Write-Host $message -ForegroundColor Yellow + $message | Out-File ($OutputFullPath) -Append +} + +function Write-Green($message) +{ + Write-DebugLog $message + Write-Host $message -ForegroundColor Green + $message | Out-File ($OutputFullPath) -Append +} + +function Write-Grey($message) +{ + Write-DebugLog $message + Write-Host $message + $message | Out-File ($OutputFullPath) -Append +} + +function Write-VerboseOutput($message) +{ + Write-Verbose $message + Write-DebugLog $message + if($Script:VerboseEnabled) + { + $message | Out-File ($OutputFullPath) -Append + } +} + +function Write-DebugLog($message) +{ + if(![string]::IsNullOrEmpty($message)) + { + $Script:Logger.WriteToFileOnly($message) + } +} + +Function Write-Break { + Write-Host "" +} + +#Function Version 1.1 +Function Write-HostWriter { +param( +[Parameter(Mandatory=$true)][string]$WriteString +) + if($Script:Logger -ne $null) + { + $Script:Logger.WriteHost($WriteString) + } + elseif($HostFunctionCaller -eq $null) + { + Write-Host $WriteString + } + else + { + &$HostFunctionCaller $WriteString + } +} + +Function Write-VerboseWriter { +param( +[Parameter(Mandatory=$true)][string]$WriteString +) + if($VerboseFunctionCaller -eq $null) + { + Write-Verbose $WriteString + } + else + { + &$VerboseFunctionCaller $WriteString + } +} + +$Script:VerboseFunctionCaller = ${Function:Write-VerboseOutput} + +#Function Version 1.0 +Function Write-ScriptMethodHostWriter{ +param( +[Parameter(Mandatory=$true)][string]$WriteString +) + if($this.LoggerObject -ne $null) + { + $this.LoggerObject.WriteHost($WriteString) + } + elseif($this.HostFunctionCaller -eq $null) + { + Write-Host $WriteString + } + else + { + $this.HostFunctionCaller($WriteString) + } +} + +#Function Version 1.0 +Function Write-ScriptMethodVerboseWriter { +param( +[Parameter(Mandatory=$true)][string]$WriteString +) + if($this.LoggerObject -ne $null) + { + $this.LoggerObject.WriteVerbose($WriteString) + } + elseif($this.VerboseFunctionCaller -eq $null -and + $this.WriteVerboseData) + { + Write-Host $WriteString -ForegroundColor Cyan + } + elseif($this.WriteVerboseData) + { + $this.VerboseFunctionCaller($WriteString) + } +} + +Function Write-HealthCheckerVersion { + + $currentVersion = Test-ScriptVersion -ApiUri "api.github.com" -RepoOwner "dpaulson45" ` + -RepoName "HealthChecker" ` + -CurrentVersion $healthCheckerVersion ` + -DaysOldLimit 90 ` + -CatchActionFunction ${Function:Invoke-CatchActions} + + $Script:DisplayedScriptVersionAlready = $true + + if($currentVersion) + { + Write-Green("Exchange Health Checker version {0}" -f $healthCheckerVersion) + } + else + { + Write-Yellow("Exchange Health Checker version {0}. This script is probably outdated. Please verify before relying on the results." -f $healthCheckerVersion) + } +} + +Function Write-ResultsToScreen { +param( +[Hashtable]$ResultsToWrite +) + Write-VerboseOutput("Calling: Write-ResultsToScreen") + $indexOrderGroupingToKey = @{} + + foreach ($keyGrouping in $ResultsToWrite.Keys) + { + $indexOrderGroupingToKey[$keyGrouping.DisplayOrder] = $keyGrouping + } + + $sortedIndexOrderGroupingToKey = $indexOrderGroupingToKey.Keys | Sort-Object + + foreach ($key in $sortedIndexOrderGroupingToKey) + { + Write-VerboseOutput("Working on Key: {0}" -f $key) + $keyGrouping = $indexOrderGroupingToKey[$key] + Write-VerboseOutput("Working on Key Group: {0}" -f $keyGrouping.Name) + Write-VerboseOutput("Total lines to write: {0}" -f ($ResultsToWrite[$keyGrouping].Count)) + + if ($keyGrouping.DisplayGroupName) + { + Write-Grey($keyGrouping.Name) + $dashes = [string]::empty + 1..($keyGrouping.Name.Length) | %{$dashes = $dashes + "-"} + Write-Grey($dashes) + } + + foreach ($line in $ResultsToWrite[$keyGrouping]) + { + $tab = [string]::Empty + + if ($line.TabNumber -ne 0) + { + 1..($line.TabNumber) | %{$tab = $tab + "`t"} + } + + $writeValue = "{0}{1}" -f $tab, $line.Line + switch ($line.WriteType) + { + "Grey" {Write-Grey($writeValue)} + "Yellow" {Write-Yellow($writeValue)} + "Green" {Write-Green($writeValue)} + "Red" {Write-Red($writeValue)} + } + } + + Write-Grey("") + } +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-AllNicInformation/Get-AllNicInformation.ps1 +Function Get-AllNicInformation { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$ComputerName, + [Parameter(Mandatory=$false)][string]$ComputerFQDN, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.6 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-WmiObjectHandler/Get-WmiObjectHandler.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 + #> + Write-VerboseWriter("Calling: Get-AllNicInformation") + Write-VerboseWriter("Passed [string]ComputerName: {0} | [string]ComputerFQDN: {1}" -f $ComputerName, $ComputerFQDN) + + Function Get-NicPnpCapabilitiesSetting { + [CmdletBinding()] + param( + [string]$NicAdapterComponentId + ) + + if ($NicAdapterComponentId -eq [string]::Empty) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid NicAdapterDeviceId or NicAdapterComponentId" + } + + $nicAdapterBasicPath = "SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}" + Write-VerboseWriter("Probing started to detect NIC adapter registry path") + + $nicAdapterRegKey = Invoke-RegistryGetValue -MachineName $ComputerName -Subkey $nicAdapterBasicPath -ReturnAfterOpenSubKey $true -CatchActionFunction $CatchActionFunction + $nicAdapterDeviceIDs = $nicAdapterRegKey.GetSubKeyNames() | Where-Object {$_.StartsWith("0")} + + foreach($deviceID in $nicAdapterDeviceIDs) + { + $nicAdapterPnPCapabilitiesProbingKey = "{0}\{1}" -f $nicAdapterBasicPath, $deviceID + $netCfgInstanceId = Invoke-RegistryGetValue -MachineName $ComputerName -Subkey $nicAdapterPnPCapabilitiesProbingKey -GetValue "NetCfgInstanceId" -CatchActionFunction $CatchActionFunction + + if ($netCfgInstanceId -eq $NicAdapterComponentId) + { + Write-VerboseWriter("Matching ComponentId found - now checking for PnPCapabilitiesValue") + $nicAdapterPnPCapabilitiesValue = Invoke-RegistryGetValue -MachineName $ComputerName -SubKey $nicAdapterPnPCapabilitiesProbingKey -GetValue "PnPCapabilities" -CatchActionFunction $CatchActionFunction + break + } + else + { + Write-VerboseWriter("No matching ComponentId found") + } + } + + $obj = New-Object PSCustomObject + $sleepyNicDisabled = $false + + if ($nicAdapterPnPCapabilitiesValue -eq 24 -or + $nicAdapterPnPCapabilitiesValue -eq 280) + { + $sleepyNicDisabled = $true + } + + $obj | Add-Member -MemberType NoteProperty -Name "PnPCapabilities" -Value $nicAdapterPnPCapabilitiesValue + $obj | Add-Member -MemberType NoteProperty -Name "SleepyNicDisabled" -Value $sleepyNicDisabled + return $obj + + } + + Function Get-NetworkConfiguration { + [CmdletBinding()] + param( + [string]$ComputerName + ) + try + { + $currentErrors = $Error.Count + $cimSession = New-CimSession -ComputerName $ComputerName -ErrorAction Stop + $networkIpConfiguration = Get-NetIPConfiguration -CimSession $CimSession -ErrorAction Stop | Where-Object {$_.NetAdapter.MediaConnectionState -eq "Connected"} + + if ($CatchActionFunction -ne $null) + { + $index = 0 + while ($index -lt ($Error.Count - $currentErrors)) + { + & $CatchActionFunction $Error[$index] + $index++ + } + } + + return $networkIpConfiguration + } + catch + { + Write-VerboseWriter("Failed to run Get-NetIPConfiguration. Error {0}." -f $Error[0].Exception) + + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + + throw + } + } + + Function New-NICInformation { + param( + [array]$NetworkConfigurations, + [bool]$WmiObject + ) + if($NetworkConfigurations -eq $null) + { + Write-VerboseWriter("NetworkConfigurations are null in New-NICInformation. Returning a null object.") + return $null + } + + Function New-IpvAddresses { + + $obj = New-Object PSCustomObject + $obj | Add-Member -MemberType NoteProperty -Name "Address" -Value ([string]::Empty) + $obj | Add-Member -MemberType NoteProperty -Name "Subnet" -Value ([string]::Empty) + $obj | Add-Member -MemberType NoteProperty -Name "DefaultGateway" -Value ([string]::Empty) + + return $obj + } + + if ($WmiObject) + { + $networkAdapterConfigurations = Get-WmiObjectHandler -ComputerName $ComputerName -Class "Win32_NetworkAdapterConfiguration" -Filter "IPEnabled = True" -CatchActionFunction $CatchActionFunction + } + + [array]$nicObjects = @() + foreach($networkConfig in $NetworkConfigurations) + { + $dnsClient = $null + $rssEnabledValue = 2 + $netAdapterRss = $null + if (!$WmiObject) + { + Write-VerboseWriter("Working on NIC: {0}" -f $networkConfig.InterfaceDescription) + $adapter = $networkConfig.NetAdapter + if($adapter.DriverFileName -ne "NdisImPlatform.sys") + { + $nicPnpCapabilitiesSetting = Get-NicPnpCapabilitiesSetting -NicAdapterComponentId $adapter.DeviceID + } + else + { + Write-VerboseWriter("Multiplexor adapter detected. Going to skip PnpCapabilities check") + $nicPnpCapabilitiesSetting = @{ + PnPCapabilities = "MultiplexorNoPnP" + } + } + + try + { + $dnsClient = $adapter | Get-DnsClient -ErrorAction Stop + Write-VerboseWriter("Got DNS Client information") + } + catch + { + Write-VerboseWriter("Failed to get the DNS Client information") + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + } + + try + { + $netAdapterRss = $adapter | Get-NetAdapterRss -ErrorAction Stop + Write-VerboseWriter("Got Net Adapter RSS information") + if ($netAdapterRss -ne $null) + { + [int]$rssEnabledValue = $netAdapterRss.Enabled + } + } + catch + { + Write-VerboseWriter("Failed to get RSS Information") + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + } + } + else + { + Write-VerboseWriter("Working on NIC: {0}" -f $networkConfig.Description) + $adapter = $networkConfig + if($adapter.ServiceName -ne "NdisImPlatformMp") + { + $nicPnpCapabilitiesSetting = Get-NicPnpCapabilitiesSetting -NicAdapterComponentId $adapter.Guid + } + else + { + Write-VerboseWriter("Multiplexor adapter detected. Going to skip PnpCapabilities check") + $nicPnpCapabilitiesSetting = @{ + PnPCapabilities = "MultiplexorNoPnP" + } + } + } + + $nicInformationObj = New-Object PSCustomObject + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "WmiObject" -Value $WmiObject + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "Name" -Value ($adapter.Name) + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "LinkSpeed" -Value ((($adapter.Speed)/1000000).ToString() + " Mbps") + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "DriverDate" -Value [DateTime]::MaxValue + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "NICObject" -Value $networkConfig + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "NetAdapterRss" -Value $netAdapterRss + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "RssEnabledValue" -Value $rssEnabledValue + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "IPv6Enabled" -Value $false + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "Description" -Value $adapter.Description + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "DriverVersion" -Value [string]::Empty + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "MTUSize" -Value 0 + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "PnPCapabilities" -Value ($nicPnpCapabilitiesSetting.PnPCapabilities) + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "SleepyNicDisabled" -Value ($nicPnpCapabilitiesSetting.SleepyNicDisabled) + + if (!$WmiObject) + { + $nicInformationObj.MTUSize = $adapter.MtuSize + $nicInformationObj.DriverDate = $adapter.DriverDate + $nicInformationObj.DriverVersion = $adapter.DriverVersionString + $nicInformationObj.Description = $adapter.InterfaceDescription + + foreach ($ipAddress in $networkConfig.AllIPAddresses.IPAddress) + { + if ($ipAddress.Contains(":")) + { + $nicInformationObj.IPv6Enabled = $true + } + } + + $ipv4Address = @() + for ($i = 0; $i -lt $networkConfig.IPv4Address.Count; $i++) + { + $obj = New-IpvAddresses + + if ($networkConfig.IPv4Address -ne $null -and + $i -lt $networkConfig.IPv4Address.Count) + { + $obj.Address = $networkConfig.IPv4Address[$i].IPAddress + $obj.Subnet = $networkConfig.IPv4Address[$i].PrefixLength + } + + if ($networkConfig.IPv4DefaultGateway -ne $null -and + $i -lt $networkConfig.IPv4DefaultGateway.Count) + { + $obj.DefaultGateway = $networkConfig.IPv4DefaultGateway[$i].NextHop + } + + $ipv4Address += $obj + } + + $ipv6Address = @() + for ($i = 0; $i -lt $networkConfig.IPv6Address.Count; $i++) + { + $obj = New-IpvAddresses + + if ($networkConfig.IPv6Address -ne $null -and + $i -lt $networkConfig.IPv6Address.Count) + { + $obj.Address = $networkConfig.IPv6Address[$i].IPAddress + $obj.Subnet = $networkConfig.IPv6Address[$i].PrefixLength + } + + if ($networkConfig.IPv6DefaultGateway -ne $null -and + $i -lt $networkConfig.IPv6DefaultGateway.Count) + { + $obj.DefaultGateway = $networkConfig.IPv6DefaultGateway[$i].NextHop + } + + $ipv6Address += $obj + } + + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "IPv4Addresses" -Value $ipv4Address + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "Ipv6Addresses" -Value $ipv6Address + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "RegisteredInDns" -Value $dnsClient.RegisterThisConnectionsAddress + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "DnsServer" -Value $networkConfig.DNSServer.ServerAddresses + $nicInformationObj | Add-Member -MemberType NoteProperty -Name "DnsClientObject" -Value $dnsClient + } + else + { + $stopProcess = $false + foreach ($adapterConfiguration in $networkAdapterConfigurations) + { + Write-VerboseWriter("Working on '{0}' | SettingID: {1}" -f $adapterConfiguration.Description, ($settingId = $adapterConfiguration.SettingID)) + if ($settingId -eq $networkConfig.GUID -or + $settingId -eq $networkConfig.InterfaceGuid) + { + foreach ($ipAddress in $adapterConfiguration.IPAddress) + { + if ($ipAddress.Contains(":")) + { + $nicInformationObj.IPv6Enabled = $true + $stopProcess = $true + break + } + } + } + + if ($stopProcess) + { + break + } + } + } + + $nicObjects += $nicInformationObj + } + + Write-VerboseWriter("Found {0} active adapters on the computer." -f $nicObjects.Count) + Write-VerboseWriter("Exiting: Get-AllNicInformation") + return $nicObjects + } + + try + { + try + { + $networkConfiguration = Get-NetworkConfiguration -ComputerName $ComputerName + } + catch + { + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + + if ($ComputerFQDN -ne [string]::Empty -and + $ComputerName -ne $null) + { + $networkConfiguration = Get-NetworkConfiguration -ComputerName $ComputerFQDN + } + else + { + $bypassCatchActions = $true + Write-VerboseWriter("No FQDN was passed, going to rethrow error.") + throw + } + } + + return (New-NICInformation -NetworkConfigurations $networkConfiguration) + } + catch + { + if (!$bypassCatchActions -and + $CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + + $wmiNetworkCards = Get-WmiObjectHandler -ComputerName $ComputerName -Class "Win32_NetworkAdapter" -Filter "NetConnectionStatus ='2'" -CatchActionFunction $CatchActionFunction + return (New-NICInformation -NetworkConfigurations $wmiNetworkCards -WmiObject $true) + } + +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-AllTlsSettingsFromRegistry/Get-AllTlsSettingsFromRegistry.ps1 +Function Get-AllTlsSettingsFromRegistry { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$MachineName, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.1 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 + #> + + Write-VerboseWriter("Calling: Get-AllTlsSettingsFromRegistry") + Write-VerboseWriter("Passed: [string]MachineName: {0}" -f $MachineName) + + $registryBase = "SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS {0}\{1}" + $tlsVersions = @("1.0","1.1","1.2") + + $tlsResults = @{} + $keyValues = ("Enabled","DisabledByDefault") + + Function Set-TLSMemberValue { + param( + [Parameter(Mandatory=$true)][string]$GetKeyType, + [Parameter(Mandatory=$false)][object]$KeyValue, + [Parameter(Mandatory=$true)][string]$ServerClientType, + [Parameter(Mandatory=$true)][string]$TlsVersion + ) + switch($GetKeyType) + { + "Enabled" { + if($KeyValue -eq $null) + { + Write-VerboseWriter("Failed to get TLS {0} {1} Enabled Key on Server {2}. We are assuming this means it is enabled." -f $TlsVersion, $ServerClientType, $MachineName) + return $true + } + else + { + Write-VerboseWriter("{0} Enabled Value '{1}'" -f $ServerClientType, $serverValue) + if($KeyValue -eq 1) + { + return $true + } + return $false + } + } + "DisabledByDefault" { + if($KeyValue -eq $null) + { + Write-VerboseWriter("Failed to get TLS {0} {1} Disabled By Default Key on Server {2}. Setting to false." -f $TlsVersion, $ServerClientType, $MachineName) + return $false + } + else + { + Write-VerboseWriter("{0} Disabled By Default Value '{1}'" -f $ServerClientType, $serverValue) + if($KeyValue -eq 1) + { + return $true + } + return $false + } + } + } + } + + Function Set-NETDefaultTLSValue { + param( + [Parameter(Mandatory=$false)][object]$KeyValue, + [Parameter(Mandatory=$true)][string]$NetVersion, + [Parameter(Mandatory=$true)][string]$KeyName + ) + if($KeyValue -eq $null) + { + Write-VerboseWriter("Failed to get {0} registry value for .NET {1} version. Setting to false." -f $KeyName, $NetVersion) + return $false + } + else + { + Write-VerboseWriter("{0} value '{1}'" -f $KeyName, $KeyValue) + if($KeyValue -eq 1) + { + return $true + } + return $false + } + } + + [hashtable]$allTlsObjects = @{} + foreach($tlsVersion in $tlsVersions) + { + $registryServer = $registryBase -f $tlsVersion, "Server" + $registryClient = $registryBase -f $tlsVersion, "Client" + $currentTLSObject = New-Object PSCustomObject + $currentTLSObject | Add-Member -MemberType NoteProperty -Name "TLSVersion" -Value $tlsVersion + + foreach($getKey in $keyValues) + { + $memberServerName = "Server{0}" -f $getKey + $memberClientName = "Client{0}" -f $getKey + + $serverValue = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $MachineName -SubKey $registryServer -GetValue $getKey -CatchActionFunction $CatchActionFunction + $clientValue = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $MachineName -SubKey $registryClient -GetValue $getKey -CatchActionFunction $CatchActionFunction + + $currentTLSObject | Add-Member -MemberType NoteProperty -Name $memberServerName -Value (Set-TLSMemberValue -GetKeyType $getKey -KeyValue $serverValue -ServerClientType "Server" -TlsVersion $tlsVersion) + $currentTLSObject | Add-Member -MemberType NoteProperty -Name $memberClientName -Value (Set-TLSMemberValue -GetKeyType $getKey -KeyValue $clientValue -ServerClientType "Client" -TlsVersion $tlsVersion) + + } + $allTlsObjects.Add($tlsVersion, $currentTLSObject) + } + + $netVersions = @("v2.0.50727","v4.0.30319") + $registryBase = "SOFTWARE\{0}\.NETFramework\{1}" + foreach($netVersion in $netVersions) + { + $currentNetTlsDefaultVersionObject = New-Object PSCustomObject + $currentNetTlsDefaultVersionObject | Add-Member -MemberType NoteProperty -Name "NetVersion" -Value $netVersion + + $SystemDefaultTlsVersions = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $MachineName -SubKey ($registryBase -f "Microsoft", $netVersion) -GetValue "SystemDefaultTlsVersions" -CatchActionFunction $CatchActionFunction + $WowSystemDefaultTlsVersions = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $MachineName -SubKey ($registryBase -f "Wow6432Node\Microsoft", $netVersion) -GetValue "SystemDefaultTlsVersions" -CatchActionFunction $CatchActionFunction + + $currentNetTlsDefaultVersionObject | Add-Member -MemberType NoteProperty -Name "SystemDefaultTlsVersions" -Value (Set-NETDefaultTLSValue -KeyValue $SystemDefaultTlsVersions -NetVersion $netVersion -KeyName "SystemDefaultTlsVersions") + $currentNetTlsDefaultVersionObject | Add-Member -MemberType NoteProperty -Name "WowSystemDefaultTlsVersions" -Value (Set-NETDefaultTLSValue -KeyValue $WowSystemDefaultTlsVersions -NetVersion $netVersion -KeyName "WowSystemDefaultTlsVersions") + + $hashKeyName = "NET{0}" -f ($netVersion.Split(".")[0]) + $allTlsObjects.Add($hashKeyName, $currentNetTlsDefaultVersionObject) + } + + return $allTlsObjects +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-DotNetDllFileVersions/Get-DotNetDllFileVersions.ps1 +Function Get-DotNetDllFileVersions { + [CmdletBinding()] + param( + [string]$ComputerName, + [array]$FileNames, + [scriptblock]$CatchActionFunction + ) + + #Function Version 1.1 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-ScriptBlockHandler/Invoke-ScriptBlockHandler.ps1 + #> + + Write-VerboseWriter("Calling: Get-DotNetDllFileVersions") + + Function ScriptBlock-GetItem{ + param( + [string]$FilePath + ) + $getItem = Get-Item $FilePath + + $returnObject = ([PSCustomObject]@{ + GetItem = $getItem + LastWriteTimeUtc = $getItem.LastWriteTimeUtc + VersionInfo = ([PSCustomObject]@{ + FileMajorPart = $getItem.VersionInfo.FileMajorPart + FileMinorPart = $getItem.VersionInfo.FileMinorPart + FileBuildPart = $getItem.VersionInfo.FileBuildPart + FilePrivatePart = $getItem.VersionInfo.FilePrivatePart + }) + }) + + return $returnObject + } + + $dotNetInstallPath = Invoke-RegistryGetValue -MachineName $ComputerName -SubKey "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" -GetValue "InstallPath" -CatchActionFunction $CatchActionFunction + + if ($dotNetInstallPath -eq [string]::Empty) + { + Write-VerboseWriter("Failed to determine .NET install path") + return + } + + $files = @{} + foreach($filename in $FileNames) + { + Write-VerboseWriter("Query .NET DLL information for machine: {0}" -f $ComputerName) + $getItem = Invoke-ScriptBlockHandler -ComputerName $ComputerName -ScriptBlock ${Function:ScriptBlock-GetItem} -ArgumentList ("{0}\{1}" -f $dotNetInstallPath, $filename) -CatchActionFunction $CatchActionFunction + $files.Add($filename, $getItem) + } + + return $files +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-ExchangeMajorVersion/Get-ExchangeMajorVersion.ps1 +Function Get-ExchangeMajorVersion { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][object]$AdminDisplayVersion + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + Write-VerboseWriter("Calling: Get-ExchangeMajorVersion") + Write-VerboseWriter("Passed: {0}" -f $AdminDisplayVersion.ToString()) + if($AdminDisplayVersion.GetType().Name -eq "string") + { + $split = $AdminDisplayVersion.Substring(($AdminDisplayVersion.IndexOf(" ")) + 1, 4).split('.') + $build = [int]$split[0] + ($split[1] / 10) + } + else + { + $build = $AdminDisplayVersion.Major + ($AdminDisplayVersion.Minor / 10) + } + Write-VerboseWriter("Determing build based off of: {0}" -f $build) + $exchangeMajorVersion = [string]::Empty + switch($build) + { + 14.3 {$exchangeMajorVersion = "Exchange2010"} + 15 {$exchangeMajorVersion = "Exchange2013"} + 15.1 {$exchangeMajorVersion = "Exchange2016"} + 15.2 {$exchangeMajorVersion = "Exchange2019"} + default {$exchangeMajorVersion = "Unknown"} + } + Write-VerboseWriter("Returned: {0}" -f $exchangeMajorVersion) + return $exchangeMajorVersion +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-NETFrameworkVersion/Get-NETFrameworkVersion.ps1 +Function Get-NETFrameworkVersion { +[CmdletBinding()] +param( +[string]$MachineName = $env:COMPUTERNAME, +[int]$NetVersionKey = -1, +[scriptblock]$CatchActionFunction +) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 + #> + + Write-VerboseWriter("Calling: Get-NETFrameworkVersion") + if ($NetVersionKey -eq -1) + { + [int]$NetVersionKey = Invoke-RegistryGetValue -MachineName $MachineName ` + -SubKey "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" ` + -GetValue "Release" ` + -CatchActionFunction $CatchActionFunction + } + + #Using Minimum Version as per https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed?redirectedfrom=MSDN#minimum-version + if ($NetVersionKey -lt 378389) + { + $friendlyName = "Unknown" + $minValue = -1 + } + elseif ($NetVersionKey -lt 378675) + { + $friendlyName = "4.5" + $minValue = 378389 + } + elseif ($NetVersionKey -lt 379893) + { + $friendlyName = "4.5.1" + $minValue = 378675 + } + elseif ($NetVersionKey -lt 393295) + { + $friendlyName = "4.5.2" + $minValue = 379893 + } + elseif ($NetVersionKey -lt 394254) + { + $friendlyName = "4.6" + $minValue = 393295 + } + elseif ($NetVersionKey -lt 394802) + { + $friendlyName = "4.6.1" + $minValue = 394254 + } + elseif ($NetVersionKey -lt 460798) + { + $friendlyName = "4.6.2" + $minValue = 394802 + } + elseif ($NetVersionKey -lt 461308) + { + $friendlyName = "4.7" + $minValue = 460798 + } + elseif ($NetVersionKey -lt 461808) + { + $friendlyName = "4.7.1" + $minValue = 461308 + } + elseif ($NetVersionKey -lt 528040) + { + $friendlyName = "4.7.2" + $minValue = 461808 + } + elseif ($NetVersionKey -ge 528040) + { + $friendlyName = "4.8" + $minValue = 528040 + } + + $netObject = New-Object PSCustomObject + $netObject | Add-Member -MemberType NoteProperty -Name "FriendlyName" -Value $friendlyName + $netObject | Add-Member -MemberType NoteProperty -Name "RegistryValue" -Value $NetVersionKey + $netObject | Add-Member -MemberType NoteProperty -Name "MinimumValue" -Value $minValue + + Write-VerboseWriter("Returning FriendlyName: {0} | RegistryValue: {1}" -f $friendlyName, $NetVersionKey) + + return $netObject +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-ProcessorInformation/Get-ProcessorInformation.ps1 +Function Get-ProcessorInformation { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$MachineName, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-WmiObjectHandler/Get-WmiObjectHandler.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-ScriptBlockHandler/Invoke-ScriptBlockHandler.ps1 + #> + + Write-VerboseWriter("Calling: Get-ProcessorInformation") + $wmiObject = Get-WmiObjectHandler -ComputerName $MachineName -Class "Win32_Processor" -CatchActionFunction $CatchActionFunction + Write-VerboseWriter("Processor object type: {0}" -f ($wmiObjectType = $wmiObject.GetType().Name)) + $multiProcessorsDetected = $false + + if($wmiObjectType -eq "ManagementObject") + { + $processorName = $wmiObject.Name + $maxClockSpeed = $wmiObject.MaxClockSpeed + $multiProcessorsDetected = $true + } + else + { + $processorName = $wmiObject[0].Name + $maxClockSpeed = $wmiObject[0].MaxClockSpeed + } + + Write-VerboseWriter("Getting the total number of cores in the processor(s)") + $processorIsThrottled = $false + $currentClockSpeed = 0 + $previousProcessor = $null + $differentProcessorsDetected = $false + $differentProcessorCoreCountDetected = $false + foreach($processor in $wmiObject) + { + $numberOfPhysicalCores += $processor.NumberOfCores + $numberOfLogicalCores += $processor.NumberOfLogicalProcessors + $numberOfProcessors++ + + if($processor.CurrentClockSpeed -lt $processor.MaxClockSpeed) + { + Write-VerboseWriter("Processor is being throttled") + $processorIsThrottled = $true + $currentClockSpeed = $processor.CurrentClockSpeed + } + if($previousProcessor -ne $null) + { + if($processor.Name -ne $previousProcessor.Name -or + $processor.MaxClockSpeed -ne $previousProcessor.MaxMegacyclesPerCore) + { + Write-VerboseWriter("Different Processors are detected!!! This is an issue.") + $differentProcessorsDetected = $true + } + if($processor.NumberOfLogicalProcessors -ne $previousProcessor.NumberOfLogicalProcessors) + { + Write-VerboseWriter("Different Processor core count per processor socket detected. This is an issue.") + $differentProcessorCoreCountDetected = $true + } + } + $previousProcessor = $processor + } + Write-VerboseWriter("NumberOfPhysicalCores: {0} | NumberOfLogicalCores: {1} | NumberOfProcessors: {2} | ProcessorIsThrottled: {3} | CurrentClockSpeed: {4} | DifferentProcessorsDetected: {5} | DifferentProcessorCoreCountDetected: {6}" -f $numberOfPhysicalCores, + $numberOfLogicalCores, $numberOfProcessors, $processorIsThrottled, $currentClockSpeed, $differentProcessorsDetected, $differentProcessorCoreCountDetected) + + $presentedProcessorCoreCount = Invoke-ScriptBlockHandler -ComputerName $MachineName -ScriptBlock {[System.Environment]::ProcessorCount} -ScriptBlockDescription "Trying to get the System.Environment ProcessorCount" -CatchActionFunction $CatchActionFunction + if($presentedProcessorCoreCount -eq $null) + { + Write-VerboseWriter("Wasn't able to get Presented Processor Core Count on the Server. Setting to -1.") + $presentedProcessorCoreCount = -1 + } + else + { + Write-VerboseWriter("Presented Processor Core Count: {0}" -f $presentedProcessorCoreCount) + } + + $processorInformationObject = New-Object PSCustomObject + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "Name" -Value $processorName + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "NumberOfPhysicalCores" -Value $numberOfPhysicalCores + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "NumberOfLogicalCores" -Value $numberOfLogicalCores + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "NumberOfProcessors" -Value $numberOfProcessors + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "MaxMegacyclesPerCore" -Value $maxClockSpeed + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "CurrentMegacyclesPerCore" -Value $currentClockSpeed + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "ProcessorIsThrottled" -Value $processorIsThrottled + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "DifferentProcessorsDetected" -Value $differentProcessorsDetected + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "DifferentProcessorCoreCountDetected" -Value $differentProcessorCoreCountDetected + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "EnvironmentProcessorCount" -Value $presentedProcessorCoreCount + $processorInformationObject | Add-Member -MemberType NoteProperty -Name "ProcessorClassObject" -Value $wmiObject + + Write-VerboseWriter("Exiting: Get-ProcessorInformation") + return $processorInformationObject +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-ServerOperatingSystemVersion/Get-ServerOperatingSystemVersion.ps1 +Function Get-ServerOperatingSystemVersion { + [CmdletBinding()] + param( + [string]$OsCaption + ) + + #Function Version 1.5 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + + if($OsCaption -eq [string]::Empty -or + $OsCaption -eq $null) + { + Write-VerboseWriter("Getting the local machine version build number") + $OsCaption = (Get-WmiObject -Class Win32_OperatingSystem).Caption + Write-VerboseWriter("Got '{0}' for the caption" -f $OsCaption) + } + else + { + Write-VerboseWriter("Passed - [string]OsCaption : {0}" -f $OsCaption) + } + + $osReturnValue = [string]::Empty + + switch -Wildcard ($OsCaption) + { + "*Server 2008 R2*" {$osReturnValue = "Windows2008R2"; break} + "*Server 2008*" {$osReturnValue = "Windows2008"} + "*Server 2012 R2*" {$osReturnValue = "Windows2012R2"; break} + "*Server 2012*" {$osReturnValue = "Windows2012"} + "*Server 2016*" {$osReturnValue = "Windows2016"} + "*Server 2019*" {$osReturnValue = "Windows2019"} + "Microsoft Windows Server Standard" {$osReturnValue = "WindowsCore"} + "Microsoft Windows Server Datacenter" {$osReturnValue = "WindowsCore"} + default {$osReturnValue = "Unknown"} + } + + Write-VerboseWriter("Returned: {0}" -f $osReturnValue) + return [string]$osReturnValue + +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-ServerRebootPending/Get-ServerRebootPending.ps1 +Function Get-ServerRebootPending { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$ServerName, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-ScriptBlockHandler/Invoke-ScriptBlockHandler.ps1 + #> + Function Get-PendingFileReboot { + try + { + if((Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\" -Name PendingFileRenameOperations -ErrorAction Stop)) + { + return $true + } + else + { + return $false + } + } + catch + { + throw + } + } + Function Get-PendingSCCMReboot { + try + { + $sccmReboot = Invoke-CimMethod -Namespace 'Root\ccm\clientSDK' -ClassName 'CCM_ClientUtilities' -Name 'DetermineIfRebootPending' -ErrorAction Stop + if($sccmReboot -and ($sccmReboot.RebootPending -or $sccmReboot.IsHardRebootPending)) + { + return $true + } + } + catch + { + throw + } + } + Function Get-PathTestingReboot { + param( + [string]$TestingPath + ) + if(Test-Path $TestingPath) + { + return $true + } + else + { + return $false + } + } + + Write-VerboseWriter("Calling: Get-ServerRebootPending") + if(Invoke-ScriptBlockHandler -ComputerName $ServerName -ScriptBlock ${Function:Get-PendingFileReboot} -ScriptBlockDescription "Get-PendingFileReboot" -CatchActionFunction $CatchActionFunction) + { + Write-VerboseWriter("Get-PendingFileReboot Determined Reboot is pending. Registry HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\ item properties of PendingFileRenameOperations.") + return $true + } + if(Invoke-ScriptBlockHandler -ComputerName $ServerName -ScriptBlock ${Function:Get-PendingSCCMReboot} -ScriptBlockDescription "Get-PendingSCCMReboot" -CatchActionFunction $CatchActionFunction) + { + Write-VerboseWriter("Get-PendingSCCMReboot determined reboot is pending.") + return $true + } + if(Invoke-ScriptBlockHandler -ComputerName $ServerName -ScriptBlock ${Function:Get-PathTestingReboot} -ScriptBlockDescription "Get-PendingAutoUpdateReboot" -CatchActionFunction $CatchActionFunction -ArgumentList "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending") + { + Write-VerboseWriter("Get-PathTestingReboot for HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending determined reboot is pending") + return $true + } + if(Invoke-ScriptBlockHandler -ComputerName $ServerName -ScriptBlock ${Function:Get-PathTestingReboot} -ScriptBlockDescription "Get-PendingAutoUpdateReboot" -CatchActionFunction $CatchActionFunction -ArgumentList "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired") + { + Write-VerboseWriter("Get-PathTestingReboot for HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired determined reboot is pending") + return $true + } + Write-VerboseWriter("Passed all reboot checks.") + return $false +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-ServerType/Get-ServerType.ps1 +Function Get-ServerType { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$ServerType + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + Write-VerboseWriter("Calling: Get-ServerType") + $returnServerType = [string]::Empty + if($ServerType -like "VMware*") { $returnServerType = "VMware"} + elseif($ServerType -like "*Microsoft Corporation*") { $returnServerType = "HyperV" } + elseif($ServerType.Length -gt 0) {$returnServerType = "Physical"} + else { $returnServerType = "Unknown" } + + Write-VerboseWriter("Returning: {0}" -f $returnServerType) + return $returnServerType +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-Smb1ServerSettings/Get-Smb1ServerSettings.ps1 +Function Get-Smb1ServerSettings { +[CmdletBinding()] +param( +[string]$ServerName = $env:COMPUTERNAME, +[scriptblock]$CatchActionFunction +) +#Function Version 1.2 +<# +Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-ScriptBlockHandler/Invoke-ScriptBlockHandler.ps1 +#> + +Write-VerboseWriter("Calling: Get-Smb1ServerSettings") +Write-VerboseWriter("Passed ServerName: {0}" -f $ServerName) +$smbServerConfiguration = Invoke-ScriptBlockHandler -ComputerName $ServerName -ScriptBlock {Get-SmbServerConfiguration} -CatchActionFunction $CatchActionFunction -ScriptBlockDescription "Get-SmbServerConfiguration" + +<# +Unknown 0 +Failed to get Install Setting 1 +Install is set to true 2 +Install is set to false 4 +Failed to get Block Setting 8 +SMB1 is not being blocked 16 +SMB1 is being blocked 32 +#> + +$smb1Status = 0 + +try +{ + $windowsFeature = Get-WindowsFeature "FS-SMB1" -ComputerName $ServerName -ErrorAction Stop +} +catch +{ + Write-VerboseWriter("Failed to Get-WindowsFeature for FS-SMB1") + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } +} + +if ($windowsFeature -eq $null) +{ + $smb1Status += 1 +} +elseif ($windowsFeature.Installed) +{ + $smb1Status += 2 +} +else +{ + $smb1Status += 4 +} + +if ($smbServerConfiguration -eq $null) +{ + $smb1Status += 8 +} +elseif ($smbServerConfiguration.EnableSMB1Protocol) +{ + $smb1Status += 16 +} +else +{ + $smb1Status += 32 +} + +$smb1ServerSettings = New-Object PSCustomObject +$smb1ServerSettings | Add-Member -MemberType NoteProperty -Name "SmbServerConfiguration" -Value $smbServerConfiguration +$smb1ServerSettings | Add-Member -MemberType NoteProperty -Name "WindowsFeature" -Value $windowsFeature +$smb1ServerSettings | Add-Member -MemberType NoteProperty -Name "Smb1Status" -Value $smb1Status + +return $smb1ServerSettings + +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-TimeZoneInformationRegistrySettings/Get-TimeZoneInformationRegistrySettings.ps1 +Function Get-TimeZoneInformationRegistrySettings { + [CmdletBinding()] + param( + [string]$MachineName = $env:COMPUTERNAME, + [scriptblock]$CatchActionFunction + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 + #> + Write-VerboseWriter("Calling: Get-TimeZoneInformationRegistrySettings") + Write-VerboseWriter("Passed: [string]MachineName: {0}" -f $MachineName) + $timeZoneInformationSubKey = "SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + $dynamicDaylightTimeDisabled = Invoke-RegistryGetValue -MachineName $MachineName -SubKey $timeZoneInformationSubKey -GetValue "DynamicDaylightTimeDisabled" -CatchActionFunction $CatchActionFunction + $timeZoneKeyName = Invoke-RegistryGetValue -MachineName $MachineName -Subkey $timeZoneInformationSubKey -GetValue "TimeZoneKeyName" -CatchActionFunction $CatchActionFunction + $standardStart = Invoke-RegistryGetValue -MachineName $MachineName -SubKey $timeZoneInformationSubKey -GetValue "StandardStart" -CatchActionFunction $CatchActionFunction + $daylightStart = Invoke-RegistryGetValue -MachineName $MachineName -SubKey $timeZoneInformationSubKey -GetValue "DaylightStart" -CatchActionFunction $CatchActionFunction + + $timeZoneInformationObject = New-Object PSCustomObject + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "DynamicDaylightTimeDisabled" -Value $dynamicDaylightTimeDisabled + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "TimeZoneKeyName" -Value $timeZoneKeyName + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "StandardStart" -Value $standardStart + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "DaylightStart" -Value $daylightStart + + $actionsToTake = @() + if($timeZoneKeyName -eq $null -or + [string]::IsNullOrEmpty($timeZoneKeyName)) + { + Write-VerboseWriter("TimeZoneKeyName is null or empty. Action should be taken to address this.") + $actionsToTake += "TimeZoneKeyName is blank. Need to switch your current time zone to a different value, then switch it back to have this value populated again." + } + foreach($value in $standardStart) + { + if($value -ne 0) + { + $standardStartNonZeroValue = $true + break + } + } + foreach($value in $daylightStart) + { + if($value -ne 0) + { + $daylightStartNonZeroValue = $true + break + } + } + if($dynamicDaylightTimeDisabled -ne 0 -and ( + $standardStartNonZeroValue -or + $daylightStartNonZeroValue + )) + { + Write-VerboseWriter("Determined that there is a chance the settings set could cause a DST issue.") + $dstIssueDetected = $true + $actionsToTake += "High Warning: DynamicDaylightTimeDisabled is set, Windows can not properly detect any DST rule changes in your time zone. ` + It is possible that you could be running into this issue. Set 'Adjust for daylight saving time automatically to on'" + } + elseif($dynamicDaylightTimeDisabled -ne 0) + { + Write-VerboseWriter("Daylight savings auto adjustment is disabled.") + $actionsToTake += "Warning: DynamicDaylightTimeDisabled is set, Windows can not properly detect any DST rule changes in your time zone." + } + + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "DstIssueDetected" -Value $dstIssueDetected + $timeZoneInformationObject | Add-Member -MemberType NoteProperty -Name "ActionsToTake" -Value $actionsToTake + + return $timeZoneInformationObject + } + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Get-WmiObjectHandler/Get-WmiObjectHandler.ps1 +Function Get-WmiObjectHandler { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)][string]$ComputerName = $env:COMPUTERNAME, + [Parameter(Mandatory=$true)][string]$Class, + [Parameter(Mandatory=$false)][string]$Filter, + [Parameter(Mandatory=$false)][string]$Namespace, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.0 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + Write-VerboseWriter("Calling: Get-WmiObjectHandler") + Write-VerboseWriter("Passed: [string]ComputerName: {0} | [string]Class: {1} | [string]Filter: {2} | [string]Namespace: {3}" -f $ComputerName, $Class, $Filter, $Namespace) + $execute = @{ + ComputerName = $ComputerName + Class = $Class + } + if(![string]::IsNullOrEmpty($Filter)) + { + $execute.Add("Filter", $Filter) + } + if(![string]::IsNullOrEmpty($Namespace)) + { + $execute.Add("Namespace", $Namespace) + } + try + { + $wmi = Get-WmiObject @execute -ErrorAction Stop + return $wmi + } + catch + { + Write-VerboseWriter("Failed to run Get-WmiObject object on class '{0}'" -f $Class) + if($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + } +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-RegistryGetValue/Invoke-RegistryGetValue.ps1 +Function Invoke-RegistryGetValue { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)][string]$RegistryHive = "LocalMachine", + [Parameter(Mandatory=$true)][string]$MachineName, + [Parameter(Mandatory=$true)][string]$SubKey, + [Parameter(Mandatory=$false)][string]$GetValue, + [Parameter(Mandatory=$false)][bool]$ReturnAfterOpenSubKey, + [Parameter(Mandatory=$false)][object]$DefaultValue, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + + #Function Version 1.2 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + Write-VerboseWriter("Calling: Invoke-RegistryGetValue") + try + { + Write-VerboseWriter("Attempting to open the Base Key '{0}' on Server '{1}'" -f $RegistryHive, $MachineName) + $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryHive, $MachineName) + Write-VerboseWriter("Attempting to open the Sub Key '{0}'" -f $SubKey) + $RegKey= $Reg.OpenSubKey($SubKey) + + if ($ReturnAfterOpenSubKey) + { + Write-VerboseWriter("Returning OpenSubKey") + return $RegKey + } + + Write-VerboseWriter("Attempting to get the value '{0}'" -f $GetValue) + $returnGetValue = $RegKey.GetValue($GetValue) + + if ($null -eq $returnGetValue -and + $null -ne $DefaultValue) + { + Write-VerboseWriter("No value found in the registry. Setting to default value: {0}" -f $DefaultValue) + $returnGetValue = $DefaultValue + } + + Write-VerboseWriter("Exiting: Invoke-RegistryHandler | Returning: {0}" -f $returnGetValue) + return $returnGetValue + } + catch + { + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + + Write-VerboseWriter("Failed to open the registry") + } + +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Invoke-ScriptBlockHandler/Invoke-ScriptBlockHandler.ps1 +Function Invoke-ScriptBlockHandler { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)][string]$ComputerName, + [Parameter(Mandatory=$true)][scriptblock]$ScriptBlock, + [Parameter(Mandatory=$false)][string]$ScriptBlockDescription, + [Parameter(Mandatory=$false)][object]$ArgumentList, + [Parameter(Mandatory=$false)][bool]$IncludeNoProxyServerOption = $true, #Default in HealthChecker + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.1 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + Write-VerboseWriter("Calling: Invoke-ScriptBlockHandler") + if(![string]::IsNullOrEmpty($ScriptBlockDescription)) + { + Write-VerboseWriter($ScriptBlockDescription) + } + try + { + if($ComputerName -ne $env:COMPUTERNAME) + { + $params = @{ + ComputerName = $ComputerName + ScriptBlock = $ScriptBlock + ErrorAction = "Stop" + } + + if ($IncludeNoProxyServerOption) + { + Write-VerboseWriter("Including SessionOption") + $params.Add("SessionOption", (New-PSSessionOption -ProxyAccessType NoProxyServer)) + } + + if($ArgumentList -ne $null) + { + $params.Add("ArgumentList", $ArgumentList) + Write-VerboseWriter("Running Invoke-Command with argument list.") + + } + else + { + Write-VerboseWriter("Running Invoke-Command without argument list.") + } + + $invokeReturn = Invoke-Command @params + return $invokeReturn + } + else + { + if($ArgumentList -ne $null) + { + Write-VerboseWriter("Running Script Block locally with argument list.") + $localReturn = & $ScriptBlock $ArgumentList + } + else + { + Write-VerboseWriter("Running Script Block locally without argument list.") + $localReturn = & $ScriptBlock + } + return $localReturn + } + } + catch + { + Write-VerboseWriter("Failed to Invoke-ScriptBlockHandler") + if($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + } +} + +Function Get-ExchangeAppPoolsInformation { + + Write-VerboseOutput("Calling: Get-ExchangeAppPoolsInformation") + + Function Get-ExchangeAppPoolsScriptBlock + { + $windir = $env:windir + $Script:appCmd = "{0}\system32\inetsrv\appcmd.exe" -f $windir + + $appPools = &$Script:appCmd list apppool + $exchangeAppPools = @() + foreach($appPool in $appPools) + { + $startIndex = $appPool.IndexOf('"') + 1 + $appPoolName = $appPool.Substring($startIndex, ($appPool.Substring($startIndex).IndexOf('"'))) + if($appPoolName.StartsWith("MSExchange")) + { + $exchangeAppPools += $appPoolName + } + } + + $exchAppPools = @{} + foreach($appPool in $exchangeAppPools) + { + $status = &$Script:appCmd list apppool $appPool /text:state + $config = &$Script:appCmd list apppool $appPool /text:CLRConfigFile + if(!([System.String]::IsNullOrEmpty($config)) -and + (Test-Path $config)) + { + $content = Get-Content $config + } + else + { + $content = $null + } + $statusObj = New-Object pscustomobject + $statusObj | Add-Member -MemberType NoteProperty -Name "Status" -Value $status + $statusObj | Add-Member -MemberType NoteProperty -Name "ConfigPath" -Value $config + $statusObj | Add-Member -MemberType NoteProperty -Name "Content" -Value $content + + $exchAppPools.Add($appPool, $statusObj) + } + + return $exchAppPools + } + $exchangeAppPoolsInfo = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock ${Function:Get-ExchangeAppPoolsScriptBlock} -ScriptBlockDescription "Getting Exchange App Pool information" -CatchActionFunction ${Function:Invoke-CatchActions} + Write-VerboseOutput("Exiting: Get-ExchangeAppPoolsInformation") + return $exchangeAppPoolsInfo +} + +Function Get-ExchangeInformation { +param( +[HealthChecker.OSServerVersion]$OSMajorVersion +) + Write-VerboseOutput("Calling: Get-ExchangeInformation") + Write-VerboseOutput("Passed: OSMajorVersion: {0}" -f $OSMajorVersion) + [HealthChecker.ExchangeInformation]$exchangeInformation = New-Object -TypeName HealthChecker.ExchangeInformation + $exchangeInformation.GetExchangeServer = (Get-ExchangeServer -Identity $Script:Server) + $exchangeInformation.ExchangeCertificates = Get-ExchangeServerCertificates + $buildInformation = $exchangeInformation.BuildInformation + $buildInformation.MajorVersion = ([HealthChecker.ExchangeMajorVersion](Get-ExchangeMajorVersion -AdminDisplayVersion $exchangeInformation.GetExchangeServer.AdminDisplayVersion)) + $buildInformation.ServerRole = (Get-ServerRole -ExchangeServerObj $exchangeInformation.GetExchangeServer) + $buildInformation.ExchangeSetup = Get-ExSetupDetails + + #Exchange 2013 or greater + if($buildInformation.MajorVersion -ge [HealthChecker.ExchangeMajorVersion]::Exchange2013) + { + $netFrameworkExchange = $exchangeInformation.NETFramework + $adminDisplayVersion = $exchangeInformation.GetExchangeServer.AdminDisplayVersion + $revisionNumber = if($adminDisplayVersion.Revision -lt 10) {$adminDisplayVersion.Revision / 10} else {$adminDisplayVersion.Revision / 100 } + $buildAndRevision = $adminDisplayVersion.Build + $revisionNumber + Write-VerboseOutput("The build and revision number: {0}" -f $buildAndRevision) + #Build Numbers: https://docs.microsoft.com/en-us/Exchange/new-features/build-numbers-and-release-dates?view=exchserver-2019 + $buildInformation.BuildNumber = "{0}.{1}.{2}.{3}" -f $adminDisplayVersion.Major, $adminDisplayVersion.Minor, $adminDisplayVersion.Build, $adminDisplayVersion.Revision + if($buildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2019) + { + Write-VerboseOutput("Exchange 2019 is detected. Checking build number...") + $buildInformation.FriendlyName = "Exchange 2019 " + + #Exchange 2019 Information + if($buildAndRevision -lt 221.12) { $buildInformation.CU = [HealthChecker.ExchangeCULevel]::Preview; $buildInformation.FriendlyName += "Preview"; $buildInformation.ReleaseDate = "07/24/2018" } + elseif($buildAndRevision -lt 330.6) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::RTM; $buildInformation.FriendlyName += "RTM"; $buildInformation.ReleaseDate = "10/22/2018" } + elseif($buildAndRevision -lt 397.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU1; $buildInformation.FriendlyName += "CU1"; $buildInformation.ReleaseDate = "02/12/2019"} + elseif($buildAndRevision -lt 464.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU2; $buildInformation.FriendlyName += "CU2"; $buildInformation.ReleaseDate = "06/18/2019"} + elseif($buildAndRevision -lt 529.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU3; $buildInformation.FriendlyName += "CU3"; $buildInformation.ReleaseDate = "09/17/2019"} + elseif($buildAndRevision -lt 595.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU4; $buildInformation.FriendlyName += "CU4"; $buildInformation.ReleaseDate = "12/17/2019"} + elseif($buildAndRevision -lt 659.4) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU5; $buildInformation.FriendlyName += "CU5"; $buildInformation.ReleaseDate = "03/17/2020"} + elseif($buildAndRevision -lt 721.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU6; $buildInformation.FriendlyName += "CU6"; $buildInformation.ReleaseDate = "06/16/2020"} + elseif($buildAndRevision -lt 792.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU7; $buildInformation.FriendlyName += "CU7"; $buildInformation.ReleaseDate = "09/15/2020"; $buildInformation.SupportedBuild = $true} + elseif($buildAndRevision -ge 792.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU8; $buildInformation.FriendlyName += "CU8"; $buildInformation.ReleaseDate = "12/15/2020"; $buildInformation.SupportedBuild = $true} + + #Exchange 2019 .NET Information + if($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU2){$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU4){$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8} + else { $netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8 } + + } + elseif($buildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2016) + { + Write-VerboseOutput("Exchange 2016 is detected. Checking build number...") + $buildInformation.FriendlyName = "Exchange 2016 " + + #Exchange 2016 Information + if($buildAndRevision -lt 225.42) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::Preview; $buildInformation.FriendlyName += "Preview"; $buildInformation.ReleaseDate = "07/22/2015"} + elseif($buildAndRevision -lt 396.30) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::RTM; $buildInformation.FriendlyName += "RTM"; $buildInformation.ReleaseDate = "10/01/2015"} + elseif($buildAndRevision -lt 466.34) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU1; $buildInformation.FriendlyName += "CU1"; $buildInformation.ReleaseDate = "03/15/2016"} + elseif($buildAndRevision -lt 544.27) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU2; $buildInformation.FriendlyName += "CU2"; $buildInformation.ReleaseDate = "06/21/2016"} + elseif($buildAndRevision -lt 669.32) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU3; $buildInformation.FriendlyName += "CU3"; $buildInformation.ReleaseDate = "09/20/2016"} + elseif($buildAndRevision -lt 845.34) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU4; $buildInformation.FriendlyName += "CU4"; $buildInformation.ReleaseDate = "12/13/2016"} + elseif($buildAndRevision -lt 1034.26) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU5; $buildInformation.FriendlyName += "CU5"; $buildInformation.ReleaseDate = "03/21/2017"} + elseif($buildAndRevision -lt 1261.35) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU6; $buildInformation.FriendlyName += "CU6"; $buildInformation.ReleaseDate = "06/24/2017"} + elseif($buildAndRevision -lt 1415.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU7; $buildInformation.FriendlyName += "CU7"; $buildInformation.ReleaseDate = "09/16/2017"} + elseif($buildAndRevision -lt 1466.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU8; $buildInformation.FriendlyName += "CU8"; $buildInformation.ReleaseDate = "12/19/2017"} + elseif($buildAndRevision -lt 1531.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU9; $buildInformation.FriendlyName += "CU9"; $buildInformation.ReleaseDate = "03/20/2018"} + elseif($buildAndRevision -lt 1591.10) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU10; $buildInformation.FriendlyName += "CU10"; $buildInformation.ReleaseDate = "06/19/2018"} + elseif($buildAndRevision -lt 1713.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU11; $buildInformation.FriendlyName += "CU11"; $buildInformation.ReleaseDate = "10/16/2018"} + elseif($buildAndRevision -lt 1779.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU12; $buildInformation.FriendlyName += "CU12"; $buildInformation.ReleaseDate = "02/12/2019"} + elseif($buildAndRevision -lt 1847.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU13; $buildInformation.FriendlyName += "CU13"; $buildInformation.ReleaseDate = "06/18/2019"} + elseif($buildAndRevision -lt 1913.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU14; $buildInformation.FriendlyName += "CU14"; $buildInformation.ReleaseDate = "09/17/2019"} + elseif($buildAndRevision -lt 1979.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU15; $buildInformation.FriendlyName += "CU15"; $buildInformation.ReleaseDate = "12/17/2019"} + elseif($buildAndRevision -lt 2044.4) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU16; $buildInformation.FriendlyName += "CU16"; $buildInformation.ReleaseDate = "03/17/2020"} + elseif($buildAndRevision -lt 2106.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU17; $buildInformation.FriendlyName += "CU17"; $buildInformation.ReleaseDate = "06/16/2020"} + elseif($buildAndRevision -lt 2176.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU18; $buildInformation.FriendlyName += "CU18"; $buildInformation.ReleaseDate = "09/15/2020"; $buildInformation.SupportedBuild = $true} + elseif($buildAndRevision -ge 2176.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU19; $buildInformation.FriendlyName += "CU19"; $buildInformation.ReleaseDate = "12/15/2020"; $buildInformation.SupportedBuild = $true} + + #Exchange 2016 .NET Information + if($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU2){$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix} + elseif($buildInformation.CU -eq [HealthChecker.ExchangeCULevel]::CU2){$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d1wFix} + elseif($buildInformation.CU -eq [HealthChecker.ExchangeCULevel]::CU3) + { + if($OSMajorVersion -eq [HealthChecker.OSServerVersion]::Windows2016) + { + $netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix + $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2 + } + else + { + $netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix + $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d1wFix + } + } + elseif($buildInformation.CU -eq [HealthChecker.ExchangeCULevel]::CU4) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU8) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU11) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d1} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU13) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d1; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU15) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8} + else {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8} + } + else + { + Write-VerboseOutput("Exchange 2013 is detected. Checking build number...") + $buildInformation.FriendlyName = "Exchange 2013 " + + #Exchange 2013 Information + if($buildAndRevision -lt 620.29) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::RTM; $buildInformation.FriendlyName += "RTM"; $buildInformation.ReleaseDate = "12/03/2012"} + elseif($buildAndRevision -lt 712.24) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU1; $buildInformation.FriendlyName += "CU1"; $buildInformation.ReleaseDate = "04/02/2013"} + elseif($buildAndRevision -lt 775.38) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU2; $buildInformation.FriendlyName += "CU2"; $buildInformation.ReleaseDate = "07/09/2013"} + elseif($buildAndRevision -lt 847.32) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU3; $buildInformation.FriendlyName += "CU3"; $buildInformation.ReleaseDate = "11/25/2013"} + elseif($buildAndRevision -lt 913.22) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU4; $buildInformation.FriendlyName += "CU4"; $buildInformation.ReleaseDate = "02/25/2014"} + elseif($buildAndRevision -lt 995.29) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU5; $buildInformation.FriendlyName += "CU5"; $buildInformation.ReleaseDate = "05/27/2014"} + elseif($buildAndRevision -lt 1044.25) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU6; $buildInformation.FriendlyName += "CU6"; $buildInformation.ReleaseDate = "08/26/2014"} + elseif($buildAndRevision -lt 1076.9) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU7; $buildInformation.FriendlyName += "CU7"; $buildInformation.ReleaseDate = "12/09/2014"} + elseif($buildAndRevision -lt 1104.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU8; $buildInformation.FriendlyName += "CU8"; $buildInformation.ReleaseDate = "03/17/2015"} + elseif($buildAndRevision -lt 1130.7) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU9; $buildInformation.FriendlyName += "CU9"; $buildInformation.ReleaseDate = "06/17/2015"} + elseif($buildAndRevision -lt 1156.6) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU10; $buildInformation.FriendlyName += "CU10"; $buildInformation.ReleaseDate = "09/15/2015"} + elseif($buildAndRevision -lt 1178.4) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU11; $buildInformation.FriendlyName += "CU11"; $buildInformation.ReleaseDate = "12/15/2015"} + elseif($buildAndRevision -lt 1210.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU12; $buildInformation.FriendlyName += "CU12"; $buildInformation.ReleaseDate = "03/15/2016"} + elseif($buildAndRevision -lt 1236.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU13; $buildInformation.FriendlyName += "CU13"; $buildInformation.ReleaseDate = "06/21/2016"} + elseif($buildAndRevision -lt 1263.5) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU14; $buildInformation.FriendlyName += "CU14"; $buildInformation.ReleaseDate = "09/20/2016"} + elseif($buildAndRevision -lt 1293.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU15; $buildInformation.FriendlyName += "CU15"; $buildInformation.ReleaseDate = "12/13/2016"} + elseif($buildAndRevision -lt 1320.4) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU16; $buildInformation.FriendlyName += "CU16"; $buildInformation.ReleaseDate = "03/21/2017"} + elseif($buildAndRevision -lt 1347.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU17; $buildInformation.FriendlyName += "CU17"; $buildInformation.ReleaseDate = "06/24/2017"} + elseif($buildAndRevision -lt 1365.1) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU18; $buildInformation.FriendlyName += "CU18"; $buildInformation.ReleaseDate = "09/16/2017"} + elseif($buildAndRevision -lt 1367.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU19; $buildInformation.FriendlyName += "CU19"; $buildInformation.ReleaseDate = "12/19/2017"} + elseif($buildAndRevision -lt 1395.4) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU20; $buildInformation.FriendlyName += "CU20"; $buildInformation.ReleaseDate = "03/20/2018"} + elseif($buildAndRevision -lt 1473.3) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU21; $buildInformation.FriendlyName += "CU21"; $buildInformation.ReleaseDate = "06/19/2018"} + elseif($buildAndRevision -lt 1497.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU22; $buildInformation.FriendlyName += "CU22"; $buildInformation.ReleaseDate = "02/12/2019"} + elseif($buildAndRevision -ge 1497.2) {$buildInformation.CU = [HealthChecker.ExchangeCULevel]::CU23; $buildInformation.FriendlyName += "CU23"; $buildInformation.ReleaseDate = "06/18/2019"; $buildInformation.SupportedBuild = $true} + + #Exchange 2013 .NET Information + if($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU12){$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU15) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d1wFix} + elseif($buildInformation.CU -eq [HealthChecker.ExchangeCULevel]::CU15) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d5d2wFix; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU19) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2} + elseif($buildInformation.CU -lt [HealthChecker.ExchangeCULevel]::CU21) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d6d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d1} + elseif($buildInformation.CU -le [HealthChecker.ExchangeCULevel]::CU22) {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d1; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2} + else {$netFrameworkExchange.MinSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d7d2; $netFrameworkExchange.MaxSupportedVersion = [HealthChecker.NetMajorVersion]::Net4d8} + } + + $exchangeInformation.MapiHttpEnabled = (Get-OrganizationConfig).MapiHttpEnabled + + if ($buildInformation.ServerRole -ne [HealthChecker.ExchangeServerRole]::Edge) + { + $exchangeInformation.ApplicationPools = Get-ExchangeAppPoolsInformation + } + + $buildInformation.KBsInstalled = Get-ExchangeUpdates -ExchangeMajorVersion $buildInformation.MajorVersion + $exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage = Invoke-RegistryGetValue -MachineName $Script:Server -SubKey "SOFTWARE\Microsoft\ExchangeServer\v15\Search\SystemParameters" -GetValue "CtsProcessorAffinityPercentage" -CatchActionFunction ${Function:Invoke-CatchActions} + $exchangeInformation.ServerMaintenance = Get-ExchangeServerMaintenanceState -ComponentsToSkip "ForwardSyncDaemon","ProvisioningRps" + if($buildInformation.ServerRole -ne [HealthChecker.ExchangeServerRole]::ClientAccess) + { + $exchangeInformation.ExchangeServicesNotRunning = Test-ServiceHealth -Server $Script:Server | %{$_.ServicesNotRunning} + } + } + elseif($buildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2010) + { + Write-VerboseOutput("Exchange 2010 detected.") + $buildInformation.FriendlyName = "Exchange 2010" + $buildInformation.BuildNumber = $exchangeInformation.GetExchangeServer.AdminDisplayVersion.ToString() + } + + Write-VerboseOutput("Exiting: Get-ExchangeInformation") + return $exchangeInformation +} + +Function Get-ExchangeServerCertificates { + + Write-VerboseOutput("Calling: Get-ExchangeServerCertificates") + + try + { + Write-VerboseOutput("Trying to receive certificates from Exchange server: {0}" -f $Script:Server) + $exchangeServerCertificates = Get-ExchangeCertificate -Server $Script:Server -ErrorAction Stop + + if($null -ne $exchangeServerCertificates) + { + try + { + $authConfig = Get-AuthConfig -ErrorAction Stop + $authConfigDetected = $true + } + catch + { + $authConfigDetected = $false + Invoke-CatchActions + } + + [array]$certObject = @() + foreach ($cert in $exchangeServerCertificates) + { + try + { + $certificateLifetime = ([DateTime]($cert.NotAfter) - (Get-Date)).Days + $sanCertificateInfo = $false + + if ($null -ne $cert.DnsNameList -and + ($cert.DnsNameList).Count -gt 1) + { + $sanCertificateInfo = $true + } + + if($authConfigDetected) + { + $isAuthConfigInfo = $false + + if ($cert.Thumbprint -eq $authConfig.CurrentCertificateThumbprint) + { + $isAuthConfigInfo = $true + } + } + else + { + $isAuthConfigInfo = "InvalidAuthConfig" + } + + $certInformationObj = New-Object PSCustomObject + $certInformationObj | Add-Member -MemberType NoteProperty -Name "FriendlyName" -Value $cert.FriendlyName + $certInformationObj | Add-Member -MemberType NoteProperty -Name "Thumbprint" -Value $cert.Thumbprint + $certInformationObj | Add-Member -MemberType NoteProperty -Name "PublicKeySize" -value $cert.PublicKeySize + $certInformationObj | Add-Member -MemberType NoteProperty -Name "IsSanCertificate" -Value $sanCertificateInfo + $certInformationObj | Add-Member -MemberType NoteProperty -Name "Namespaces" -Value $cert.DnsNameList + $certInformationObj | Add-Member -MemberType NoteProperty -Name "Services" -Value $cert.Services + $certInformationObj | Add-Member -MemberType NoteProperty -Name "IsCurrentAuthConfigCertificate" -Value $isAuthConfigInfo + $certInformationObj | Add-Member -MemberType NoteProperty -Name "LifetimeInDays" -Value $certificateLifetime + $certInformationObj | Add-Member -MemberType NoteProperty -Name "Status" -Value $cert.Status + $certInformationObj | Add-Member -MemberType NoteProperty -Name "CertificateObject" -Value $cert + + $certObject += $certInformationObj + } + catch + { + Write-VerboseOutput("Unable to process certificate: {0}" -f $cert.Thumbprint) + Invoke-CatchActions + } + } + Write-VerboseOutput("Processed: {0} certificates" -f $certObject.Count) + return $certObject + } + else + { + Write-VerboseOutput("Failed to find any Exchange certificates") + return $null + } + } + catch + { + Write-VerboseWriter("Failed to run Get-ExchangeCertificate. Error: {0}." -f $Error[0].Exception) + Invoke-CatchActions + } +} + +Function Get-ExchangeServerMaintenanceState { +param( +[Parameter(Mandatory=$false)][array]$ComponentsToSkip +) + Write-VerboseOutput("Calling Function: Get-ExchangeServerMaintenanceState") + + [HealthChecker.ExchangeServerMaintenance]$serverMaintenance = New-Object -TypeName HealthChecker.ExchangeServerMaintenance + $serverMaintenance.GetServerComponentState = Get-ServerComponentState -Identity $Script:Server -ErrorAction SilentlyContinue + + try + { + $serverMaintenance.GetMailboxServer = Get-MailboxServer -Identity $Script:Server -ErrorAction SilentlyContinue + } + catch + { + Write-VerboseOutput("Failed to run Get-MailboxServer") + Invoke-CatchActions + } + + try + { + $serverMaintenance.GetClusterNode = Get-ClusterNode -Name $Script:Server -ErrorAction Stop + } + catch + { + Write-VerboseOutput("Failed to run Get-ClusterNode") + Invoke-CatchActions + } + + Write-VerboseOutput("Running ServerComponentStates checks") + + foreach ($component in $serverMaintenance.GetServerComponentState) + { + if (($ComponentsToSkip -ne $null -and + $ComponentsToSkip.Count -ne 0) -and + $ComponentsToSkip -notcontains $component.Component) + { + if ($component.State -ne "Active") + { + $latestLocalState = $null + $latestRemoteState = $null + + if ($component.LocalStates -ne $null) + { + $latestLocalState = ($component.LocalStates | Sort-Object {$_.TimeStamp} -ErrorAction SilentlyContinue)[-1] + } + + if ($component.RemoteStates -ne $null) + { + $latestRemoteState = ($component.RemoteStates | Sort-Object {$_.TimeStamp} -ErrorAction SilentlyContinue)[-1] + } + + Write-VerboseOutput("Component: {0} LocalState: '{1}' RemoteState: '{2}'" -f $component.Component, $latestLocalState.State, $latestRemoteState.State) + + if ($latestLocalState.State -eq $latestRemoteState.State) + { + $serverMaintenance.InactiveComponents += "'{0}' is in Maintenance Mode" -f $component.Component + } + else + { + if (($latestLocalState -ne $null) -and + ($latestLocalState.State -ne "Active")) + { + $serverMaintenance.InactiveComponents += "'{0}' is in Local Maintenance Mode only" -f $component.Component + } + + if (($latestRemoteState -ne $null) -and + ($latestRemoteState.State -ne "Active")) + { + $serverMaintenance.InactiveComponents += "'{0}' is in Remote Maintenance Mode only" -f $component.Component + } + } + } + else + { + Write-VerboseOutput("Component '{0}' is Active" -f $component.Component) + } + } + else + { + Write-VerboseOutput("Component: {0} will be skipped" -f $component.Component) + } + } + + return $serverMaintenance +} + +Function Get-ExchangeUpdates { +param( +[Parameter(Mandatory=$true)][HealthChecker.ExchangeMajorVersion]$ExchangeMajorVersion +) + Write-VerboseOutput("Calling: Get-ExchangeUpdates") + Write-VerboseOutput("Passed: {0}" -f $ExchangeMajorVersion.ToString()) + $RegLocation = [string]::Empty + + if([HealthChecker.ExchangeMajorVersion]::Exchange2013 -eq $ExchangeMajorVersion) + { + $RegLocation = "SOFTWARE\Microsoft\Updates\Exchange 2013" + } + elseif([HealthChecker.ExchangeMajorVersion]::Exchange2016 -eq $ExchangeMajorVersion) + { + $RegLocation = "SOFTWARE\Microsoft\Updates\Exchange 2016" + } + else + { + $RegLocation = "SOFTWARE\Microsoft\Updates\Exchange 2019" + } + + $RegKey = Invoke-RegistryGetValue -MachineName $Script:Server -SubKey $RegLocation -ReturnAfterOpenSubKey $true -CatchActionFunction ${Function:Invoke-CatchActions} + + if($null -ne $RegKey) + { + $IU = $RegKey.GetSubKeyNames() + if($null -ne $IU) + { + Write-VerboseOutput("Detected fixes installed on the server") + $fixes = @() + foreach($key in $IU) + { + $IUKey = $RegKey.OpenSubKey($key) + $IUName = $IUKey.GetValue("PackageName") + Write-VerboseOutput("Found: " + $IUName) + $fixes += $IUName + } + return $fixes + } + else + { + Write-VerboseOutput("No IUs found in the registry") + } + } + else + { + Write-VerboseOutput("No RegKey returned") + } + + Write-VerboseOutput("Exiting: Get-ExchangeUpdates") + return $null +} + +Function Get-ExSetupDetails { + + Write-VerboseOutput("Calling: Get-ExSetupDetails") + $exSetupDetails = [string]::Empty + Function Get-ExSetupDetailsScriptBlock { + Get-Command ExSetup | ForEach-Object{$_.FileVersionInfo} + } + + $exSetupDetails = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock ${Function:Get-ExSetupDetailsScriptBlock} -ScriptBlockDescription "Getting ExSetup remotely" -CatchActionFunction ${Function:Invoke-CatchActions} + Write-VerboseOutput("Exiting: Get-ExSetupDetails") + return $exSetupDetails +} + +Function Get-HealthCheckerExchangeServer { + + Write-VerboseOutput("Calling: Get-HealthCheckerExchangeServer") + + [HealthChecker.HealthCheckerExchangeServer]$HealthExSvrObj = New-Object -TypeName HealthChecker.HealthCheckerExchangeServer + $HealthExSvrObj.ServerName = $Script:Server + $HealthExSvrObj.HardwareInformation = Get-HardwareInformation + $HealthExSvrObj.OSInformation = Get-OperatingSystemInformation + $HealthExSvrObj.ExchangeInformation = Get-ExchangeInformation -OSMajorVersion $HealthExSvrObj.OSInformation.BuildInformation.MajorVersion + if($HealthExSvrObj.ExchangeInformation.BuildInformation.MajorVersion -ge [HealthChecker.ExchangeMajorVersion]::Exchange2013) + { + $netFrameworkVersion = Get-NETFrameworkVersion -MachineName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} + $HealthExSvrObj.OSInformation.NETFramework.FriendlyName = $netFrameworkVersion.FriendlyName + $HealthExSvrObj.OSInformation.NETFramework.RegistryValue = $netFrameworkVersion.RegistryValue + $HealthExSvrObj.OSInformation.NETFramework.NetMajorVersion = $netFrameworkVersion.MinimumValue + $HealthExSvrObj.OSInformation.NETFramework.FileInformation = Get-DotNetDllFileVersions -ComputerName $Script:Server -FileNames @("System.Data.dll","System.Configuration.dll") -CatchActionFunction ${Function:Invoke-CatchActions} + + if ($netFrameworkVersion.MinimumValue -eq $HealthExSvrObj.ExchangeInformation.NETFramework.MaxSupportedVersion) + { + $HealthExSvrObj.ExchangeInformation.NETFramework.OnRecommendedVersion = $true + } + } + $HealthExSvrObj.HealthCheckerVersion = $healthCheckerVersion + Write-VerboseOutput("Finished building health Exchange Server Object for server: " + $Script:Server) + return $HealthExSvrObj +} + +Function Get-CredentialGuardEnabled { + + Write-VerboseOutput("Calling: Get-CredentialGuardEnabled") + + $registryValue = Invoke-RegistryGetValue -MachineName $Script:Server -SubKey "SYSTEM\CurrentControlSet\Control\LSA" -GetValue "LsaCfgFlags" -CatchActionFunction ${Function:Invoke-CatchActions} + + if ($registryValue -ne $null -and + $registryValue -ne 0) + { + return $true + } + + return $false +} + +Function Get-HardwareInformation { + + Write-VerboseOutput("Calling: Get-HardwareInformation") + + [HealthChecker.HardwareInformation]$hardware_obj = New-Object HealthChecker.HardwareInformation + $system = Get-WmiObjectHandler -ComputerName $Script:Server -Class "Win32_ComputerSystem" -CatchActionFunction ${Function:Invoke-CatchActions} + $hardware_obj.Manufacturer = $system.Manufacturer + $hardware_obj.System = $system + $hardware_obj.AutoPageFile = $system.AutomaticManagedPagefile + $hardware_obj.TotalMemory = $system.TotalPhysicalMemory + $hardware_obj.ServerType = (Get-ServerType -ServerType $system.Manufacturer) + $processorInformation = Get-ProcessorInformation -MachineName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} + + #Need to do it this way because of Windows 2012R2 + $processor = New-Object HealthChecker.ProcessorInformation + $processor.Name = $processorInformation.Name + $processor.NumberOfPhysicalCores = $processorInformation.NumberOfPhysicalCores + $processor.NumberOfLogicalCores = $processorInformation.NumberOfLogicalCores + $processor.NumberOfProcessors = $processorInformation.NumberOfProcessors + $processor.MaxMegacyclesPerCore = $processorInformation.MaxMegacyclesPerCore + $processor.CurrentMegacyclesPerCore = $processorInformation.CurrentMegacyclesPerCore + $processor.ProcessorIsThrottled = $processorInformation.ProcessorIsThrottled + $processor.DifferentProcessorsDetected = $processorInformation.DifferentProcessorsDetected + $processor.DifferentProcessorCoreCountDetected = $processorInformation.DifferentProcessorCoreCountDetected + $processor.EnvironmentProcessorCount = $processorInformation.EnvironmentProcessorCount + $processor.ProcessorClassObject = $processorInformation.ProcessorClassObject + + $hardware_obj.Processor = $processor + $hardware_obj.Model = $system.Model + + Write-VerboseOutput("Exiting: Get-HardwareInformation") + return $hardware_obj +} + +Function Get-HttpProxySetting { + + $httpProxy32 = [String]::Empty + $httpProxy64 = [String]::Empty + Write-VerboseOutput("Calling: Get-HttpProxySetting") + + Function Get-WinHttpSettings { + param( + [Parameter(Mandatory=$true)][string]$RegistryLocation + ) + $connections = Get-ItemProperty -Path $RegistryLocation + $Proxy = [string]::Empty + if(($connections -ne $null) -and ($Connections | gm).Name -contains "WinHttpSettings") + { + foreach($Byte in $Connections.WinHttpSettings) + { + if($Byte -ge 48) + { + $Proxy += [CHAR]$Byte + } + } + } + return $(if($Proxy -eq [string]::Empty){""} else {$Proxy}) + } + + $httpProxy32 = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock ${Function:Get-WinHttpSettings} -ArgumentList "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" -ScriptBlockDescription "Getting 32 Http Proxy Value" -CatchActionFunction ${Function:Invoke-CatchActions} + $httpProxy64 = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock ${Function:Get-WinHttpSettings} -ArgumentList "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" -ScriptBlockDescription "Getting 64 Http Proxy Value" -CatchActionFunction ${Function:Invoke-CatchActions} + + Write-VerboseOutput("Http Proxy 32: {0}" -f $httpProxy32) + Write-VerboseOutput("Http Proxy 64: {0}" -f $httpProxy64) + Write-VerboseOutput("Exiting: Get-HttpProxySetting") + + if($httpProxy32 -ne "") + { + return $httpProxy32 + } + else + { + return $httpProxy64 + } +} + +Function Get-LmCompatibilityLevelInformation { + + Write-VerboseOutput("Calling: Get-LmCompatibilityLevelInformation") + + [HealthChecker.LmCompatibilityLevelInformation]$ServerLmCompatObject = New-Object -TypeName HealthChecker.LmCompatibilityLevelInformation + $ServerLmCompatObject.RegistryValue = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $Script:Server -SubKey "SYSTEM\CurrentControlSet\Control\Lsa" -GetValue "LmCompatibilityLevel" -CatchActionFunction ${Function:Invoke-CatchActions} -DefaultValue 3 + Switch ($ServerLmCompatObject.RegistryValue) + { + 0 {$ServerLmCompatObject.Description = "Clients use LM and NTLM authentication, but they never use NTLMv2 session security. Domain controllers accept LM, NTLM, and NTLMv2 authentication." } + 1 {$ServerLmCompatObject.Description = "Clients use LM and NTLM authentication, and they use NTLMv2 session security if the server supports it. Domain controllers accept LM, NTLM, and NTLMv2 authentication." } + 2 {$ServerLmCompatObject.Description = "Clients use only NTLM authentication, and they use NTLMv2 session security if the server supports it. Domain controller accepts LM, NTLM, and NTLMv2 authentication." } + 3 {$ServerLmCompatObject.Description = "Clients use only NTLMv2 authentication, and they use NTLMv2 session security if the server supports it. Domain controllers accept LM, NTLM, and NTLMv2 authentication." } + 4 {$ServerLmCompatObject.Description = "Clients use only NTLMv2 authentication, and they use NTLMv2 session security if the server supports it. Domain controller refuses LM authentication responses, but it accepts NTLM and NTLMv2." } + 5 {$ServerLmCompatObject.Description = "Clients use only NTLMv2 authentication, and they use NTLMv2 session security if the server supports it. Domain controller refuses LM and NTLM authentication responses, but it accepts NTLMv2." } + } + + Write-VerboseOutput("Exiting: Get-LmCompatibilityLevelInformation") + Return $ServerLmCompatObject +} + +Function Get-OperatingSystemInformation { + + Write-VerboseOutput("Calling: Get-OperatingSystemInformation") + + [HealthChecker.OperatingSystemInformation]$osInformation = New-Object HealthChecker.OperatingSystemInformation + $win32_OperatingSystem = Get-WmiObjectHandler -ComputerName $Script:Server -Class Win32_OperatingSystem -CatchActionFunction ${Function:Invoke-CatchActions} + $win32_PowerPlan = Get-WmiObjectHandler -ComputerName $Script:Server -Class Win32_PowerPlan -Namespace 'root\cimv2\power' -Filter "isActive='true'" -CatchActionFunction ${Function:Invoke-CatchActions} + $currentDateTime = Get-Date + $lastBootUpTime = [Management.ManagementDateTimeConverter]::ToDateTime($win32_OperatingSystem.lastbootuptime) + $osInformation.BuildInformation.VersionBuild = $win32_OperatingSystem.Version + $osInformation.BuildInformation.MajorVersion = (Get-ServerOperatingSystemVersion -OsCaption $win32_OperatingSystem.Caption) + $osInformation.BuildInformation.FriendlyName = $win32_OperatingSystem.Caption + $osInformation.BuildInformation.OperatingSystem = $win32_OperatingSystem + $osInformation.ServerBootUp.Days = ($currentDateTime - $lastBootUpTime).Days + $osInformation.ServerBootUp.Hours = ($currentDateTime - $lastBootUpTime).Hours + $osInformation.ServerBootUp.Minutes = ($currentDateTime - $lastBootUpTime).Minutes + $osInformation.ServerBootUp.Seconds = ($currentDateTime - $lastBootUpTime).Seconds + + if($win32_PowerPlan -ne $null) + { + if($win32_PowerPlan.InstanceID -eq "Microsoft:PowerPlan\{8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c}") + { + Write-VerboseOutput("High Performance Power Plan is set to true") + $osInformation.PowerPlan.HighPerformanceSet = $true + } + else{Write-VerboseOutput("High Performance Power Plan is NOT set to true")} + $osInformation.PowerPlan.PowerPlanSetting = $win32_PowerPlan.ElementName + } + else + { + Write-VerboseOutput("Power Plan Information could not be read") + $osInformation.PowerPlan.PowerPlanSetting = "N/A" + } + $osInformation.PowerPlan.PowerPlan = $win32_PowerPlan + $osInformation.PageFile = Get-PageFileInformation + $osInformation.NetworkInformation.NetworkAdapters = (Get-AllNicInformation -ComputerName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} -ComputerFQDN $Script:ServerFQDN) + foreach($adapter in $osInformation.NetworkInformation.NetworkAdapters) + { + if (!$adapter.IPv6Enabled) + { + $osInformation.NetworkInformation.IPv6DisabledOnNICs = $true + break + } + } + + $osInformation.NetworkInformation.IPv6DisabledComponents = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $Script:Server -SubKey "SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" -GetValue "DisabledComponents" -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.NetworkInformation.TCPKeepAlive = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $Script:Server -SubKey "SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" -GetValue "KeepAliveTime" -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.NetworkInformation.RpcMinConnectionTimeout = Invoke-RegistryGetValue -RegistryHive "LocalMachine" -MachineName $Script:Server -SubKey "Software\Policies\Microsoft\Windows NT\RPC\" -GetValue "MinimumConnectionTimeout" -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.NetworkInformation.HttpProxy = Get-HttpProxySetting + $osInformation.InstalledUpdates.HotFixes = (Get-HotFix -ComputerName $Script:Server -ErrorAction SilentlyContinue) #old school check still valid and faster and a failsafe + $osInformation.LmCompatibility = Get-LmCompatibilityLevelInformation + $counterSamples = (Get-CounterSamples -MachineNames $Script:Server -Counters "\Network Interface(*)\Packets Received Discarded") + if($counterSamples -ne $null) + { + $osInformation.NetworkInformation.PacketsReceivedDiscarded = $counterSamples + } + $osInformation.ServerPendingReboot = (Get-ServerRebootPending -ServerName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions}) + $timeZoneInformation = Get-TimeZoneInformationRegistrySettings -MachineName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.TimeZone.DynamicDaylightTimeDisabled = $timeZoneInformation.DynamicDaylightTimeDisabled + $osInformation.TimeZone.TimeZoneKeyName = $timeZoneInformation.TimeZoneKeyName + $osInformation.TimeZone.StandardStart = $timeZoneInformation.StandardStart + $osInformation.TimeZone.DaylightStart = $timeZoneInformation.DaylightStart + $osInformation.TimeZone.DstIssueDetected = $timeZoneInformation.DstIssueDetected + $osInformation.TimeZone.ActionsToTake = $timeZoneInformation.ActionsToTake + $osInformation.TimeZone.CurrentTimeZone = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock {([System.TimeZone]::CurrentTimeZone).StandardName} -ScriptBlockDescription "Getting Current Time Zone" -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.TLSSettings = Get-AllTlsSettingsFromRegistry -MachineName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.VcRedistributable = Get-VisualCRedistributableVersion + $osInformation.CredentialGuardEnabled = Get-CredentialGuardEnabled + $osInformation.RegistryValues.CurrentVersionUbr = Invoke-RegistryGetValue ` + -MachineName $Script:Server ` + -SubKey "SOFTWARE\Microsoft\Windows NT\CurrentVersion" ` + -GetValue "UBR" ` + -CatchActionFunction ${Function:Invoke-CatchActions} + + $osInformation.RegistryValues.LanManServerDisabledCompression = Invoke-RegistryGetValue ` + -MachineName $Script:Server ` + -SubKey "SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" ` + -GetValue "DisableCompression" ` + -CatchActionFunction ${Function:Invoke-CatchActions} + + $getSmb1ServerSettings = Get-Smb1ServerSettings -ServerName $Script:Server -CatchActionFunction ${Function:Invoke-CatchActions} + $osInformation.Smb1ServerSettings.SmbServerConfiguration = $getSmb1ServerSettings.SmbServerConfiguration + $osInformation.Smb1ServerSettings.WindowsFeature = $getSmb1ServerSettings.WindowsFeature + $osInformation.Smb1ServerSettings.Smb1Status = $getSmb1ServerSettings.Smb1Status + + Write-VerboseOutput("Exiting: Get-OperatingSystemInformation") + return $osInformation +} + +Function Get-PageFileInformation { + + Write-VerboseOutput("Calling: Get-PageFileInformation") + + [HealthChecker.PageFileInformation]$page_obj = New-Object HealthChecker.PageFileInformation + $pagefile = Get-WmiObjectHandler -ComputerName $Script:Server -Class "Win32_PageFileSetting" -CatchActionFunction ${Function:Invoke-CatchActions} + if($pagefile -ne $null) + { + if($pagefile.GetType().Name -eq "ManagementObject") + { + $page_obj.MaxPageSize = $pagefile.MaximumSize + } + $page_obj.PageFile = $pagefile + } + else + { + Write-VerboseOutput("Return Null value") + } + + Write-VerboseOutput("Exiting: Get-PageFileInformation") + return $page_obj +} + +Function Get-ServerRole { +param( +[Parameter(Mandatory=$true)][object]$ExchangeServerObj +) + Write-VerboseOutput("Calling: Get-ServerRole") + $roles = $ExchangeServerObj.ServerRole.ToString() + Write-VerboseOutput("Roll: " + $roles) + #Need to change this to like because of Exchange 2010 with AIO with the hub role. + if($roles -like "Mailbox, ClientAccess*") + { + return [HealthChecker.ExchangeServerRole]::MultiRole + } + elseif($roles -eq "Mailbox") + { + return [HealthChecker.ExchangeServerRole]::Mailbox + } + elseif($roles -eq "Edge") + { + return [HealthChecker.ExchangeServerRole]::Edge + } + elseif($roles -like "*ClientAccess*") + { + return [HealthChecker.ExchangeServerRole]::ClientAccess + } + else + { + return [HealthChecker.ExchangeServerRole]::None + } +} + +Function Get-VisualCRedistributableVersion { + + Write-VerboseOutput("Calling: Get-VisualCRedistributableVersion") + + $installedSoftware = Invoke-ScriptBlockHandler -ComputerName $Script:Server -ScriptBlock {Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*} -ScriptBlockDescription "Quering for software" -CatchActionFunction ${Function:Invoke-CatchActions} + $softwareInfos = @() + foreach ($software in $installedSoftware) + { + if($software.DisplayName -like "Microsoft Visual C++ *") + { + Write-VerboseOutput("Microsoft Visual C++ Redistributable found: {0}" -f $software.DisplayName) + [HealthChecker.SoftwareInformation]$softwareInfo = New-Object Healthchecker.SoftwareInformation + $softwareInfo.DisplayName = $software.DisplayName + $softwareInfo.DisplayVersion = $software.DisplayVersion + $softwareInfo.InstallDate = $software.InstallDate + $softwareInfo.VersionIdentifier = $software.Version + $softwareInfos += $softwareInfo + } + } + + Write-VerboseOutput("Exiting: Get-VisualCRedistributableVersion") + return $softwareInfos +} + +Function Add-AnalyzedResultInformation { +param( +[object]$Details, +[string]$Name, +[string]$HtmlName, +[object]$DisplayGroupingKey, +[int]$DisplayCustomTabNumber = -1, +[object]$DisplayTestingValue, +[string]$DisplayWriteType = "Grey", +[bool]$AddDisplayResultsLineInfo = $true, +[bool]$AddHtmlDetailRow = $true, +[string]$HtmlDetailsCustomValue = "", +[bool]$AddHtmlOverviewValues = $false, +[bool]$AddHtmlActionRow = $false, +[string]$ActionSettingClass = "", +[string]$ActionSettingValue, +[string]$ActionRecommendedDetailsClass = "", +[string]$ActionRecommendedDetailsValue, +[string]$ActionMoreInformationClass = "", +[string]$ActionMoreInformationValue, +[HealthChecker.AnalyzedInformation]$AnalyzedInformation +) + + Write-VerboseOutput("Calling Add-AnalyzedResultInformation: {0}" -f $name) + + if ($AddDisplayResultsLineInfo) + { + if (!($AnalyzedInformation.DisplayResults.ContainsKey($DisplayGroupingKey))) + { + Write-VerboseOutput("Adding Display Grouping Key: {0}" -f $DisplayGroupingKey.Name) + [System.Collections.Generic.List[HealthChecker.DisplayResultsLineInfo]]$list = New-Object System.Collections.Generic.List[HealthChecker.DisplayResultsLineInfo] + $AnalyzedInformation.DisplayResults.Add($DisplayGroupingKey, $list) + } + + $lineInfo = New-Object HealthChecker.DisplayResultsLineInfo + $lineInfo.DisplayValue = $Details + $lineInfo.Name = $Name + + if ($DisplayCustomTabNumber -ne -1) + { + $lineInfo.TabNumber = $DisplayCustomTabNumber + } + else + { + $lineInfo.TabNumber = $DisplayGroupingKey.DefaultTabNumber + } + + if ($DisplayTestingValue -ne $null) + { + $lineInfo.TestingValue = $DisplayTestingValue + } + else + { + $lineInfo.TestingValue = $Details + } + + $lineInfo.WriteType = $DisplayWriteType + $AnalyzedInformation.DisplayResults[$DisplayGroupingKey].Add($lineInfo) + } + + if ($AddHtmlDetailRow) + { + if (!($analyzedResults.HtmlServerValues.ContainsKey("ServerDetails"))) + { + [System.Collections.Generic.List[HealthChecker.HtmlServerInformationRow]]$list = New-Object System.Collections.Generic.List[HealthChecker.HtmlServerInformationRow] + $AnalyzedInformation.HtmlServerValues.Add("ServerDetails", $list) + } + + $detailRow = New-Object HealthChecker.HtmlServerInformationRow + + if ($displayWriteType -ne "Grey") + { + $detailRow.Class = $displayWriteType + } + + if ([string]::IsNullOrEmpty($HtmlName)) + { + $detailRow.Name = $Name + } + else + { + $detailRow.Name = $HtmlName + } + + if ([string]::IsNullOrEmpty($HtmlDetailsCustomValue)) + { + $detailRow.DetailValue = $Details + } + else + { + $detailRow.DetailValue = $HtmlDetailsCustomValue + } + + $AnalyzedInformation.HtmlServerValues["ServerDetails"].Add($detailRow) + } + + if ($AddHtmlOverviewValues) + { + if (!($analyzedResults.HtmlServerValues.ContainsKey("OverviewValues"))) + { + [System.Collections.Generic.List[HealthChecker.HtmlServerInformationRow]]$list = New-Object System.Collections.Generic.List[HealthChecker.HtmlServerInformationRow] + $AnalyzedInformation.HtmlServerValues.Add("OverviewValues", $list) + } + + $overviewValue = New-Object HealthChecker.HtmlServerInformationRow + + if ($displayWriteType -ne "Grey") + { + $overviewValue.Class = $displayWriteType + } + + if ([string]::IsNullOrEmpty($HtmlName)) + { + $overviewValue.Name = $Name + } + else + { + $overviewValue.Name = $HtmlName + } + + if ([string]::IsNullOrEmpty($HtmlDetailsCustomValue)) + { + $overviewValue.DetailValue = $Details + } + else + { + $overviewValue.DetailValue = $HtmlDetailsCustomValue + } + + $AnalyzedInformation.HtmlServerValues["OverviewValues"].Add($overviewValue) + } + + if ($AddHtmlActionRow) + { + #TODO + } + + return $AnalyzedInformation +} + +Function New-DisplayResultsGroupingKey { +param( +[string]$Name, +[bool]$DisplayGroupName = $true, +[int]$DisplayOrder, +[int]$DefaultTabNumber = 1 +) + $obj = New-Object HealthChecker.DisplayResultsGroupingKey + $obj.Name = $Name + $obj.DisplayGroupName = $DisplayGroupName + $obj.DisplayOrder = $DisplayOrder + $obj.DefaultTabNumber = $DefaultTabNumber + return $obj +} + +Function Start-AnalyzerEngine { +param( +[HealthChecker.HealthCheckerExchangeServer]$HealthServerObject +) + Write-VerboseOutput("Calling: Start-AnalyzerEngine") + + $analyzedResults = New-Object HealthChecker.AnalyzedInformation + $analyzedResults.HealthCheckerExchangeServer = $HealthServerObject + + #Display Grouping Keys + $order = 0 + $keyBeginningInfo = New-DisplayResultsGroupingKey -Name "BeginningInfo" -DisplayGroupName $false -DisplayOrder ($order++) -DefaultTabNumber 0 + $keyExchangeInformation = New-DisplayResultsGroupingKey -Name "Exchange Information" -DisplayOrder ($order++) + $keyOSInformation = New-DisplayResultsGroupingKey -Name "Operating System Information" -DisplayOrder ($order++) + $keyHardwareInformation = New-DisplayResultsGroupingKey -Name "Processor/Hardware Information" -DisplayOrder ($order++) + $keyNICSettings = New-DisplayResultsGroupingKey -Name "NIC Settings Per Active Adapter" -DisplayOrder ($order++) -DefaultTabNumber 2 + $keyFrequentConfigIssues = New-DisplayResultsGroupingKey -Name "Frequent Configuration Issues" -DisplayOrder ($order++) + $keySecuritySettings = New-DisplayResultsGroupingKey -Name "Security Settings" -DisplayOrder ($order++) + $keyWebApps = New-DisplayResultsGroupingKey -Name "Exchange Web App Pools" -DisplayOrder ($order++) + + #Set short cut variables + $exchangeInformation = $HealthServerObject.ExchangeInformation + $osInformation = $HealthServerObject.OSInformation + $hardwareInformation = $HealthServerObject.HardwareInformation + + if (!$Script:DisplayedScriptVersionAlready) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Exchange Health Checker Version" -Details $Script:healthCheckerVersion ` + -DisplayGroupingKey $keyBeginningInfo ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + if ($HealthServerObject.HardwareInformation.ServerType -eq [HealthChecker.ServerType]::VMWare -or + $HealthServerObject.HardwareInformation.ServerType -eq [HealthChecker.ServerType]::HyperV) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $VirtualizationWarning -DisplayWriteType "Yellow" ` + -DisplayGroupingKey $keyBeginningInfo ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + ######################### + # Exchange Information + ######################### + Write-VerboseOutput("Working on Exchange Information") + + $analyzedResults = Add-AnalyzedResultInformation -Name "Name" -Details ($HealthServerObject.ServerName) ` + -DisplayGroupingKey $keyExchangeInformation ` + -AddHtmlOverviewValues $true ` + -HtmlName "Server Name" ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Version" -Details ($exchangeInformation.BuildInformation.FriendlyName) ` + -DisplayGroupingKey $keyExchangeInformation ` + -AddHtmlOverviewValues $true ` + -HtmlName "Exchange Version" ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Build Number" -Details ($exchangeInformation.BuildInformation.BuildNumber) ` + -DisplayGroupingKey $keyExchangeInformation ` + -AnalyzedInformation $analyzedResults + + if ($exchangeInformation.BuildInformation.SupportedBuild -eq $false) + { + $daysOld = ($date - ([System.Convert]::ToDateTime([DateTime]$exchangeInformation.BuildInformation.ReleaseDate))).Days + + $analyzedResults = Add-AnalyzedResultInformation -Name "Error" -Details ("Out of date Cumulative Update. Please upgrade to one of the two most recently released Cumulative Updates. Currently running on a build that is {0} days old." -f $daysOld) ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayWriteType "Red" ` + -DisplayCustomTabNumber 2 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + if ($exchangeInformation.BuildInformation.KBsInstalled -ne $null) + { + $analyzedResults = Add-AnalyzedResultInformation -Details ("Exchange IU or Security Hotfix Detected.") ` + -DisplayGroupingKey $keyExchangeInformation ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + + foreach ($kb in $exchangeInformation.BuildInformation.KBsInstalled) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $kb ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Server Role" -Details ($exchangeInformation.BuildInformation.ServerRole) ` + -DisplayGroupingKey $keyExchangeInformation ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "MAPI/HTTP Enabled" -Details ($exchangeInformation.MapiHttpEnabled) ` + -DisplayGroupingKey $keyExchangeInformation ` + -AnalyzedInformation $analyzedResults + + if ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2013 -and + $exchangeInformation.BuildInformation.ServerRole -ne [HealthChecker.ExchangeServerRole]::Edge) + { + $content = [xml]$exchangeInformation.ApplicationPools["MSExchangeMapiFrontEndAppPool"].Content + [bool]$enabled = $content.Configuration.Runtime.gcServer.Enabled -eq "true" + [bool]$unknown = $content.Configuration.Runtime.gcServer.Enabled -ne "true" -and $content.Configuration.Runtime.gcServer.Enabled -ne "false" + $warning = [string]::Empty + $displayWriteType = "Green" + $displayValue = "Server" + + if ($hardwareInformation.TotalMemory -ge 21474836480 -and + $enabled -eq $false) + { + $displayWriteType = "Red" + $displayValue = "Workstation --- Error" + $warning = "To Fix this issue go into the file MSExchangeMapiFrontEndAppPool_CLRConfig.config in the Exchange Bin directory and change the GCServer to true and recycle the MAPI Front End App Pool" + } + elseif ($unknown) + { + $displayValue = "Unknown --- Warning" + $displayWriteType = "Yellow" + } + elseif (!($enabled)) + { + $displayWriteType = "Yellow" + $displayValue = "Workstation --- Warning" + $warning = "You could be seeing some GC issues within the Mapi Front End App Pool. However, you don't have enough memory installed on the system to recommend switching the GC mode by default without consulting a support professional." + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "MAPI Front End App Pool GC Mode" -Details $displayValue ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType $displayWriteType ` + -AnalyzedInformation $analyzedResults + + if ($warning -ne [string]::Empty) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $warning ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + + ############################## + # Exchange Server Maintenance + ############################## + Write-VerboseOutput("Working on Exchange Server Maintenance") + $serverMaintenance = $exchangeInformation.ServerMaintenance + + if (($serverMaintenance.InactiveComponents).Count -eq 0 -and + ($serverMaintenance.GetClusterNode -eq $null -or + $serverMaintenance.GetClusterNode.State -eq "Up") -and + ($serverMaintenance.GetMailboxServer -eq $null -or + ($serverMaintenance.GetMailboxServer.DatabaseCopyActivationDisabledAndMoveNow -eq $false -and + $serverMaintenance.GetMailboxServer.DatabaseCopyAutoActivationPolicy -eq "Unrestricted"))) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Exchange Server Maintenance" -Details "Server is not in Maintenance Mode" ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayWriteType "Green" ` + -AnalyzedInformation $analyzedResults + } + else + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Exchange Server Maintenance" ` + -DisplayGroupingKey $keyExchangeInformation ` + -AnalyzedInformation $analyzedResults + + if (($serverMaintenance.InactiveComponents).Count -ne 0) + { + foreach ($inactiveComponent in $serverMaintenance.InactiveComponents) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Component" -Details $inactiveComponent ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + } + } + + if ($serverMaintenance.GetMailboxServer.DatabaseCopyActivationDisabledAndMoveNow -or + $serverMaintenance.GetMailboxServer.DatabaseCopyAutoActivationPolicy -eq "Blocked") + { + $displayValue = "`r`n`t`tDatabaseCopyActivationDisabledAndMoveNow: {0} --- should be 'false'`r`n`t`tDatabaseCopyAutoActivationPolicy: {1} --- should be 'unrestricted'" -f ` + $serverMaintenance.GetMailboxServer.DatabaseCopyActivationDisabledAndMoveNow, + $serverMaintenance.GetMailboxServer.DatabaseCopyAutoActivationPolicy + + $analyzedResults = Add-AnalyzedResultInformation -Name "Database Copy Maintenance" -Details $displayValue ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + } + + if ($serverMaintenance.GetClusterNode -ne $null -and $serverMaintenance.GetClusterNode.State -ne "Up") + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Cluster Node" -Details ("'{0}' --- should be 'Up'" -f $serverMaintenance.GetClusterNode.State) ` + -DisplayGroupingKey $keyExchangeInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + } + } + + ######################### + # Operating System + ######################### + Write-VerboseOutput("Working on Operating System") + + $analyzedResults = Add-AnalyzedResultInformation -Name "Version" -Details ($osInformation.BuildInformation.FriendlyName) ` + -DisplayGroupingKey $keyOSInformation ` + -AddHtmlOverviewValues $true ` + -HtmlName "OS Version" ` + -AnalyzedInformation $analyzedResults + + $upTime = "{0} day(s) {1} hour(s) {2} minute(s) {3} second(s)" -f $osInformation.ServerBootUp.Days, + $osInformation.ServerBootUp.Hours, + $osInformation.ServerBootUp.Minutes, + $osInformation.ServerBootUp.Seconds + + $analyzedResults = Add-AnalyzedResultInformation -Name "System Up Time" -Details $upTime ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayTestingValue ($osInformation.ServerBootUp) ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Time Zone" -Details ($osInformation.TimeZone.CurrentTimeZone) ` + -DisplayGroupingKey $keyOSInformation ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + + $writeValue = $false + $warning = @("Windows can not properly detect any DST rule changes in your time zone. Set 'Adjust for daylight saving time automatically to on'") + + if ($osInformation.TimeZone.DstIssueDetected) + { + $writeType = "Red" + } + elseif ($osInformation.TimeZone.DynamicDaylightTimeDisabled -ne 0) + { + $writeType = "Yellow" + } + else + { + $warning = [string]::Empty + $writeValue = $true + $writeType = "Grey" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Dynamic Daylight Time Enabled" -Details $writeValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType $writeType ` + -AnalyzedInformation $analyzedResults + + if ($warning -ne [string]::Empty) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $warning ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Yellow" ` + -DisplayCustomTabNumber 2 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + if ([string]::IsNullOrEmpty($osInformation.TimeZone.TimeZoneKeyName)) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Time Zone Key Name" -Details "Empty --- Warning Need to switch your current time zone to a different value, then switch it back to have this value populated again." ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + } + + if ($exchangeInformation.NETFramework.OnRecommendedVersion) + { + $analyzedResults = Add-AnalyzedResultInformation -Name ".NET Framework" -Details ($osInformation.NETFramework.FriendlyName) ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Green" ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + } + else + { + $testObject = New-Object PSCustomObject + $testObject | Add-Member -MemberType NoteProperty -Name "CurrentValue" -Value ($osInformation.NETFramework.FriendlyName) + $testObject | Add-Member -MemberType NoteProperty -Name "MaxSupportedVersion" -Value ($exchangeInformation.NETFramework.MaxSupportedVersion) + $displayFriendly = Get-NETFrameworkVersion -NetVersionKey $exchangeInformation.NETFramework.MaxSupportedVersion + $displayValue = "{0} - Warning Recommended .NET Version is {1}" -f $osInformation.NETFramework.FriendlyName, $displayFriendly.FriendlyName + $analyzedResults = Add-AnalyzedResultInformation -Name ".NET Framework" -Details $displayValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Yellow" ` + -DisplayTestingValue $testObject ` + -HtmlDetailsCustomValue ($osInformation.NETFramework.FriendlyName) ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + } + + $displayValue = [string]::Empty + $displayWriteType = "Yellow" + Write-VerboseOutput("Total Memory: {0}" -f ($totalPhysicalMemory = $hardwareInformation.TotalMemory)) + Write-VerboseOutput("Page File: {0}" -f ($maxPageSize = $osInformation.PageFile.MaxPageSize)) + $testingValue = New-Object PSCustomObject + $testingValue | Add-Member -MemberType NoteProperty -Name "TotalPhysicalMemory" -Value $totalPhysicalMemory + $testingValue | Add-Member -MemberType NoteProperty -Name "MaxPageSize" -Value $maxPageSize + $testingValue | Add-Member -MemberType NoteProperty -Name "MultiPageFile" -Value ($osInformation.PageFile.PageFile.Count -gt 1) + $testingValue | Add-Member -MemberType NoteProperty -Name "RecommendedPageFile" -Value 0 + if ($maxPageSize -eq 0) + { + $displayValue = "Error: System is set to automatically manage the pagefile size." + $displayWriteType = "Red" + } + elseif ($osInformation.PageFile.PageFile.Count -gt 1) + { + $displayValue = "Multiple page files detected. `r`n`t`tError: This has been know to cause performance issues please address this." + $displayWriteType = "Red" + } + elseif ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2019) + { + $testingValue.RecommendedPageFile = ($recommendedPageFileSize = [Math]::Truncate(($totalPhysicalMemory / 1MB) / 4)) + Write-VerboseOutput("Recommended Page File Size: {0}" -f $recommendedPageFileSize) + if ($recommendedPageFileSize -ne $maxPageSize) + { + $displayValue = "{0}MB `r`n`t`tWarning: Page File is not set to 25% of the Total System Memory which is {1}MB. Recommended is {2}MB" -f $maxPageSize, ([Math]::Truncate($totalPhysicalMemory / 1MB)), $recommendedPageFileSize + } + else + { + $displayValue = "{0}MB" -f $recommendedPageFileSize + $displayWriteType = "Grey" + } + } + #32GB = 1024 * 1024 * 1024 * 32 = 34,359,738,368 + elseif ($totalPhysicalMemory -ge 34359738368) + { + if ($maxPageSize -eq 32778) + { + $displayValue = "{0}MB" -f $maxPageSize + $displayWriteType = "Grey" + } + else + { + $displayValue = "{0}MB `r`n`t`tWarning: Pagefile should be capped at 32778MB for 32GB plus 10MB - Article: https://docs.microsoft.com/en-us/exchange/exchange-2013-sizing-and-configuration-recommendations-exchange-2013-help#pagefile" -f $maxPageSize + } + } + else + { + $testingValue.RecommendedPageFile = ($recommendedPageFileSize = [Math]::Round(($totalPhysicalMemory / 1MB) + 10)) + if ($recommendedPageFileSize -ne $maxPageSize) + { + $displayValue = "{0}MB `r`n`t`tWarning: Page File is not set to Total System Memory plus 10MB which should be {1}MB" -f $maxPageSize, $recommendedPageFileSize + } + else + { + $displayValue = "{0}MB" -f $maxPageSize + $displayWriteType = "Grey" + } + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Page File Size" -Details $displayValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue $testingValue ` + -AnalyzedInformation $analyzedResults + + if ($osInformation.PowerPlan.HighPerformanceSet) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Power Plan" -Details ($osInformation.PowerPlan.PowerPlanSetting) ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Green" ` + -AnalyzedInformation $analyzedResults + } + else + { + $displayValue = "{0} --- Error" -f $osInformation.PowerPlan.PowerPlanSetting + $analyzedResults = Add-AnalyzedResultInformation -Name "Power Plan" -Details $displayValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + } + + if ($osInformation.NetworkInformation.HttpProxy -eq "") + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Http Proxy Setting" -Details ($osInformation.NetworkInformation.HttpProxy) ` + -DisplayGroupingKey $keyOSInformation ` + -HtmlDetailsCustomValue "None" ` + -AnalyzedInformation $analyzedResults + } + else + { + $displayValue = "{0} --- Warning this can cause client connectivity issues." -f $osInformation.NetworkInformation.HttpProxy + $analyzedResults = Add-AnalyzedResultInformation -Name "Http Proxy Setting" -Details $displayValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType "Yellow" ` + -DisplayTestingValue ($osInformation.NetworkInformation.HttpProxy) ` + -AnalyzedInformation $analyzedResults + } + + $displayWriteType2012 = "Yellow" + $displayWriteType2013 = "Yellow" + $displayValue2012 = "Unknown" + $displayValue2013 = "Unknown" + + if ($osInformation.VcRedistributable -ne $null) + { + Write-VerboseOutput("VCRedist2012 Testing value: {0}" -f [HealthChecker.VCRedistVersion]::VCRedist2012.value__) + Write-VerboseOutput("VCRedist2013 Testing value: {0}" -f [HealthChecker.VCRedistVersion]::VCRedist2013.value__) + $vc2013Required = $exchangeInformation.BuildInformation.ServerRole -ne [HealthChecker.ExchangeServerRole]::Edge + $displayValue2012 = "Redistributable is outdated" + $displayValue2013 = "Redistributable is outdated" + + foreach ($detectedVisualRedistVersion in $osInformation.VcRedistributable) + { + Write-VerboseOutput("Testing {0} version id '{1}'" -f $detectedVisualRedistVersion.DisplayName, $detectedVisualRedistVersion.VersionIdentifier) + + if ($detectedVisualRedistVersion.VersionIdentifier -eq [HealthChecker.VCRedistVersion]::VCRedist2012) + { + $displayValue2012 = "{0} Version is current" -f $detectedVisualRedistVersion.DisplayVersion + $displayWriteType2012 = "Green" + } + elseif ($vc2013Required -and + $detectedVisualRedistVersion.VersionIdentifier -eq [HealthChecker.VCRedistVersion]::VCRedist2013) + { + $displayWriteType2013 = "Green" + $displayValue2013 = "{0} Version is current" -f $detectedVisualRedistVersion.DisplayVersion + } + } + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Visual C++ 2012" -Details $displayValue2012 ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType $displayWriteType2012 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Visual C++ 2013" -Details $displayValue2013 ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType $displayWriteType2013 ` + -AnalyzedInformation $analyzedResults + + if ($osInformation.VcRedistributable -ne $null -and + ($displayWriteType2012 -eq "Yellow" -or + $displayWriteType2013 -eq "Yellow")) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Note: For more information about the latest C++ Redistributeable please visit: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads`r`n`t`tThis is not a requirement to upgrade, only a notification to bring to your attention." ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + } + + $displayValue = "False" + $writeType = "Grey" + + if ($osInformation.ServerPendingReboot) + { + $displayValue = "True --- Warning a reboot is pending and can cause issues on the server." + $writeType = "Yellow" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Server Pending Reboot" -Details $displayValue ` + -DisplayGroupingKey $keyOSInformation ` + -DisplayWriteType $writeType ` + -DisplayTestingValue ($osInformation.ServerPendingReboot) ` + -AnalyzedInformation $analyzedResults + + ################################ + # Processor/Hardware Information + ################################ + Write-VerboseOutput("Working on Processor/Hardware Information") + + $analyzedResults = Add-AnalyzedResultInformation -Name "Type" -Details ($hardwareInformation.ServerType) ` + -DisplayGroupingKey $keyHardwareInformation ` + -AddHtmlOverviewValues $true ` + -Htmlname "Hardware Type" ` + -AnalyzedInformation $analyzedResults + + if ($hardwareInformation.ServerType -eq [HealthChecker.ServerType]::Physical -or + $hardwareInformation.ServerType -eq [HealthChecker.ServerType]::AmazonEC2) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Manufacturer" -Details ($hardwareInformation.Manufacturer) ` + -DisplayGroupingKey $keyHardwareInformation ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Model" -Details ($hardwareInformation.Model) ` + -DisplayGroupingKey $keyHardwareInformation ` + -AnalyzedInformation $analyzedResults + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Processor" -Details ($hardwareInformation.Processor.Name) ` + -DisplayGroupingKey $keyHardwareInformation ` + -AnalyzedInformation $analyzedResults + + $value = $hardwareInformation.Processor.NumberOfProcessors + $processorName = "Number of Processors" + + if ($hardwareInformation.ServerType -ne [HealthChecker.ServerType]::Physical) + { + $analyzedResults = Add-AnalyzedResultInformation -Name $processorName -Details $value ` + -DisplayGroupingKey $keyHardwareInformation ` + -AnalyzedInformation $analyzedResults + + if ($hardwareInformation.ServerType -eq [HealthChecker.ServerType]::VMWare) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Note: Please make sure you are following VMware's performance recommendation to get the most out of your guest machine. VMware blog 'Does corespersocket Affect Performance?' https://blogs.vmware.com/vsphere/2013/10/does-corespersocket-affect-performance.html" ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + } + } + elseif ($value -gt 2) + { + $analyzedResults = Add-AnalyzedResultInformation -Name $processorName -Details ("{0} - Error: Recommended to only have 2 Processors" -f $value) ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Red" ` + -DisplayTestingValue $value ` + -HtmlDetailsCustomValue $value ` + -AnalyzedInformation $analyzedResults + } + else + { + $analyzedResults = Add-AnalyzedResultInformation -Name $processorName -Details $value ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Green" ` + -AnalyzedInformation $analyzedResults + } + + $physicalValue = $hardwareInformation.Processor.NumberOfPhysicalCores + $logicalValue = $hardwareInformation.Processor.NumberOfLogicalCores + + $displayWriteType = "Green" + + if (($logicalValue -gt 24 -and + $exchangeInformation.BuildInformation.MajorVersion -lt [HealthChecker.ExchangeMajorVersion]::Exchange2019) -or + $logicalValue -gt 48) + { + $displayWriteType = "Yellow" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Number of Physical Cores" -Details $physicalValue ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType $displayWriteType ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Number of Logical Cores" -Details $logicalValue ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType $displayWriteType ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + + if ($logicalValue -gt $physicalValue) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Hyper-Threading" -Details "Enabled --- Error: Having Hyper-Threading enabled goes against best practices and can cause performance issues. Please disable as soon as possible." ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Red" ` + -DisplayTestingValue $true ` + -AnalyzedInformation $analyzedResults + + if ($hardwareInformation.ServerType -eq [HealthChecker.ServerType]::AmazonEC2) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Error: For high-performance computing (HPC) application, like Exchange, Amazon recommends that you have Hyper-Threading Technology disabled in their service. More informaiton: https://aws.amazon.com/blogs/compute/disabling-intel-hyper-threading-technology-on-amazon-ec2-windows-instances/" ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Red" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + if ($hardwareInformation.Processor.Name.StartsWith("AMD")) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "This script may incorrectly report that Hyper-Threading is enabled on certain AMD processors. Check with the manufacturer to see if your mondel supports SMT." ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + else + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Hyper-Threading" -Details "Disabled" ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Green" ` + -DisplayTestingValue $false ` + -AnalyzedInformation $analyzedResults + } + + #NUMA BIOS CHECK - AKA check to see if we can properly see all of our cores on the box + $displayWriteType = "Yellow" + $testingValue = "Unknown" + $displayValue = [string]::Empty + if ($hardwareInformation.Model.Contains("ProLiant")) + { + $name = "NUMA Group Size Optimization" + if ($hardwareInformation.Processor.EnvironmentProcessorCount -eq -1) + { + $displayValue = "Unknown `r`n`t`tWarning: If this is set to Clustered, this can cause multiple types of issues on the server" + } + elseif ($hardwareInformation.Processor.EnvironmentProcessorCount -ne $logicalValue) + { + $displayValue = "Clustered `r`n`t`tError: This setting should be set to Flat. By having this set to Clustered, we will see multiple different types of issues." + $testingValue = "Clustered" + $displayWriteType = "Red" + } + else + { + $displayValue = "Flat" + $testingValue = "Flat" + $displayWriteType = "Green" + } + } + else + { + $name = "All Processor Cores Visible" + if ($hardwareInformation.Processor.EnvironmentProcessorCount -eq -1) + { + $displayValue = "Unknown `r`n`t`tWarning: If we aren't able to see all processor cores from Exchange, we could see performance related issues." + } + elseif ($hardwareInformation.Processor.EnvironmentProcessorCount -ne $logicalValue) + { + $displayValue = "Failed `r`n`t`tError: Not all Processor Cores are visible to Exchange and this will cause a performance impact" + $displayWriteType = "Red" + $testingValue = "Failed" + } + else + { + $displayWriteType = "Green" + $displayValue = "Passed" + $testingValue = "Passed" + } + } + + $analyzedResults = Add-AnalyzedResultInformation -Name $name -Details $displayValue ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue $testingValue ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Max Processor Speed" -Details ($hardwareInformation.Processor.MaxMegacyclesPerCore) ` + -DisplayGroupingKey $keyHardwareInformation ` + -AnalyzedInformation $analyzedResults + + if ($hardwareInformation.Processor.ProcessorIsThrottled) + { + $currentSpeed = $hardwareInformation.Processor.CurrentMegacyclesPerCore + $analyzedResults = Add-AnalyzedResultInformation -Name "Current Processor Speed" -Details ("{0} --- Error: Processor appears to be throttled." -f $currentSpeed) ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Red" ` + -DisplayTestingValue $currentSpeed ` + -AnalyzedInformation $analyzedResults + + $displayValue = "Error: Power Plan is NOT set to `"High Performance`". This change doesn't require a reboot and takes affect right away. Re-run script after doing so" + + if ($osInformation.PowerPlan.HighPerformanceSet) + { + $displayValue = "Error: Power Plan is set to `"High Performance`", so it is likely that we are throttling in the BIOS of the computer settings." + } + + $analyzedResults = Add-AnalyzedResultInformation -Details $displayValue ` + -DisplayGroupingKey $keyHardwareInformation ` + -DisplayWriteType "Red" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + $totalPhysicalMemory = [System.Math]::Round($hardwareInformation.TotalMemory / 1024 / 1024 / 1024) + $displayWriteType = "Yellow" + $displayDetails = [string]::Empty + + if ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2019) + { + if ($totalPhysicalMemory -gt 256) + { + $displayDetails = "{0} GB `r`n`t`tWarning: We recommend for the best performance to be scaled at or below 256 GB of Memory" -f $totalPhysicalMemory + } + elseif ($totalPhysicalMemory -lt 64 -and + $exchangeInformation.BuildInformation.ServerRole -eq [HealthChecker.ExchangeServerRole]::Edge) + { + $displayDetails = "{0} GB `r`n`t`tWarning: We recommend for the best performance to have a minimum of 64GB of RAM installed on the machine." -f $totalPhysicalMemory + } + elseif ($totalPhysicalMemory -lt 128) + { + $displayDetails = "{0} GB `r`n`t`tWarning: We recommend for the best performance to have a minimum of 128GB of RAM installed on the machine." -f $totalPhysicalMemory + } + else + { + $displayDetails = "{0} GB" -f $totalPhysicalMemory + $displayWriteType = "Grey" + } + } + elseif ($totalPhysicalMemory -gt 192 -and + $exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2016) + { + $displayDetails = "{0} GB `r`n`t`tWarning: We recommend for the best performance to be scaled at or below 192 GB of Memory." -f $totalPhysicalMemory + } + elseif ($totalPhysicalMemory -gt 96 -and + $exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2013) + { + $displayDetails = "{0} GB `r`n`t`tWarning: We recommend for the best performance to be scaled at or below 96GB of Memory." -f $totalPhysicalMemory + } + else + { + $displayDetails = "{0} GB" -f $totalPhysicalMemory + $displayWriteType = "Grey" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Physical Memory" -Details $displayDetails ` + -DisplayGroupingKey $keyHardwareInformation ` + -DipslayTestingValue $totalPhysicalMemory ` + -DisplayWriteType $displayWriteType ` + -AddHtmlOverviewValues $true ` + -AnalyzedInformation $analyzedResults + + ################################ + #NIC Settings Per Active Adapter + ################################ + Write-VerboseOutput("Working on NIC Settings Per Active Adapter Information") + + foreach ($adapter in $osInformation.NetworkInformation.NetworkAdapters) + { + if ($adapter.Description -eq "Remote NDIS Compatible Device") + { + Write-VerboseOutput("Remote NDSI Compatible Device found. Ignoring NIC.") + continue + } + + $value = "{0} [{1}]" -f $adapter.Description, $adapter.Name + $analyzedResults = Add-AnalyzedResultInformation -Name "Interface Description" -Details $value ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayCustomTabNumber 1 ` + -AnalyzedInformation $analyzedResults + + if ($osInformation.BuildInformation.MajorVersion -ge [HealthChecker.OSServerVersion]::Windows2012R2) + { + Write-VerboseOutput("On Windows 2012 R2 or new. Can provide more details on the NICs") + + $driverDate = $adapter.DriverDate + $detailsValue = $driverDate + + if ($hardwareInformation.ServerType -eq [HealthChecker.ServerType]::Physical -or + $hardwareInformation.ServerType -eq [HealthChecker.ServerType]::AmazonEC2) + { + if ($driverDate -eq $null -or + $driverDate -eq [DateTime]::MaxValue) + { + $detailsValue = "Unknown" + } + elseif ((New-TimeSpan -Start $date -End $driverDate).Days -lt [int]-365) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Warning: NIC driver is over 1 year old. Verify you are at the latest version." ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType "Yellow" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Driver Date" -Details $detailsValue ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Driver Version" -Details ($adapter.DriverVersion) ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "MTU Size" -Details ($adapter.MTUSize) ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + $writeType = "Yellow" + $testingValue = $null + + if ($adapter.RssEnabledValue -eq 0) + { + $detailsValue = "False --- Warning: Enabling RSS is recommended." + $testingValue = $false + } + elseif ($adapter.RssEnabledValue -eq 1) + { + $detailsValue = "True" + $testingValue = $true + $writeType = "Green" + } + else + { + $detailsValue = "No RSS Feature Detected." + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "RSS Enabled" -Details $detailsValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType $writeType ` + -DisplayTestingValue $testingValue ` + -AnalyzedInformation $analyzedResults + } + else + { + Write-VerboseOutput("On Windows 2012 or older and can't get advanced NIC settings") + } + + $linkSpeed = $adapter.LinkSpeed + $displayValue = "{0} --- This may not be accurate due to virtualized hardware" -f $linkSpeed + + if ($hardwareInformation.ServerType -eq [HealthChecker.ServerType]::Physical -or + $hardwareInformation.ServerType -eq [HealthChecker.ServerType]::AmazonEC2) + { + $displayValue = $linkSpeed + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Link Speed" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayTestingValue $linkSpeed ` + -AnalyzedInformation $analyzedResults + + $displayValue = "{0}" -f $adapter.IPv6Enabled + $displayWriteType = "Grey" + $testingValue = $adapter.IPv6Enabled + + if ($osInformation.NetworkInformation.IPv6DisabledComponents -ne 255 -and + $adapter.IPv6Enabled -eq $false) + { + $displayValue = "{0} --- Warning" -f $adapter.IPv6Enabled + $displayWriteType = "Yellow" + $testingValue = $false + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "IPv6 Enabled" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue $TestingValue ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "IPv4 Address" ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + foreach ($address in $adapter.IPv4Addresses) + { + $displayValue = "{0}\{1}" -f $address.Address, $address.Subnet + + if ($address.DefaultGateway -ne [string]::Empty) + { + $displayValue += " Gateway: {0}" -f $address.DefaultGateway + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Address" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayCustomTabNumber 3 ` + -AnalyzedInformation $analyzedResults + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "IPv6 Address" ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + foreach ($address in $adapter.IPv6Addresses) + { + $displayValue = "{0}\{1}" -f $address.Address, $address.Subnet + + if ($address.DefaultGateway -ne [string]::Empty) + { + $displayValue += " Gateway: {0}" -f $address.DefaultGateway + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Address" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayCustomTabNumber 3 ` + -AnalyzedInformation $analyzedResults + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "DNS Server" -Details $adapter.DnsServer ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Registered In DNS" -Details $adapter.RegisteredInDns ` + -DisplayGroupingKey $keyNICSettings ` + -AnalyzedInformation $analyzedResults + + #Assuming that all versions of Hyper-V doesn't allow sleepy NICs + if (($hardwareInformation.ServerType -ne [HealthChecker.ServerType]::HyperV) -and ($adapter.PnPCapabilities -ne "MultiplexorNoPnP")) + { + $displayWriteType = "Grey" + $displayValue = $adapter.SleepyNicDisabled + + if (!$adapter.SleepyNicDisabled) + { + $displayWriteType = "Yellow" + $displayValue = "False --- Warning: It's recommended to disable NIC power saving options`r`n`t`t`tMore Information: http://support.microsoft.com/kb/2740020" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Sleepy NIC Disabled" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue $adapter.SleepyNicDisabled ` + -AnalyzedInformation $analyzedResults + } + + $adapterDescription = $adapter.Description + $cookedValue = 0 + $foundCounter = $false + + if ($osInformation.NetworkInformation.PacketsReceivedDiscarded -eq $null) + { + Write-VerboseOutput("PacketsReceivedDiscarded is null") + continue + } + + foreach ($prdInstance in $osInformation.NetworkInformation.PacketsReceivedDiscarded) + { + $instancePath = $prdInstance.Path + $startIndex = $instancePath.IndexOf("(") + 1 + $charLength = $instancePath.Substring($startIndex, ($instancePath.IndexOf(")") - $startIndex)).Length + $instanceName = $instancePath.Substring($startIndex, $charLength) + $possibleInstanceName = $adapterDescription.Replace("#","_") + + if ($instanceName -eq $adapterDescription -or + $instanceName -eq $possibleInstanceName) + { + $cookedValue = $prdInstance.CookedValue + $foundCounter = $true + break + } + } + + $displayWriteType = "Yellow" + $displayValue = $cookedValue + $baseDisplayValue = "{0} --- {1}: This value should be at 0." + $knownIssue = $false + + if ($foundCounter) + { + if ($cookedValue -eq 0) + { + $displayWriteType = "Green" + } + elseif ($cookedValue -lt 1000) + { + $displayValue = $baseDisplayValue -f $cookedValue, "Warning" + } + else + { + $displayWriteType = "Red" + $displayValue = [string]::Concat(($baseDisplayValue -f $cookedValue, "Error"), "We are also seeing this value being rather high so this can cause a performance impacted on a system.") + } + + if ($adapterDescription -like "*vmxnet3*" -and + $cookedValue -gt 0) + { + $knownIssue = $true + } + } + else + { + $displayValue = "Couldn't find value for the counter." + $cookedValue = $null + $displayWriteType = "Grey" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Packets Received Discarded" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayTestingValue $cookedValue ` + -DisplayWriteType $displayWriteType ` + -AnalyzedInformation $analyzedResults + + if ($knownIssue) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Known Issue with vmxnet3: 'Large packet loss at the guest operating system level on the VMXNET3 vNIC in ESXi (2039495)' - https://kb.vmware.com/s/article/2039495" ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType "Yellow" ` + -DisplayCustomTabNumber 3 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + + if ($osInformation.NetworkInformation.NetworkAdapters.Count -gt 1) + { + $analyzedResults = Add-AnalyzedResultInformation -Details "Multiple active network adapters detected. Exchange 2013 or greater may not need separate adapters for MAPI and replication traffic. For details please refer to https://docs.microsoft.com/en-us/exchange/planning-for-high-availability-and-site-resilience-exchange-2013-help#NR" ` + -DisplayGroupingKey $keyNICSettings ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + if ($osInformation.NetworkInformation.IPv6DisabledOnNICs) + { + $displayWriteType = "Grey" + $displayValue = "True" + $testingValue = $true + if ($osInformation.NetworkInformation.IPv6DisabledComponents -ne 255) + { + $displayWriteType = "Red" + $testingValue = $false + $displayValue = "False `r`n`t`tError: IPv6 is disabled on some NIC level settings but not fully disabled. DisabledComponents registry key currently set to '{0}'. For details please refer to the following articles: `r`n`t`thttps://docs.microsoft.com/en-us/archive/blogs/rmilne/disabling-ipv6-and-exchange-going-all-the-way `r`n`t`thttps://support.microsoft.com/en-us/help/929852/guidance-for-configuring-ipv6-in-windows-for-advanced-users" -f $osInformation.NetworkInformation.DisabledComponents + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Disable IPv6 Correctly" -Details $displayValue ` + -DisplayGroupingKey $keyNICSettings ` + -DisplayWriteType $displayWriteType ` + -DisplayCustomTabNumber 1 ` + -AnalyzedInformation $analyzedResults + } + + ################ + #TCP/IP Settings + ################ + Write-VerboseOutput("Working on TCP/IP Settings") + + $tcpKeepAlive = $osInformation.NetworkInformation.TCPKeepAlive + + if ($tcpKeepAlive -eq 0) + { + $displayValue = "Not Set `r`n`t`tError: Without this value the KeepAliveTime defaults to two hours, which can cause connectivity and performance issues between network devices such as firewalls and load balancers depending on their configuration. `r`n`t`tMore details: https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Checklist-for-troubleshooting-Outlook-connectivity-in-Exchange/ba-p/604792" + $displayWriteType = "Red" + } + elseif ($tcpKeepAlive -lt 900000 -or + $tcpKeepAlive -gt 1800000) + { + $displayValue = "{0} `r`n`t`tWarning: Not configured optimally, recommended value between 15 to 30 minutes (900000 and 1800000 decimal). `r`n`t`tMore details: https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Checklist-for-troubleshooting-Outlook-connectivity-in-Exchange/ba-p/604792" -f $tcpKeepAlive + $displayWriteType = "Yellow" + } + else + { + $displayValue = $tcpKeepAlive + $displayWriteType = "Green" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "TCP/IP Settings" -Details $displayValue ` + -DisplayGroupingKey $keyFrequentConfigIssues ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue $tcpKeepAlive ` + -HtmlName "TCPKeepAlive" ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "RPC Min Connection Timeout" -Details ("{0} `r`n`t`tMore Information: https://blogs.technet.microsoft.com/messaging_with_communications/2012/06/06/outlook-anywhere-network-timeout-issue/" -f $osInformation.NetworkInformation.RpcMinConnectionTimeout) ` + -DisplayGroupingKey $keyFrequentConfigIssues ` + -HtmlName "RPC Minimum Connection Timeout" ` + -AnalyzedInformation $analyzedResults + + $displayValue = $exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage + $displayWriteType = "Green" + + if ($exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage -ne 0) + { + $displayWriteType = "Red" + $displayValue = "{0} `r`n`t`tError: This can cause an impact to the server's search performance. This should only be used a temporary fix if no other options are available vs a long term solution." -f $exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "CTS Processor Affinity Percentage" -Details $displayValue ` + -DisplayGroupingKey $keyFrequentConfigIssues ` + -DisplayWriteType $displayWriteType ` + -DisplayTestingValue ($exchangeInformation.RegistryValues.CtsProcessorAffinityPercentage) ` + -HtmlName "CtsProcessorAffinityPercentage" ` + -AnalyzedInformation $analyzedResults + + $displayValue = $osInformation.CredentialGuardEnabled + $displayWriteType = "Grey" + + if($osInformation.CredentialGuardEnabled) + { + $displayValue = "{0} `r`n`t`tError: Credential Guard is not supported on an Exchange Server. This can cause a performance hit on the server." -f $osInformation.CredentialGuardEnabled + $displayWriteType = "Red" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Credential Guard Enabled" -Details $displayValue ` + -DisplayGroupingKey $keyFrequentConfigIssues ` + -DisplayWriteType $displayWriteType ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "LmCompatibilityLevel Settings" -Details ($osInformation.LmCompatibility.RegistryValue) ` + -DisplayGroupingKey $keySecuritySettings ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Description" -Details ($osInformation.LmCompatibility.Description) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + + ############## + # TLS Settings + ############## + Write-VerboseOutput("Working on TLS Settings") + + $tlsVersions = @("1.0","1.1","1.2") + $currentNetVersion = $osInformation.TLSSettings["NETv4"] + + foreach ($tlsKey in $tlsVersions) + { + $currentTlsVersion = $osInformation.TLSSettings[$tlsKey] + + $analyzedResults = Add-AnalyzedResultInformation -Details ("TLS {0}" -f $tlsKey) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 1 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name ("Server Enabled") -Details ($currentTlsVersion.ServerEnabled) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name ("Server Disabled By Default") -Details ($currentTlsVersion.ServerDisabledByDefault) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name ("Client Enabled") -Details ($currentTlsVersion.ClientEnabled) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name ("Client Disabled By Default") -Details ($currentTlsVersion.ClientDisabledByDefault) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + if ($currentTlsVersion.ServerEnabled -ne $currentTlsVersion.ClientEnabled) + { + $detectedTlsMismatch = $true + $analyzedResults = Add-AnalyzedResultInformation -Details ("Error: Mismatch in TLS version for client and server. Exchange can be both client and a server. This can cause issues within Exchange for communication.") ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 3 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + } + + if (($tlsKey -eq "1.0" -or + $tlsKey -eq "1.1") -and ( + $currentTlsVersion.ServerEnabled -eq $false -or + $currentTlsVersion.ClientEnabled -eq $false -or + $currentTlsVersion.ServerDisabledByDefault -or + $currentTlsVersion.ClientDisabledByDefault) -and + ($currentNetVersion.SystemDefaultTlsVersions -eq $false -or + $currentNetVersion.WowSystemDefaultTlsVersions -eq $false)) + { + $analyzedResults = Add-AnalyzedResultInformation -Details ("Error: Failed to set .NET SystemDefaultTlsVersions. Please visit on how to properly enable TLS 1.2 https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Exchange-Server-TLS-guidance-Part-2-Enabling-TLS-1-2-and/ba-p/607761") ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 3 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + } + } + + if ($detectedTlsMismatch) + { + $displayValues = @("Exchange Server TLS guidance Part 1: Getting Ready for TLS 1.2: https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Exchange-Server-TLS-guidance-part-1-Getting-Ready-for-TLS-1-2/ba-p/607649", + "Exchange Server TLS guidance Part 2: Enabling TLS 1.2 and Identifying Clients Not Using It: https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Exchange-Server-TLS-guidance-Part-2-Enabling-TLS-1-2-and/ba-p/607761", + "Exchange Server TLS guidance Part 3: Turning Off TLS 1.0/1.1: https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Exchange-Server-TLS-guidance-Part-3-Turning-Off-TLS-1-0-1-1/ba-p/607898") + + $analyzedResults = Add-AnalyzedResultInformation -Details "For More Information on how to properly set TLS follow these blog posts:" ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Yellow" ` + -AnalyzedInformation $analyzedResults + + foreach ($displayValue in $displayValues) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $displayValue ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType "Yellow" ` + -DisplayCustomTabNumber 3 ` + -AnalyzedInformation $analyzedResults + } + } + + foreach($certificate in $exchangeInformation.ExchangeCertificates) + { + if($certificate.LifetimeInDays -ge 60) + { + $displayColor = "Green" + } + elseif($certificate.LifetimeInDays -ge 30) + { + $displayColor = "Yellow" + } + else + { + $displayColor = "Red" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Certificate" ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 1 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "FriendlyName" -Details $certificate.FriendlyName ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Thumbprint" -Details $certificate.Thumbprint ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Lifetime in days" -Details $certificate.LifetimeInDays ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType $displayColor ` + -AnalyzedInformation $analyzedResults + + if($certificate.PublicKeySize -lt 2048) + { + $additionalDisplayValue = "It's recommended to use a key size of at least 2048 bit." + + $analyzedResults = Add-AnalyzedResultInformation -Name "Key size" -Details $certificate.PublicKeySize ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Details $additionalDisplayValue ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + } + else + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Key size" -Details $certificate.PublicKeySize ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "Bound to services" -Details $certificate.Services ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Current Auth Certificate" -Details $certificate.IsCurrentAuthConfigCertificate ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "SAN Certificate" -Details $certificate.IsSanCertificate ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Name "Namespaces" ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -AnalyzedInformation $analyzedResults + + foreach($namespace in $certificate.Namespaces) + { + $analyzedResults = Add-AnalyzedResultInformation -Details $namespace ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 3 ` + -AnalyzedInformation $analyzedResults + } + } + + if($exchangeInformation.ExchangeCertificates.IsCurrentAuthConfigCertificate.Contains($true)) + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Valid Auth Certificate Found On Server" -Details $true ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 1 ` + -DisplayWriteType "Green" ` + -AnalyzedInformation $analyzedResults + } + else + { + $analyzedResults = Add-AnalyzedResultInformation -Name "Valid Auth Certificate Found On Server" -Details $false ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 1 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + + $analyzedResults = Add-AnalyzedResultInformation -Details "No valid Auth Certificate found. This may cause several problems --- Error" ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayCustomTabNumber 2 ` + -DisplayWriteType "Red" ` + -AnalyzedInformation $analyzedResults + } + + $additionalDisplayValue = [string]::Empty + $smb1Status = $osInformation.Smb1ServerSettings.Smb1Status + + if ($osInformation.BuildInformation.MajorVersion -gt [HealthChecker.OSServerVersion]::Windows2012) + { + $displayValue = "False" + $writeType = "Green" + + if ($smb1Status -band 1) + { + $displayValue = "Failed to get install status" + $writeType = "Yellow" + } + elseif ($smb1Status -band 2) + { + $displayValue = "True" + $writeType = "Red" + $additionalDisplayValue = "SMB1 should be uninstalled" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "SMB1 Installed" -Details $displayValue ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType $writeType ` + -AnalyzedInformation $analyzedResults + } + + $writeType = "Green" + $displayValue = "True" + + if ($smb1Status -band 8) + { + $displayValue = "Failed to get block status" + $writeType = "Yellow" + } + elseif ($smb1Status -band 16) + { + $displayValue = "False" + $writeType = "Red" + $additionalDisplayValue += " SMB1 should be blocked" + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "SMB1 Blocked" -Details $displayValue ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType $writeType ` + -AnalyzedInformation $analyzedResults + + if ($additionalDisplayValue -ne [string]::Empty) + { + $additionalDisplayValue += "`r`n`t`tMore Information: https://techcommunity.microsoft.com/t5/exchange-team-blog/exchange-server-and-smbv1/ba-p/1165615" + + $analyzedResults = Add-AnalyzedResultInformation -Details $additionalDisplayValue.Trim() ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType "Yellow" ` + -DisplayCustomTabNumber 2 ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + + ########################## + #Exchange Web App GC Mode# + ########################## + if ($exchangeInformation.BuildInformation.ServerRole -ne [HealthChecker.ExchangeServerRole]::Edge) + { + Write-VerboseOutput("Working on Exchange Web App GC Mode") + + $analyzedResults = Add-AnalyzedResultInformation -Name "Web App Pool" -Details "GC Server Mode Enabled | Status" ` + -DisplayGroupingKey $keyWebApps ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + + foreach ($webAppKey in $exchangeInformation.ApplicationPools.Keys) + { + $xmlData = [xml]$exchangeInformation.ApplicationPools[$webAppKey].Content + $testingValue = New-Object PSCustomObject + $testingValue | Add-Member -MemberType NoteProperty -Name "GCMode" -Value ($enabled = $xmlData.Configuration.Runtime.gcServer.Enabled -eq 'true') + $testingValue | Add-Member -MemberType NoteProperty -Name "Status" -Value ($status = $exchangeInformation.ApplicationPools[$webAppKey].Status) + + $analyzedResults = Add-AnalyzedResultInformation -Name $webAppKey -Details ("{0} | {1}" -f $enabled, $status) ` + -DisplayGroupingKey $keyWebApps ` + -DisplayTestingValue $testingValue ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + } + + ###################### + # Vulnerability Checks + ###################### + + Function Test-VulnerabilitiesByBuildNumbersForDisplay { + param( + [Parameter(Mandatory=$true)][string]$ExchangeBuildRevision, + [Parameter(Mandatory=$true)][array]$SecurityFixedBuilds, + [Parameter(Mandatory=$true)][array]$CVENames + ) + [int]$fileBuildPart = ($split = $ExchangeBuildRevision.Split("."))[0] + [int]$filePrivatePart = $split[1] + $Script:breakpointHit = $false + + foreach ($securityFixedBuild in $SecurityFixedBuilds) + { + [int]$securityFixedBuildPart = ($split = $securityFixedBuild.Split("."))[0] + [int]$securityFixedPrivatePart = $split[1] + + if($fileBuildPart -eq $securityFixedBuildPart) + { + $Script:breakpointHit = $true + } + + if (($fileBuildPart -lt $securityFixedBuildPart) -or + ($fileBuildPart -eq $securityFixedBuildPart -and + $filePrivatePart -lt $securityFixedPrivatePart)) + { + foreach ($cveName in $CVENames) + { + $Script:AnalyzedInformation = Add-AnalyzedResultInformation -Name "Security Vulnerability" -Details ("{0}`r`n`t`tSee: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/{0} for more information." -f $cveName) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayTestingValue $cveName ` + -DisplayWriteType "Red" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $Script:AnalyzedInformation + } + + $Script:AllVulnerabilitiesPassed = $false + break + } + + if($Script:breakpointHit) + { + break + } + } + } + + $Script:AllVulnerabilitiesPassed = $true + $Script:AnalyzedInformation = $analyzedResults + [string]$buildRevision = ("{0}.{1}" -f $exchangeInformation.BuildInformation.ExchangeSetup.FileBuildPart, $exchangeInformation.BuildInformation.ExchangeSetup.FilePrivatePart) + + Write-VerboseOutput("Exchange Build Revision: {0}" -f $buildRevision) + Write-VerboseOutput("Exchange CU: {0}" -f ($exchangeCU = $exchangeInformation.BuildInformation.CU)) + + if ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2013) + { + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU19) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1347.5","1365.3" -CVENames "CVE-2018-0924","CVE-2018-0940" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU20) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1365.7","1367.6" -CVENames "CVE-2018-8151","CVE-2018-8154","CVE-2018-8159" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU21) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1367.9","1395.7" -CVENames "CVE-2018-8302" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1395.8" -CVENames "CVE-2018-8265","CVE-2018-8448" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1395.10" -CVENames "CVE-2019-0586","CVE-2019-0588" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU22) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1473.3" -CVENames "CVE-2019-0686","CVE-2019-0724" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1473.4" -CVENames "CVE-2019-0817","CVE-2019-0858" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1473.5" -CVENames "ADV190018" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU23) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.3" -CVENames "CVE-2019-1084","CVE-2019-1136","CVE-2019-1137" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.4" -CVENames "CVE-2019-1373" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.6" -CVENames "CVE-2020-0688","CVE-2020-0692" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.7" -CVENames "CVE-2020-16969" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.8" -CVENames "CVE-2020-17083","CVE-2020-17084","CVE-2020-17085" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1497.10" -CVENames "CVE-2020-17117","CVE-2020-17132","CVE-2020-17142","CVE-2020-17143" + } + } + elseif ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2016) + { + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU8) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1261.39","1415.4" -CVENames "CVE-2018-0924","CVE-2018-0940","CVE-2018-0941" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU9) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1415.7","1466.8" -CVENames "CVE-2018-8151","CVE-2018-8152","CVE-2018-8153","CVE-2018-8154","CVE-2018-8159" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU10) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1466.9","1531.6" -CVENames "CVE-2018-8374","CVE-2018-8302" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1531.8" -CVENames "CVE-2018-8265","CVE-2018-8448" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU11) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1531.8","1591.11" -CVENames "CVE-2018-8604" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1531.10","1591.13" -CVENames "CVE-2019-0586","CVE-2019-0588" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU12) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1591.16","1713.6" -CVENames "CVE-2019-0817","CVE-2018-0858" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1591.17","1713.7" -CVENames "ADV190018" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1713.5" -CVENames "CVE-2019-0686","CVE-2019-0724" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU13) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1713.8","1779.4" -CVENames "CVE-2019-1084","CVE-2019-1136","CVE-2019-1137" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1713.9","1779.5" -CVENames "CVE-2019-1233","CVE-2019-1266" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU14) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1779.7","1847.5" -CVENames "CVE-2019-1373" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU15) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1847.7","1913.7" -CVENames "CVE-2020-0688","CVE-2020-0692" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1847.10","1913.10" -CVENames "CVE-2020-0903" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU17) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "1979.6","2044.6" -CVENames "CVE-2020-16875" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU18) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "2044.7","2106.3" -CVENames "CVE-2020-16969" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "2044.8","2106.4" -CVENames "CVE-2020-17083","CVE-2020-17084","CVE-2020-17085" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "2044.12","2106.6" -CVENames "CVE-2020-17117","CVE-2020-17132","CVE-2020-17141","CVE-2020-17142","CVE-2020-17143" + } + if ($exchangeCU -ge [HealthChecker.ExchangeCULevel]::CU19) + { + Write-VerboseOutput("There are no known vulnerabilities in this Exchange Server Build.") + } + } + elseif ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2019) + { + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU1) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "221.14" -CVENames "CVE-2019-0586","CVE-2019-0588" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "221.16","330.7" -CVENames "CVE-2019-0817","CVE-2019-0858" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "221.17","330.8" -CVENames "ADV190018" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "330.6" -CVENames "CVE-2019-0686","CVE-2019-0724" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU2) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "330.9","397.5" -CVENames "CVE-2019-1084","CVE-2019-1137" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "397.6","330.10" -CVENames "CVE-2019-1233","CVE-2019-1266" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU3) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "397.9","464.7" -CVENames "CVE-2019-1373" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU4) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "464.11","529.8" -CVENames "CVE-2020-0688","CVE-2020-0692" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "464.14","529.11" -CVENames "CVE-2020-0903" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU6) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "595.6","659.6" -CVENames "CVE-2020-16875" + } + if ($exchangeCU -le [HealthChecker.ExchangeCULevel]::CU7) + { + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "659.7","721.3" -CVENames "CVE-2020-16969" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "659.8","721.4" -CVENames "CVE-2020-17083","CVE-2020-17084","CVE-2020-17085" + Test-VulnerabilitiesByBuildNumbersForDisplay -ExchangeBuildRevision $buildRevision -SecurityFixedBuilds "659.11","721.6" -CVENames "CVE-2020-17117","CVE-2020-17132","CVE-2020-17141","CVE-2020-17142","CVE-2020-17143" + } + if ($exchangeCU -ge [HealthChecker.ExchangeCULevel]::CU8) + { + Write-VerboseOutput("There are no known vulnerabilities in this Exchange Server Build.") + } + } + else + { + Write-VerboseOutput("Unknown Version of Exchange") + $Script:AllVulnerabilitiesPassed = $false + } + + #Description: Check for CVE-2020-0796 SMBv3 vulnerability + #Affected OS versions: Windows 10 build 1903 and 1909 + #Fix: KB4551762 + #Workaround: Disable SMBv3 compression + + if ($exchangeInformation.BuildInformation.MajorVersion -eq [HealthChecker.ExchangeMajorVersion]::Exchange2019) + { + Write-VerboseOutput("Testing CVE: CVE-2020-0796") + $buildNumber = $osInformation.BuildInformation.VersionBuild.Split(".")[2] + + if (($buildNumber -eq 18362 -or + $buildNumber -eq 18363) -and + ($osInformation.RegistryValues.CurrentVersionUbr -lt 720)) + { + Write-VerboseOutput("Build vulnerable to CVE-2020-0796. Checking if workaround is in place.") + $writeType = "Red" + $writeValue = "System Vulnerable" + + if ($osInformation.RegistryValues.LanManServerDisabledCompression -eq 1) + { + Write-VerboseOutput("Workaround to disable affected SMBv3 compression is in place.") + $writeType = "Yellow" + $writeValue = "Workaround is in place" + } + else + { + Write-VerboseOutput("Workaround to disable affected SMBv3 compression is NOT in place.") + $Script:AllVulnerabilitiesPassed = $false + } + + $analyzedResults = Add-AnalyzedResultInformation -Name "CVE-2020-0796" -Details ("{0}`r`n`t`tSee: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-0796 for more information." -f $writeValue) ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType $writeType ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $analyzedResults + } + else + { + Write-VerboseOutput("System NOT vulnerable to CVE-2020-0796. Information URL: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-0796") + } + } + else + { + Write-VerboseOutput("Operating System NOT vulnerable to CVE-2020-0796.") + } + + #Description: Check for CVE-2020-1147 + #Affected OS versions: Every OS supporting .NET Core 2.1 and 3.1 and .NET Framework 2.0 SP2 or above + #Fix: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1147 + #Workaround: N/A + $dllFileBuildPartToCheckAgainst = 3630 + + if ($osInformation.NETFramework.NetMajorVersion -eq [HealthChecker.NetMajorVersion]::Net4d8) + { + $dllFileBuildPartToCheckAgainst = 4190 + } + + Write-VerboseOutput("System.Data.dll FileBuildPart: {0} | LastWriteTimeUtc: {1}" -f ($systemDataDll = $osInformation.NETFramework.FileInformation["System.Data.dll"]).VersionInfo.FileBuildPart, ` + $systemDataDll.LastWriteTimeUtc) + Write-VerboseOutput("System.Configuration.dll FileBuildPart: {0} | LastWriteTimeUtc: {1}" -f ($systemConfigurationDll = $osInformation.NETFramework.FileInformation["System.Configuration.dll"]).VersionInfo.FileBuildPart, ` + $systemConfigurationDll.LastWriteTimeUtc) + + if($systemDataDll.VersionInfo.FileBuildPart -ge $dllFileBuildPartToCheckAgainst -and + $systemConfigurationDll.VersionInfo.FileBuildPart -ge $dllFileBuildPartToCheckAgainst -and + $systemDataDll.LastWriteTimeUtc -ge ([System.Convert]::ToDateTime("06/05/2020", [System.Globalization.DateTimeFormatInfo]::InvariantInfo)) -and + $systemConfigurationDll.LastWriteTimeUtc -ge ([System.Convert]::ToDateTime("06/05/2020", [System.Globalization.DateTimeFormatInfo]::InvariantInfo))) + { + Write-VerboseOutput("System NOT vulnerable to {0}. Information URL: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/{0}" -f "CVE-2020-1147") + } + else + { + $Script:AllVulnerabilitiesPassed = $false + $Script:AnalyzedInformation = Add-AnalyzedResultInformation -Name "Security Vulnerability" -Details ("{0}`r`n`t`tSee: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/{0} for more information." -f "CVE-2020-1147") ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType "Red" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $Script:AnalyzedInformation + } + + if ($Script:AllVulnerabilitiesPassed) + { + $Script:AnalyzedInformation = Add-AnalyzedResultInformation -Details "All known security issues in this version of the script passed." ` + -DisplayGroupingKey $keySecuritySettings ` + -DisplayWriteType "Green" ` + -AddHtmlDetailRow $false ` + -AnalyzedInformation $Script:AnalyzedInformation + } + + Write-Debug("End of Analyzer Engine") + return $Script:AnalyzedInformation +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Confirm-Administrator/Confirm-Administrator.ps1 +Function Confirm-Administrator { + #Function Version 1.1 + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent() ) + if($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator )) + { + return $true + } + else + { + return $false + } +} + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Confirm-ExchangeShell/Confirm-ExchangeShell.ps1 +Function Confirm-ExchangeShell { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)][bool]$LoadExchangeShell = $true, + [Parameter(Mandatory=$false)][bool]$LoadExchangeVariables = $true, + [Parameter(Mandatory=$false)][bool]$ByPassLocalExchangeServerTest = $false, + [Parameter(Mandatory=$false)][scriptblock]$CatchActionFunction + ) + #Function Version 1.6 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-HostWriters/Write-HostWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-VerboseWriter.ps1 + #> + + $passed = $false + Write-VerboseWriter("Calling: Confirm-ExchangeShell") + Write-VerboseWriter("Passed: [bool]LoadExchangeShell: {0} | [bool]LoadExchangeVariables: {1} | [bool]ByPassLocalExchangeServerTest: {2}" -f $LoadExchangeShell, + $LoadExchangeVariables, $ByPassLocalExchangeServerTest) + #Test that we are on Exchange 2010 or newer + if(($isLocalExchangeServer = (Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\Setup')) -or + ($isLocalExchangeServer = (Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup')) -or + $ByPassLocalExchangeServerTest) + { + Write-VerboseWriter("We are on Exchange 2010 or newer") + if((Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\EdgeTransportRole') -or + (Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\EdgeTransportRole')) + { + Write-VerboseWriter("We are on Exchange Edge Transport Server") + $IsEdgeTransport = $true + } + try + { + if(((Get-PSSession | Where-Object {($_.Availability -eq 'Available') -and + ($_.ConfigurationName -eq 'Microsoft.Exchange')}).Count -eq 0) -and + ((Get-Module -Name RemoteExchange).Count -eq 1)) + { + Write-VerboseWriter("Removing RemoteExchange module") + Remove-Module -Name RemoteExchange + $currentPSModules = Get-Module + foreach ($PSModule in $currentPSModules) + { + if(($PSModule.ModuleType -eq "Script") -and + ($PSModule.ModuleBase -like "*\Microsoft\Exchange\RemotePowerShell\*")) + { + Write-VerboseWriter("Removing module {0} for implicit remoting" -f $PSModule.Name) + Remove-Module -Name $PSModule.Name + } + } + } + + Get-ExchangeServer -ErrorAction Stop | Out-Null + Write-VerboseWriter("Exchange PowerShell Module already loaded.") + $passed = $true + } + catch + { + Write-VerboseWriter("Failed to run Get-ExchangeServer") + if($CatchActionFunction -ne $null) + { + & $CatchActionFunction + $watchErrors = $true + } + if($LoadExchangeShell -and + $isLocalExchangeServer) + { + Write-HostWriter "Loading Exchange PowerShell Module..." + try + { + if($watchErrors) + { + $currentErrors = $Error.Count + } + if($IsEdgeTransport) + { + [xml]$PSSnapIns = Get-Content -Path "$env:ExchangeInstallPath\Bin\exshell.psc1" -ErrorAction Stop + ForEach($PSSnapIn in $PSSnapIns.PSConsoleFile.PSSnapIns.PSSnapIn) + { + Write-VerboseWriter("Trying to add PSSnapIn: {0}" -f $PSSnapIn.Name) + Add-PSSnapin -Name $PSSnapIn.Name -ErrorAction Stop + } + Import-Module $env:ExchangeInstallPath\bin\Exchange.ps1 -ErrorAction Stop + $passed = $true #We are just going to assume this passed. + } + else + { + Import-Module $env:ExchangeInstallPath\bin\RemoteExchange.ps1 -ErrorAction Stop + Connect-ExchangeServer -Auto -ClientApplication:ManagementShell + $passed = $true #We are just going to assume this passed. + } + if($watchErrors) + { + $index = 0 + while($index -lt ($Error.Count - $currentErrors)) + { + & $CatchActionFunction $Error[$index] + $index++ + } + } + } + catch + { + Write-HostWriter("Failed to Load Exchange PowerShell Module...") + } + } + } + finally + { + if($LoadExchangeVariables -and + $passed -and + $isLocalExchangeServer) + { + #Diff from master not using Get-ExchangeInstallDirectory because of required functions + if($ExInstall -eq $null -or $ExBin -eq $null) + { + if(Test-Path 'HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\Setup') + { + $Global:ExInstall = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\Setup).MsiInstallPath + } + else + { + $Global:ExInstall = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath + } + + $Global:ExBin = $Global:ExInstall + "\Bin" + + Write-VerboseWriter("Set ExInstall: {0}" -f $Global:ExInstall) + Write-VerboseWriter("Set ExBin: {0}" -f $Global:ExBin) + } + } + } + } + else + { + Write-VerboseWriter("Does not appear to be an Exchange 2010 or newer server.") + } + Write-VerboseWriter("Returned: {0}" -f $passed) + return $passed + } + +#Master Template: https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/New-LoggerObject/New-LoggerObject.ps1 +Function New-LoggerObject { + [CmdletBinding()] + param( + [Parameter(Mandatory=$false)][string]$LogDirectory = ".", + [Parameter(Mandatory=$false)][string]$LogName = "Script_Logging", + [Parameter(Mandatory=$false)][bool]$EnableDateTime = $true, + [Parameter(Mandatory=$false)][bool]$IncludeDateTimeToFileName = $true, + [Parameter(Mandatory=$false)][int]$MaxFileSizeInMB = 10, + [Parameter(Mandatory=$false)][int]$CheckSizeIntervalMinutes = 10, + [Parameter(Mandatory=$false)][int]$NumberOfLogsToKeep = 10, + [Parameter(Mandatory=$false)][bool]$VerboseEnabled, + [Parameter(Mandatory=$false)][scriptblock]$HostFunctionCaller, + [Parameter(Mandatory=$false)][scriptblock]$VerboseFunctionCaller + ) + + #Function Version 1.2 + <# + Required Functions: + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-HostWriters/Write-ScriptMethodHostWriter.ps1 + https://raw.githubusercontent.com/dpaulson45/PublicPowerShellScripts/master/Functions/Write-VerboseWriters/Write-ScriptMethodVerboseWriter.ps1 + #> + + ######################## + # + # Template Functions + # + ######################## + + Function Write-ToLog { + param( + [object]$WriteString, + [string]$LogLocation + ) + $WriteString | Out-File ($LogLocation) -Append + } + + ######################## + # + # End Template Functions + # + ######################## + + + ########## Parameter Binding Exceptions ############## + # throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid ParameterName" + if($LogDirectory -eq ".") + { + $LogDirectory = (Get-Location).Path + } + if([string]::IsNullOrEmpty($LogName)) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid LogName" + } + if(!(Test-Path $LogDirectory)) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid LogDirectory" + } + + $loggerObject = New-Object pscustomobject + $loggerObject | Add-Member -MemberType NoteProperty -Name "FileDirectory" -Value $LogDirectory + $loggerObject | Add-Member -MemberType NoteProperty -Name "FileName" -Value $LogName + $loggerObject | Add-Member -MemberType NoteProperty -Name "FullPath" -Value $fullLogPath + $loggerObject | Add-Member -MemberType NoteProperty -Name "InstanceBaseName" -Value ([string]::Empty) + $loggerObject | Add-Member -MemberType NoteProperty -Name "EnableDateTime" -Value $EnableDateTime + $loggerObject | Add-Member -MemberType NoteProperty -Name "IncludeDateTimeToFileName" -Value $IncludeDateTimeToFileName + $loggerObject | Add-Member -MemberType NoteProperty -Name "MaxFileSizeInMB" -Value $MaxFileSizeInMB + $loggerObject | Add-Member -MemberType NoteProperty -Name "CheckSizeIntervalMinutes" -Value $CheckSizeIntervalMinutes + $loggerObject | Add-Member -MemberType NoteProperty -Name "NextFileCheckTime" -Value ((Get-Date).AddMinutes($CheckSizeIntervalMinutes)) + $loggerObject | Add-Member -MemberType NoteProperty -Name "InstanceNumber" -Value 1 + $loggerObject | Add-Member -MemberType NoteProperty -Name "NumberOfLogsToKeep" -Value $NumberOfLogsToKeep + $loggerObject | Add-Member -MemberType NoteProperty -Name "WriteVerboseData" -Value $VerboseEnabled + $loggerObject | Add-Member -MemberType NoteProperty -Name "PreventLogCleanup" -Value $false + $loggerObject | Add-Member -MemberType ScriptMethod -Name "ToLog" -Value ${Function:Write-ToLog} + $loggerObject | Add-Member -MemberType ScriptMethod -Name "WriteHostWriter" -Value ${Function:Write-ScriptMethodHostWriter} + $loggerObject | Add-Member -MemberType ScriptMethod -Name "WriteVerboseWriter" -Value ${Function:Write-ScriptMethodVerboseWriter} + + if($HostFunctionCaller -ne $null) + { + $loggerObject | Add-Member -MemberType ScriptMethod -Name "HostFunctionCaller" -Value $HostFunctionCaller + } + if($VerboseFunctionCaller -ne $null) + { + $loggerObject | Add-Member -MemberType ScriptMethod -Name "VerboseFunctionCaller" -Value $VerboseFunctionCaller + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "WriteHost" -Value { + param( + [object]$LoggingString + ) + if($LoggingString -eq $null) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid LoggingString" + } + + if($this.EnableDateTime) + { + $LoggingString = "[{0}] : {1}" -f [System.DateTime]::Now, $LoggingString + } + + $this.WriteHostWriter($LoggingString) + $this.ToLog($LoggingString, $this.FullPath) + $this.LogUpKeep() + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "WriteVerbose" -Value { + param( + [object]$LoggingString + ) + if($LoggingString -eq $null) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid LoggingString" + } + + if($this.EnableDateTime) + { + $LoggingString = "[{0}] : {1}" -f [System.DateTime]::Now, $LoggingString + } + $this.WriteVerboseWriter($LoggingString) + $this.ToLog($LoggingString, $this.FullPath) + $this.LogUpKeep() + + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "WriteToFileOnly" -Value { + param( + [object]$LoggingString + ) + if($LoggingString -eq $null) + { + throw [System.Management.Automation.ParameterBindingException] "Failed to provide valid LoggingString" + } + + if($this.EnableDateTime) + { + $LoggingString = "[{0}] : {1}" -f [System.DateTime]::Now, $LoggingString + } + $this.ToLog($LoggingString, $this.FullPath) + $this.LogUpKeep() + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "UpdateFileLocation" -Value{ + + if($this.FullPath -eq $null) + { + if($this.IncludeDateTimeToFileName) + { + $this.InstanceBaseName = "{0}_{1}" -f $this.FileName, ((Get-Date).ToString('yyyyMMddHHmmss')) + $this.FullPath = "{0}\{1}.txt" -f $this.FileDirectory, $this.InstanceBaseName + } + else + { + $this.InstanceBaseName = "{0}" -f $this.FileName + $this.FullPath = "{0}\{1}.txt" -f $this.FileDirectory, $this.InstanceBaseName + } + } + else + { + + do{ + $this.FullPath = "{0}\{1}_{2}.txt" -f $this.FileDirectory, $this.InstanceBaseName, $this.InstanceNumber + $this.InstanceNumber++ + }while(Test-Path $this.FullPath) + $this.WriteVerbose("Updated to New Log") + } + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "LogUpKeep" -Value { + + if($this.NextFileCheckTime -gt [System.DateTime]::Now) + { + return + } + $this.NextFileCheckTime = (Get-Date).AddMinutes($this.CheckSizeIntervalMinutes) + $this.CheckFileSize() + $this.CheckNumberOfFiles() + $this.WriteVerbose("Did Log Object Up Keep") + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "CheckFileSize" -Value { + + $item = Get-ChildItem $this.FullPath + if(($item.Length / 1MB) -gt $this.MaxFileSizeInMB) + { + $this.UpdateFileLocation() + } + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "CheckNumberOfFiles" -Value { + + $filter = "{0}*" -f $this.InstanceBaseName + $items = Get-ChildItem -Path $this.FileDirectory | ?{$_.Name -like $filter} + if($items.Count -gt $this.NumberOfLogsToKeep) + { + do{ + $items | Sort-Object LastWriteTime | Select -First 1 | Remove-Item -Force + $items = Get-ChildItem -Path $this.FileDirectory | ?{$_.Name -like $filter} + }while($items.Count -gt $this.NumberOfLogsToKeep) + } + } + + $loggerObject | Add-Member -MemberType ScriptMethod -Name "RemoveLatestLogFile" -Value { + + if(!$this.PreventLogCleanup) + { + $item = Get-ChildItem $this.FullPath + Remove-Item $item -Force + } + } + + $loggerObject.UpdateFileLocation() + try + { + "[{0}] : Creating a new logger instance" -f [System.DateTime]::Now | Out-File ($loggerObject.FullPath) -Append + } + catch + { + throw + } + + return $loggerObject + } + +Function Get-CounterSamples { +param( +[Parameter(Mandatory=$true)][array]$MachineNames, +[Parameter(Mandatory=$true)][array]$Counters +) + Write-VerboseOutput("Calling: Get-CounterSamples") + try + { + $counterSamples = (Get-Counter -ComputerName $MachineNames -Counter $Counters -ErrorAction Stop).CounterSamples + } + catch + { + Invoke-CatchActions + Write-VerboseOutput("Failed to get counter samples") + } + Write-VerboseOutput("Exiting: Get-CounterSamples") + return $counterSamples +} + +Function Get-ErrorsThatOccurred { + + if($Error.Count -gt $Script:ErrorStartCount) + { + Write-Grey(" "); Write-Grey(" ") + Function Write-Errors { + $index = 0; + "`r`n`r`nErrors that occurred that wasn't handled" | Out-File ($Script:OutputFullPath) -Append + $Script:Logger.WriteToFileOnly("`r`n`r`nErrors that occurred that wasn't handled") + while($index -lt ($Error.Count - $Script:ErrorStartCount)) + { + #for 2008R2 can't use .Contains on an array object, need to do something else. + $goodError = $false + foreach($okayErrors in $Script:ErrorsExcluded) + { + if($okayErrors.Equals($Error[$index])) + { + $goodError = $true + break + } + } + if(!($goodError)) + { + $Script:Logger.WriteToFileOnly($Error[$index]) + $Error[$index] | Out-File ($Script:OutputFullPath) -Append + } + $index++ + } + Write-Grey(" "); Write-Grey(" ") + "Errors that were handled" | Out-File ($Script:OutputFullPath) -Append + $Script:Logger.WriteToFileOnly("`r`n`r`nErrors that were handled") + foreach($okayErrors in $Script:ErrorsExcluded) + { + $okayErrors | Out-File ($Script:OutputFullPath) -Append + $Script:Logger.WriteToFileOnly($okayErrors) + } + } + + if(($Error.Count - $Script:ErrorStartCount) -ne $Script:ErrorsExcludedCount) + { + Write-Red("There appears to have been some errors in the script. To assist with debugging of the script, please send the HealthChecker-Debug_*.txt and .xml file to ExToolsFeedback@microsoft.com.") + $Script:Logger.PreventLogCleanup = $true + Write-Errors + } + elseif($Script:VerboseEnabled -or + $SaveDebugLog) + { + Write-VerboseOutput("All errors that occurred were in try catch blocks and was handled correctly.") + $Script:Logger.PreventLogCleanup = $true + Write-Errors + } + } + else + { + Write-VerboseOutput("No errors occurred in the script.") + } +} + +Function Get-HealthCheckFilesItemsFromLocation{ + $items = Get-ChildItem $XMLDirectoryPath | Where-Object{$_.Name -like "HealthCheck-*-*.xml"} + if($items -eq $null) + { + Write-Host("Doesn't appear to be any Health Check XML files here....stopping the script") + exit + } + return $items +} + +Function Get-OnlyRecentUniqueServersXMLs { +param( +[Parameter(Mandatory=$true)][array]$FileItems +) + $aObject = @() + foreach($item in $FileItems) + { + $obj = New-Object PSCustomobject + [string]$itemName = $item.Name + $ServerName = $itemName.Substring(($itemName.IndexOf("-") + 1), ($itemName.LastIndexOf("-") - $itemName.IndexOf("-") - 1)) + $obj | Add-Member -MemberType NoteProperty -Name ServerName -Value $ServerName + $obj | Add-Member -MemberType NoteProperty -Name FileName -Value $itemName + $obj | Add-Member -MemberType NoteProperty -Name FileObject -Value $item + $aObject += $obj + } + + $grouped = $aObject | Group-Object ServerName + $FilePathList = @() + foreach($gServer in $grouped) + { + if($gServer.Count -gt 1) + { + #going to only use the most current file for this server providing that they are using the newest updated version of Health Check we only need to sort by name + $groupData = $gServer.Group #because of win2008 + $FilePathList += ($groupData | Sort-Object FileName -Descending | Select-Object -First 1).FileObject.VersionInfo.FileName + } + else + { + $FilePathList += ($gServer.Group).FileObject.VersionInfo.FileName + } + } + return $FilePathList +} + +Function Import-MyData { +param( +[Parameter(Mandatory=$true)][array]$FilePaths +) + [System.Collections.Generic.List[System.Object]]$myData = New-Object -TypeName System.Collections.Generic.List[System.Object] + foreach($filePath in $FilePaths) + { + $importData = Import-Clixml -Path $filePath + $myData.Add($importData) + } + return $myData +} + +Function Invoke-CatchActions{ +param( +[object]$CopyThisError +) + + Write-VerboseOutput("Calling: Invoke-CatchActions") + $Script:ErrorsExcludedCount++ + if($CopyThisError -eq $null) + { + $Script:ErrorsExcluded += $Error[0] + } + else + { + $Script:ErrorsExcluded += $CopyThisError + } +} + +Function Set-ScriptLogFileLocation { +param( +[Parameter(Mandatory=$true)][string]$FileName, +[Parameter(Mandatory=$false)][bool]$IncludeServerName = $false +) + $endName = "-{0}.txt" -f $dateTimeStringFormat + if($IncludeServerName) + { + $endName = "-{0}{1}" -f $Script:Server, $endName + } + + $Script:OutputFullPath = "{0}\{1}{2}" -f $OutputFilePath, $FileName, $endName + $Script:OutXmlFullPath = $Script:OutputFullPath.Replace(".txt",".xml") + if($AnalyzeDataOnly -or + $BuildHtmlServersReport) + { + return + } + + $byPassLocalExchangeServerTest = $false + + if ($Script:Server -ne $env:COMPUTERNAME) + { + $byPassLocalExchangeServerTest = $true + } + + if(!(Confirm-ExchangeShell ` + -ByPassLocalExchangeServerTest $byPassLocalExchangeServerTest ` + -CatchActionFunction ${Function:Invoke-CatchActions} )) + { + Write-Yellow("Failed to load Exchange Shell... stopping script") + exit + } +} + +Function Test-RequiresServerFqdn { + + Write-VerboseOutput("Calling: Test-RequiresServerFqdn") + try + { + $Script:ServerFQDN = (Get-ExchangeServer $Script:Server).FQDN + Invoke-Command -ComputerName $Script:Server -ScriptBlock {Get-Date | Out-Null} -ErrorAction Stop + Write-VerboseOutput("Connected successfully using NetBIOS name.") + } + catch + { + Invoke-CatchActions + Write-VerboseOutput("Failed to connect to {0} using NetBIOS name. Fallback to Fqdn: {1}" -f $Script:Server, $Script:ServerFQDN) + $Script:Server = $Script:ServerFQDN + } +} + +Function Test-IsCurrentVersion { +param( +[Parameter(Mandatory=$true)][string]$CurrentVersion, +[Parameter(Mandatory=$true)][string]$TestingVersion +) + Write-VerboseOutput("Calling: Test-IsCurrentVersion") + $splitCurrentVersion = $CurrentVersion.Split(".") + $splitTestingVersion = $TestingVersion.Split(".") + if($splitCurrentVersion.Count -eq $splitTestingVersion.Count) + { + for($i = 0; $i -lt $splitCurrentVersion.Count; $i++) + { + if($splitCurrentVersion[$i] -lt $splitTestingVersion[$i]) + { + return $false + } + } + return $true + } + else + { + Write-VerboseOutput("Split count isn't the same, assuming that we are not on current version.") + return $false + } +} + +Function Test-ScriptVersion { +param( +[Parameter(Mandatory=$true)][string]$ApiUri, +[Parameter(Mandatory=$true)][string]$RepoOwner, +[Parameter(Mandatory=$true)][string]$RepoName, +[Parameter(Mandatory=$true)][string]$CurrentVersion, +[Parameter(Mandatory=$true)][int]$DaysOldLimit, +[Parameter(Mandatory=$false)][Scriptblock]$CatchActionFunction +) + Write-VerboseOutput("Calling: Test-ScriptVersion") + + $isCurrent = $false + + if(Test-Connection -ComputerName $ApiUri -Count 1 -Quiet) + { + try + { + $ScriptBlock = { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + ConvertFrom-Json(Invoke-WebRequest -Uri ($uri = "https://$($args[0])/repos/$($args[1])/$($args[2])/releases/latest")) + } + + $WebRequestJob = Start-Job -ScriptBlock $ScriptBlock -Name "WebRequestJob" -ArgumentList $ApiUri,$RepoOwner,$RepoName + do + { + $i++ + if((Get-Job -Id $WebRequestJob.Id).State -eq "Completed") + { + Write-VerboseOutput("WebRequest after {0} attempts successfully completed. Receiving results." -f $i) + + try + { + $releaseInformation = Receive-Job -Id $WebRequestJob.Id -Keep -ErrorAction Stop + } + catch + { + if ($CatchActionFunction -ne $null) + { + & $CatchActionFunction + } + } + + Write-VerboseOutput("Removing background worker job") + Remove-Job -Id $WebRequestJob.Id + Break + } + else + { + Write-VerboseOutput("Attempt: {0} WebRequest not yet complete." -f $i) + if($i -eq 30) + { + Write-VerboseOutput("Reached 30 attempts. Removing background worker job.") + Remove-Job -Id $WebRequestJob.Id + } + Start-Sleep -Seconds 1 + } + } + while($i -lt 30) + } + catch + { + Invoke-CatchActions + Write-VerboseOutput("Failed to run Invoke-WebRequest") + } + + if($releaseInformation -ne $null) + { + Write-VerboseOutput("We're online: {0} connected successfully." -f $uri) + $latestVersion = ($releaseInformation.tag_name).Split("v")[1] + if(Test-IsCurrentVersion -CurrentVersion $CurrentVersion -TestingVersion $latestVersion) + { + Write-VerboseOutput("Version '{0}' is the latest version." -f $latestVersion) + $isCurrent = $true + } + else + { + Write-VerboseOutput("Version '{0}' is outdated. Lastest version is '{1}'" -f $CurrentVersion, $latestVersion) + } + } + else + { + Write-VerboseOutput("Release information was null.") + } + } + else + { + Write-VerboseOutput("We're offline: Unable to connect to '{0}" -f $ApiUri) + Write-VerboseOutput("Unable to determine if this version '{0}' is current. Checking to see if the file is older than {1} days." -f $CurrentVersion, $DaysOldLimit) + $writeTime = (Get-ChildItem ($MyInvocation.ScriptName)).LastWriteTime + if($writeTime -gt ($testDate = ([datetime]::Now).AddDays(-$DaysOldLimit))) + { + Write-VerboseOutput("Determined that the script write time '{0}' is new than our our test date '{1}'." -f $writeTime, $testDate) + $isCurrent = $true + } + else + { + Write-VerboseOutput("Script doesn't appear to be on the latest possible version. Script write time '{0}' vs out test date '{1}'" -f $writeTime, $testDate) + } + } + + Write-VerboseOutput("Exiting: Test-ScriptVersion | Returning: {0}" -f $isCurrent) + return $isCurrent +} + +Function Create-HtmlServerReport { +param( +[Parameter(Mandatory=$true)][array]$AnalyzedHtmlServerValues +) + Write-VerboseOutput("Calling: Create-HtmlServerReport") + + $htmlHeader = " + + +

Exchange Health Checker v$($Script:healthCheckerVersion)


+

Servers Overview

" + + [array]$htmlOverviewTable += "

+ + " + + foreach ($tableHeaderName in $AnalyzedHtmlServerValues[0]["OverviewValues"].Name) + { + $htmlOverviewTable += "" -f $tableHeaderName + } + + $htmlOverviewTable += "" + + foreach ($serverHtmlServerValues in $AnalyzedHtmlServerValues) + { + $htmlTableRow = @() + [array]$htmlTableRow += "" + foreach ($htmlTableDataRow in $serverHtmlServerValues["OverviewValues"]) + { + $htmlTableRow += "" -f $htmlTableDataRow.Class, ` + $htmlTableDataRow.DetailValue + } + + $htmlTableRow += "" + $htmlOverviewTable += $htmlTableRow + } + + $htmlOverviewTable += "
{0}
{1}

" + + [array]$htmlServerDetails += "

Server Details

" + + foreach ($serverHtmlServerValues in $AnalyzedHtmlServerValues) + { + foreach ($htmlTableDataRow in $serverHtmlServerValues["ServerDetails"]) + { + if ($htmlTableDataRow.Name -eq "Server Name") + { + $htmlServerDetails += "" -f $htmlTableDataRow.Name, ` + $htmlTableDataRow.DetailValue + } + else + { + $htmlServerDetails += "" -f $htmlTableDataRow.Class, ` + $htmlTableDataRow.Name, ` + $htmlTableDataRow.DetailValue + } + } + } + $htmlServerDetails += "
{0}{1}
{1}{2}

" + + $htmlReport = $htmlHeader + $htmlOverviewTable + $htmlServerDetails + "" + + $htmlReport | Out-File $HtmlReportFile -Encoding UTF8 +} + +Function Get-CASLoadBalancingReport { + + Write-VerboseOutput("Calling: Get-CASLoadBalancingReport") + Write-Yellow("Note: CAS Load Balancing Report has known issues with attempting to get counter from servers. If you see errors regarding 'Get-Counter path not valid', please ignore for the time being. This is going to be addressed in later versions") + #Connection and requests per server and client type values + $CASConnectionStats = @{} + $TotalCASConnectionCount = 0 + $AutoDStats = @{} + $TotalAutoDRequests = 0 + $EWSStats = @{} + $TotalEWSRequests = 0 + $MapiHttpStats = @{} + $TotalMapiHttpRequests = 0 + $EASStats = @{} + $TotalEASRequests = 0 + $OWAStats = @{} + $TotalOWARequests = 0 + $RpcHttpStats = @{} + $TotalRpcHttpRequests = 0 + $CASServers = @() + + if($CasServerList -ne $null) + { + Write-Grey("Custom CAS server list is being used. Only servers specified after the -CasServerList parameter will be used in the report.") + foreach($cas in $CasServerList) + { + $CASServers += (Get-ExchangeServer $cas) + } + } + elseif($SiteName -ne [string]::Empty) + { + Write-Grey("Site filtering ON. Only Exchange 2013/2016 CAS servers in {0} will be used in the report." -f $SiteName) + $CASServers = Get-ExchangeServer | Where-Object{($_.IsClientAccessServer -eq $true) -and ($_.AdminDisplayVersion -Match "^Version 15") -and ($_.Site.Name -eq $SiteName)} + } + else + { + Write-Grey("Site filtering OFF. All Exchange 2013/2016 CAS servers will be used in the report.") + $CASServers = Get-ExchangeServer | Where-Object{($_.IsClientAccessServer -eq $true) -and ($_.AdminDisplayVersion -Match "^Version 15")} + } + + if($CASServers.Count -eq 0) + { + Write-Red("Error: No CAS servers found using the specified search criteria.") + Exit + } + + #Request stats from perfmon for all CAS + $PerformanceCounters = @() + $PerformanceCounters += "\Web Service(Default Web Site)\Current Connections" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_Autodiscover)\Requests Executing" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_EWS)\Requests Executing" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_mapi)\Requests Executing" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_Microsoft-Server-ActiveSync)\Requests Executing" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_owa)\Requests Executing" + $PerformanceCounters += "\ASP.NET Apps v4.0.30319(_LM_W3SVC_1_ROOT_Rpc)\Requests Executing" + + try + { + $AllCounterResults = Get-Counter -ComputerName $CASServers -Counter $PerformanceCounters + } + catch + { + Invoke-CatchActions + Write-VerboseOutput("Failed to get counter samples") + } + + ForEach($Result in $AllCounterResults.CounterSamples) + { + $CasName = ($Result.Path).Split("\\",[System.StringSplitOptions]::RemoveEmptyEntries)[0] + $ResultCookedValue = $Result.CookedValue + + if($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[0]) + { + #Total connections + $CASConnectionStats.Add($CasName,$ResultCookedValue) + $TotalCASConnectionCount += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[1]) + { + #AutoD requests + $AutoDStats.Add($CasName,$ResultCookedValue) + $TotalAutoDRequests += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[2]) + { + #EWS requests + $EWSStats.Add($CasName,$ResultCookedValue) + $TotalEWSRequests += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[3]) + { + #MapiHttp requests + $MapiHttpStats.Add($CasName,$ResultCookedValue) + $TotalMapiHttpRequests += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[4]) + { + #EAS requests + $EASStats.Add($CasName,$ResultCookedValue) + $TotalEASRequests += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[5]) + { + #OWA requests + $OWAStats.Add($CasName,$ResultCookedValue) + $TotalOWARequests += $ResultCookedValue + } + elseif($Result.Path -like "*{0}*{1}" -f $CasName,$PerformanceCounters[6]) + { + #RPCHTTP requests + $RpcHttpStats.Add($CasName,$ResultCookedValue) + $TotalRpcHttpRequests += $ResultCookedValue + } + } + + + #Report the results for connection count + Write-Grey("") + Write-Grey("Connection Load Distribution Per Server") + Write-Grey("Total Connections: {0}" -f $TotalCASConnectionCount) + #Calculate percentage of connection load + $CASConnectionStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Connections = " + [math]::Round((([int]$_.Value/$TotalCASConnectionCount)*100)) + "% Distribution") + } + + #Same for each client type. These are request numbers not connection numbers. + #AutoD + if($TotalAutoDRequests -gt 0) + { + Write-Grey("") + Write-Grey("Current AutoDiscover Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalAutoDRequests) + $AutoDStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalAutoDRequests)*100)) + "% Distribution") + } + } + + #EWS + if($TotalEWSRequests -gt 0) + { + Write-Grey("") + Write-Grey("Current EWS Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalEWSRequests) + $EWSStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalEWSRequests)*100)) + "% Distribution") + } + } + + #MapiHttp + if($TotalMapiHttpRequests -gt 0) + { + Write-Grey("") + Write-Grey("Current MapiHttp Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalMapiHttpRequests) + $MapiHttpStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalMapiHttpRequests)*100)) + "% Distribution") + } + } + + #EAS + if($TotalEASRequests -gt 0) + { + Write-Grey("") + Write-Grey("Current EAS Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalEASRequests) + $EASStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalEASRequests)*100)) + "% Distribution") + } + } + + #OWA + if($TotalOWARequests -gt 0) + { + Write-Grey("") + Write-Grey("Current OWA Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalOWARequests) + $OWAStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalOWARequests)*100)) + "% Distribution") + } + } + + #RpcHttp + if($TotalRpcHttpRequests -gt 0) + { + Write-Grey("") + Write-Grey("Current RpcHttp Requests Per Server") + Write-Grey("Total Requests: {0}" -f $TotalRpcHttpRequests) + $RpcHttpStats.GetEnumerator() | Sort-Object -Descending | ForEach-Object { + Write-Grey($_.Key + ": " + $_.Value + " Requests = " + [math]::Round((([int]$_.Value/$TotalRpcHttpRequests)*100)) + "% Distribution") + } + } + Write-Grey("") +} + +Function Get-ComputerCoresObject { +param( +[Parameter(Mandatory=$true)][string]$Machine_Name +) + Write-VerboseOutput("Calling: Get-ComputerCoresObject") + Write-VerboseOutput("Passed: {0}" -f $Machine_Name) + + $returnObj = New-Object pscustomobject + $returnObj | Add-Member -MemberType NoteProperty -Name Error -Value $false + $returnObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Machine_Name + $returnObj | Add-Member -MemberType NoteProperty -Name NumberOfCores -Value ([int]::empty) + $returnObj | Add-Member -MemberType NoteProperty -Name Exception -Value ([string]::empty) + $returnObj | Add-Member -MemberType NoteProperty -Name ExceptionType -Value ([string]::empty) + try + { + $wmi_obj_processor = Get-WmiObjectHandler -ComputerName $Machine_Name -Class "Win32_Processor" -CatchActionFunction ${Function:Invoke-CatchActions} + + foreach($processor in $wmi_obj_processor) + { + $returnObj.NumberOfCores +=$processor.NumberOfCores + } + + Write-Grey("Server {0} Cores: {1}" -f $Machine_Name, $returnObj.NumberOfCores) + } + catch + { + Invoke-CatchActions + $thisError = $Error[0] + if($thisError.Exception.Gettype().FullName -eq "System.UnauthorizedAccessException") + { + Write-Yellow("Unable to get processor information from server {0}. You do not have the correct permissions to get this data from that server. Exception: {1}" -f $Machine_Name, $thisError.ToString()) + } + else + { + Write-Yellow("Unable to get processor information from server {0}. Reason: {1}" -f $Machine_Name, $thisError.ToString()) + } + $returnObj.Exception = $thisError.ToString() + $returnObj.ExceptionType = $thisError.Exception.Gettype().FullName + $returnObj.Error = $true + } + + return $returnObj +} + +Function Get-ExchangeDCCoreRatio { + + Set-ScriptLogFileLocation -FileName "HealthCheck-ExchangeDCCoreRatio" + Write-VerboseOutput("Calling: Get-ExchangeDCCoreRatio") + Write-Grey("Exchange Server Health Checker Report - AD GC Core to Exchange Server Core Ratio - v{0}" -f $healthCheckerVersion) + $coreRatioObj = New-Object pscustomobject + try + { + Write-VerboseOutput("Attempting to load Active Directory Module") + Import-Module ActiveDirectory + Write-VerboseOutput("Successfully loaded") + } + catch + { + Write-Red("Failed to load Active Directory Module. Stopping the script") + exit + } + + $ADSite = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().Name + [array]$DomainControllers = (Get-ADForest).Domains | %{ Get-ADDomainController -Filter {isGlobalCatalog -eq $true -and Site -eq $ADSite} -Server $_ } + + [System.Collections.Generic.List[System.Object]]$DCList = New-Object System.Collections.Generic.List[System.Object] + $DCCoresTotal = 0 + Write-Break + Write-Grey("Collecting data for the Active Directory Environment in Site: {0}" -f $ADSite) + $iFailedDCs = 0 + foreach($DC in $DomainControllers) + { + $DCCoreObj = Get-ComputerCoresObject -Machine_Name $DC.Name + $DCList.Add($DCCoreObj) + if(-not ($DCCoreObj.Error)) + { + $DCCoresTotal += $DCCoreObj.NumberOfCores + } + else + { + $iFailedDCs++ + } + } + + $coreRatioObj | Add-Member -MemberType NoteProperty -Name DCList -Value $DCList + if($iFailedDCs -eq $DomainControllers.count) + { + #Core count is going to be 0, no point to continue the script + Write-Red("Failed to collect data from your DC servers in site {0}." -f $ADSite) + Write-Yellow("Because we can't determine the ratio, we are going to stop the script. Verify with the above errors as to why we failed to collect the data and address the issue, then run the script again.") + exit + } + + [array]$ExchangeServers = Get-ExchangeServer | Where-Object {$_.Site -match $ADSite} + $EXCoresTotal = 0 + [System.Collections.Generic.List[System.Object]]$EXList = New-Object System.Collections.Generic.List[System.Object] + Write-Break + Write-Grey("Collecting data for the Exchange Environment in Site: {0}" -f $ADSite) + foreach($svr in $ExchangeServers) + { + $EXCoreObj = Get-ComputerCoresObject -Machine_Name $svr.Name + $EXList.Add($EXCoreObj) + if(-not ($EXCoreObj.Error)) + { + $EXCoresTotal += $EXCoreObj.NumberOfCores + } + } + $coreRatioObj | Add-Member -MemberType NoteProperty -Name ExList -Value $EXList + + Write-Break + $CoreRatio = $EXCoresTotal / $DCCoresTotal + Write-Grey("Total DC/GC Cores: {0}" -f $DCCoresTotal) + Write-Grey("Total Exchange Cores: {0}" -f $EXCoresTotal) + Write-Grey("You have {0} Exchange Cores for every Domain Controller Global Catalog Server Core" -f $CoreRatio) + if($CoreRatio -gt 8) + { + Write-Break + Write-Red("Your Exchange to Active Directory Global Catalog server's core ratio does not meet the recommended guidelines of 8:1") + Write-Red("Recommended guidelines for Exchange 2013/2016 for every 8 Exchange cores you want at least 1 Active Directory Global Catalog Core.") + Write-Yellow("Documentation:") + Write-Yellow("`thttps://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Ask-the-Perf-Guy-Sizing-Exchange-2013-Deployments/ba-p/594229") + Write-Yellow("`thttps://docs.microsoft.com/en-us/exchange/exchange-2013-sizing-and-configuration-recommendations-exchange-2013-help#active-directory") + + } + else + { + Write-Break + Write-Green("Your Exchange Environment meets the recommended core ratio of 8:1 guidelines.") + } + + $XMLDirectoryPath = $OutputFullPath.Replace(".txt",".xml") + $coreRatioObj | Export-Clixml $XMLDirectoryPath + Write-Grey("Output file written to {0}" -f $OutputFullPath) + Write-Grey("Output XML Object file written to {0}" -f $XMLDirectoryPath) +} + +Function Get-MailboxDatabaseAndMailboxStatistics { + + Write-VerboseOutput("Calling: Get-MailboxDatabaseAndMailboxStatistics") + + $AllDBs = Get-MailboxDatabaseCopyStatus -server $Script:Server -ErrorAction SilentlyContinue + $MountedDBs = $AllDBs | Where-Object{$_.ActiveCopy -eq $true} + if($MountedDBs.Count -gt 0) + { + Write-Grey("`tActive Database:") + foreach($db in $MountedDBs) + { + Write-Grey("`t`t" + $db.Name) + } + $MountedDBs.DatabaseName | ForEach-Object{Write-VerboseOutput("Calculating User Mailbox Total for Active Database: $_"); $TotalActiveUserMailboxCount += (Get-Mailbox -Database $_ -ResultSize Unlimited).Count} + Write-Grey("`tTotal Active User Mailboxes on server: " + $TotalActiveUserMailboxCount) + $MountedDBs.DatabaseName | ForEach-Object{Write-VerboseOutput("Calculating Public Mailbox Total for Active Database: $_"); $TotalActivePublicFolderMailboxCount += (Get-Mailbox -Database $_ -ResultSize Unlimited -PublicFolder).Count} + Write-Grey("`tTotal Active Public Folder Mailboxes on server: " + $TotalActivePublicFolderMailboxCount) + Write-Grey("`tTotal Active Mailboxes on server " + $Script:Server + ": " + ($TotalActiveUserMailboxCount + $TotalActivePublicFolderMailboxCount).ToString()) + } + else + { + Write-Grey("`tNo Active Mailbox Databases found on server " + $Script:Server + ".") + } + + $HealthyDbs = $AllDBs | Where-Object{$_.Status -match 'Healthy'} + if($HealthyDbs.count -gt 0) + { + Write-Grey("`r`n`tPassive Databases:") + foreach($db in $HealthyDbs) + { + Write-Grey("`t`t" + $db.Name) + } + $HealthyDbs.DatabaseName | ForEach-Object{Write-VerboseOutput("`tCalculating User Mailbox Total for Passive Healthy Databases: $_"); $TotalPassiveUserMailboxCount += (Get-Mailbox -Database $_ -ResultSize Unlimited).Count} + Write-Grey("`tTotal Passive user Mailboxes on Server: " + $TotalPassiveUserMailboxCount) + $HealthyDbs.DatabaseName | ForEach-Object{Write-VerboseOutput("`tCalculating Passive Mailbox Total for Passive Healthy Databases: $_"); $TotalPassivePublicFolderMailboxCount += (Get-Mailbox -Database $_ -ResultSize Unlimited -PublicFolder).Count} + Write-Grey("`tTotal Passive Public Mailboxes on server: " + $TotalPassivePublicFolderMailboxCount) + Write-Grey("`tTotal Passive Mailboxes on server: " + ($TotalPassiveUserMailboxCount + $TotalPassivePublicFolderMailboxCount).ToString()) + } + else + { + Write-Grey("`tNo Passive Mailboxes found on server " + $Script:Server + ".") + } +} + +Function Main { + + if(-not (Confirm-Administrator) -and + (-not $AnalyzeDataOnly -and + -not $BuildHtmlServersReport)) + { + Write-Warning "The script needs to be executed in elevated mode. Start the Exchange Management Shell as an Administrator." + $Script:ErrorStartCount = $Error.Count + Start-Sleep -Seconds 2; + exit + } + + if ($Error.Count -gt 175) + { + Write-Verbose("Clearing Error to avoid script issues") + $Error.Clear() + } + + $Script:ErrorStartCount = $Error.Count #useful for debugging + $Script:ErrorsExcludedCount = 0 #this is a way to determine if the only errors occurred were in try catch blocks. If there is a combination of errors in and out, then i will just dump it all out to avoid complex issues. + $Script:ErrorsExcluded = @() + $Script:date = (Get-Date) + $Script:dateTimeStringFormat = $date.ToString("yyyyMMddHHmmss") + + if($BuildHtmlServersReport) + { + Set-ScriptLogFileLocation -FileName "HealthChecker-HTMLServerReport" + $files = Get-HealthCheckFilesItemsFromLocation + $fullPaths = Get-OnlyRecentUniqueServersXMLs $files + $importData = Import-MyData -FilePaths $fullPaths + Create-HtmlServerReport -AnalyzedHtmlServerValues $importData.HtmlServerValues + sleep 2; + return + } + + if((Test-Path $OutputFilePath) -eq $false) + { + Write-Host "Invalid value specified for -OutputFilePath." -ForegroundColor Red + return + } + + if($LoadBalancingReport) + { + Set-ScriptLogFileLocation -FileName "LoadBalancingReport" + Write-HealthCheckerVersion + Write-Green("Client Access Load Balancing Report on " + $date) + Get-CASLoadBalancingReport + Write-Grey("Output file written to " + $OutputFullPath) + Write-Break + Write-Break + return + } + + if($DCCoreRatio) + { + $oldErrorAction = $ErrorActionPreference + $ErrorActionPreference = "Stop" + try + { + Get-ExchangeDCCoreRatio + return + } + finally + { + $ErrorActionPreference = $oldErrorAction + } + } + + if($MailboxReport) + { + Set-ScriptLogFileLocation -FileName "HealthCheck-MailboxReport" -IncludeServerName $true + Get-MailboxDatabaseAndMailboxStatistics + Write-Grey("Output file written to {0}" -f $Script:OutputFullPath) + return + } + + if ($AnalyzeDataOnly) + { + Set-ScriptLogFileLocation -FileName "HealthChecker-Analyzer" + $files = Get-HealthCheckFilesItemsFromLocation + $fullPaths = Get-OnlyRecentUniqueServersXMLs $files + $importData = Import-MyData -FilePaths $fullPaths + + $analyzedResults = @() + foreach ($serverData in $importData) + { + $analyzedServerResults = Start-AnalyzerEngine -HealthServerObject $serverData.HealthCheckerExchangeServer + Write-ResultsToScreen -ResultsToWrite $analyzedServerResults.DisplayResults + $analyzedResults += $analyzedServerResults + } + + Create-HtmlServerReport -AnalyzedHtmlServerValues $analyzedResults.HtmlServerValues + return + } + + Set-ScriptLogFileLocation -FileName "HealthCheck" -IncludeServerName $true + Test-RequiresServerFqdn + Write-HealthCheckerVersion + [HealthChecker.HealthCheckerExchangeServer]$HealthObject = Get-HealthCheckerExchangeServer + $analyzedResults = Start-AnalyzerEngine -HealthServerObject $HealthObject + Write-ResultsToScreen -ResultsToWrite $analyzedResults.DisplayResults + $currentErrors = $Error.Count + + try + { + $analyzedResults | Export-Clixml -Path $OutXmlFullPath -Encoding UTF8 -Depth 6 -ErrorAction SilentlyContinue + } + catch + { + Write-VerboseOutput("Failed to Export-Clixml. Converting HealthCheckerExchangeServer to json") + $jsonHealthChecker = $analyzedResults.HealthCheckerExchangeServer | ConvertTo-Json + + $testOuputxml = [PSCustomObject]@{ + HealthCheckerExchangeServer = $jsonHealthChecker | ConvertFrom-Json + HtmlServerValues = $analyzedResults.HtmlServerValues + DisplayResults = $analyzedResults.DisplayResults + } + + $testOuputxml | Export-Clixml -Path $OutXmlFullPath -Encoding UTF8 -Depth 6 -ErrorAction Stop + } + finally + { + if ($currentErrors -ne $Error.Count) + { + $index = 0 + while ($index -lt ($Error.Count - $currentErrors)) + { + Invoke-CatchActions $Error[$index] + $index++ + } + } + + Write-Grey("Output file written to {0}" -f $Script:OutputFullPath) + Write-Grey("Exported Data Object Written to {0} " -f $Script:OutXmlFullPath) + } +} + +try +{ + $Script:Logger = New-LoggerObject -LogName "HealthChecker-Debug" -LogDirectory $OutputFilePath -VerboseEnabled $true -EnableDateTime $false -ErrorAction SilentlyContinue + Main +} +finally +{ + Get-ErrorsThatOccurred + if($Script:VerboseEnabled) + { + $Host.PrivateData.VerboseForegroundColor = $VerboseForeground + } + $Script:Logger.RemoveLatestLogFile() + if($Script:Logger.PreventLogCleanup) + { + Write-Host("Output Debug file written to {0}" -f $Script:Logger.FullPath) + } +} + diff --git a/powershell/exchange/MessageTrackingLogGUI.ps1 b/powershell/exchange/MessageTrackingLogGUI.ps1 new file mode 100644 index 0000000..aab9047 --- /dev/null +++ b/powershell/exchange/MessageTrackingLogGUI.ps1 @@ -0,0 +1,507 @@ +锘縡unction GenerateForm { +############################################################################################################### +# Version History: +# +# 1.1 - 18/07/2012 : https://gallery.technet.microsoft.com/office/MessageTrackingLog-search-72a5dbc7 +# 2.0 - 12/19/2015 : Nicolas PRIGENT - www.get-cmd.com +# New features : search by subject / send results by email / export results in csv file +# Existing feature corrected : search by event ID did not work. Global variable was not created. +# Code optimized +# +# Description: +# This script searches the MessageTrackingLog in your Organization. +# You have to enter valid From Addr or To Addr or the subject and select the date. +# You can also select a specific event ID. You can leave the blank entry to search the whole TrackingLog. +# +############################################################################################################### + +#region Import the Assemblies +[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null +[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null +#endregion + +#region Generated Form Objects +$formTrackLog = New-Object System.Windows.Forms.Form +$labEventID = New-Object System.Windows.Forms.Label +$comboBoxEventID = New-Object System.Windows.Forms.ComboBox +$labEndDate = New-Object System.Windows.Forms.Label +$labStartDate = New-Object System.Windows.Forms.Label +$labFrom = New-Object System.Windows.Forms.Label +$dgResults = New-Object System.Windows.Forms.DataGrid +$dateTimePickerEnd = New-Object System.Windows.Forms.DateTimePicker +$dateTimePickerStart = New-Object System.Windows.Forms.DateTimePicker +$txtBoxRecipients = New-Object System.Windows.Forms.TextBox +$txtBoxSenders = New-Object System.Windows.Forms.TextBox +$buttonGo = New-Object System.Windows.Forms.Button +$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState +$labTo = New-Object System.Windows.Forms.Label +$labSubject = New-Object System.Windows.Forms.Label +$txtBoxSubject = New-Object System.Windows.Forms.TextBox +$txtBoxMail = New-Object System.Windows.Forms.TextBox +$txtBoxCSV = New-Object System.Windows.Forms.TextBox +$chkBoxCSV = New-Object System.Windows.Forms.CheckBox +$chkBoxMail = New-Object System.Windows.Forms.CheckBox +$txtBoxFromMail = New-Object System.Windows.Forms.TextBox +$txtBoxToMail = New-Object System.Windows.Forms.TextBox +$txtBoxCSV = New-Object System.Windows.Forms.TextBox +#endregion Generated Form Objects + +#---------------------------------------------- +#Generated Event Script Blocks +#---------------------------------------------- + +Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010 + +$processData= +{ + #This section determine the date and puts it in a working format + $array = New-Object System.Collections.ArrayList + $date1 = get-Date -date $dateTimePickerStart.value -uformat "%m/%d/%Y 00:00:01" + $date3 = [System.DateTime]$date1 + $date2 = get-Date -date $dateTimePickerEnd.value -uformat "%m/%d/%Y 23:59:59" + $date4 = [System.DateTime]$date2 + $Sort = "TimeStamp" + + if ($ChoiceEventID -eq "BADMAIL" -or $ChoiceEventID -eq "DEFER" -or $ChoiceEventID -eq "DELIVER" -or $ChoiceEventID -eq "SEND" -or $ChoiceEventID -eq "DSN" -or $ChoiceEventID -eq "FAIL" -or $ChoiceEventID -eq "POISONMESSAGE" -or $ChoiceEventID -eq "RECEIVE" -or $ChoiceEventID -eq "REDIRECT" -or $ChoiceEventID -eq "RESOLVE" -or $ChoiceEventID -eq "SUBMIT" -or $ChoiceEventID -eq "TRANSFER" -or $ChoiceEventID -eq "EXPAND") + { + $EventID = $ChoiceEventID + } + else + { + $EventID = "" + } + + if ( $EventID -ne "") + { + if(($txtBoxRecipients.text -eq "") -and ($txtBoxSenders.text -eq "") -and ($txtBoxSubject.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -start $date3 -end $date4 -EventID $EventID -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif (($txtBoxRecipients.text -eq "") -and ($txtBoxSenders.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -MessageSubject $txtBoxSubject.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif (($txtBoxSubject.text -eq "") -and ($txtBoxSenders.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif (($txtBoxSubject.text -eq "") -and ($txtBoxRecipients.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -Sender $txtBoxSenders.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxRecipients.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Sender $txtBoxSenders.text -MessageSubject $txtBoxSubject.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxSenders.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -MessageSubject $txtBoxSubject.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxSubject.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -sender $txtBoxSenders.text -EventID $EventID -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + } + else + { + if(($txtBoxRecipients.text -eq "") -and ($txtBoxSenders.text -eq "") -and ($txtBoxSubject.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif (($txtBoxRecipients.text -eq "") -and ($txtBoxSenders.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -MessageSubject $txtBoxSubject.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif (($txtBoxSubject.text -eq "") -and ($txtBoxSenders.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject,EventID, serverhostname | sort $sort + } + elseif (($txtBoxSubject.text -eq "") -and ($txtBoxRecipients.text -eq "")) + { + $ausgabe = Get-MessageTrackingLog -Sender $txtBoxSenders.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxRecipients.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Sender $txtBoxSenders.text -MessageSubject $txtBoxSubject.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxSenders.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -MessageSubject $txtBoxSubject.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + elseif ($txtBoxSubject.text -eq "") + { + $ausgabe = Get-MessageTrackingLog -Recipients $txtBoxRecipients.text -sender $txtBoxSenders.text -start $date3 -end $date4 -resultsize unlimited | Select-object Timestamp, sender, @{Name='Recipients';Expression={[string]::join(";", ($_.Recipients))}}, messagesubject, EventID, serverhostname | sort $sort + } + } + + if ($ausgabe) { + $array.addrange($ausgabe) + $dgResults.datasource = $array + $array | export-csv "MessageTrackingGUI.log" + if ($chkBoxCSV.Checked) + { + $array | export-csv $txtBoxCSV.text + } + if ($chkBoxMail.Checked) + { + $Date = Get-Date + $SubjectDate = "Exchange Message Tracking " + $Date.Tostring('HH:mm-MM.dd.yyyy') + send-mailmessage -to $txtBoxToMail.text -from $txtBoxFromMail.text -subject $SubjectDate -body "Attached is the message tracking" -Attachments 'MessageTrackingGUI.log' -BodyAsHTML -SmtpServer $txtBoxMail.text + } + $formTrackLog.refresh() + } else { + write-host "No results found!" -ForegroundColor white -BackgroundColor Red + } +} + + +$handler_comboBoxEventID_SelectedIndexChanged= +{ +# Get the Event ID when item is selected + $Global:ChoiceEventID = $comboBoxEventID.selectedItem.ToString() +} + +$OnLoadForm_StateCorrection= +{#Correct the initial state of the form to prevent the .Net maximized form issue + $formTrackLog.WindowState = $InitialFormWindowState +} + +#---------------------------------------------- +#region Generated Form Code +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 550 +$System_Drawing_Size.Width = 1000 +$formTrackLog.ClientSize = $System_Drawing_Size +$formTrackLog.DataBindings.DefaultDataSourceUpdateMode = 0 +$formTrackLog.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0) +$formTrackLog.Name = "formTrackLog" +$formTrackLog.Text = "Message Tracking Log GUI - By Nicolas PRIGENT [www.get-cmd.com]" +$formTrackLog.add_Load($handler_formTrackLog_Load) + +$labEventID.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 570 +$System_Drawing_Point.Y = 5 +$labEventID.Location = $System_Drawing_Point +$labEventID.Name = "labEventID" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 23 +$System_Drawing_Size.Width = 60 +$labEventID.Size = $System_Drawing_Size +$labEventID.TabIndex = 18 +$labEventID.Text = "Event ID:" +$labEventID.add_Click($handler_labEventID_Click) + +$formTrackLog.Controls.Add($labEventID) + +$comboBoxEventID.DataBindings.DefaultDataSourceUpdateMode = 0 +$comboBoxEventID.FormattingEnabled = $True +$comboBoxEventID.Items.Add("")|Out-Null +$comboBoxEventID.Items.Add("SEND")|Out-Null +$comboBoxEventID.Items.Add("DELIVER")|Out-Null +$comboBoxEventID.Items.Add("RECEIVE")|Out-Null +$comboBoxEventID.Items.Add("FAIL")|Out-Null +$comboBoxEventID.Items.Add("DSN")|Out-Null +$comboBoxEventID.Items.Add("RESOLVE")|Out-Null +$comboBoxEventID.Items.Add("EXPAND")|Out-Null +$comboBoxEventID.Items.Add("REDIRECT")|Out-Null +$comboBoxEventID.Items.Add("TRANSFER")|Out-Null +$comboBoxEventID.Items.Add("SUBMIT")|Out-Null +$comboBoxEventID.Items.Add("POISONMESSAGE")|Out-Null +$comboBoxEventID.Items.Add("DEFER")|Out-Null +$System_Drawing_PointComboEVentID = New-Object System.Drawing.Point +$System_Drawing_PointComboEVentID.X = 630 +$System_Drawing_PointComboEVentID.Y = 3 +$comboBoxEventID.Location = $System_Drawing_PointComboEVentID +$comboBoxEventID.Name = "comboBoxEventID" +$System_Drawing_SizeComboEVentID = New-Object System.Drawing.Size +$System_Drawing_SizeComboEVentID.Height = 21 +$System_Drawing_SizeComboEVentID.Width = 121 +$comboBoxEventID.Size = $System_Drawing_SizeComboEVentID +$comboBoxEventID.TabIndex = 17 +$comboBoxEventID.add_SelectedIndexChanged($handler_comboBoxEventID_SelectedIndexChanged) +$formTrackLog.Controls.Add($comboBoxEventID) + +$labEndDate.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 300 +$System_Drawing_Point.Y = 33 +$labEndDate.Location = $System_Drawing_Point +$labEndDate.Name = "labEndDate" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 54 +$labEndDate.Size = $System_Drawing_Size +$labEndDate.TabIndex = 12 +$labEndDate.Text = "End" + +$formTrackLog.Controls.Add($labEndDate) + +$labStartDate.DataBindings.DefaultDataSourceUpdateMode = 0 + +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 300 +$System_Drawing_Point.Y = 5 +$labStartDate.Location = $System_Drawing_Point +$labStartDate.Name = "labStartDate" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 54 +$labStartDate.Size = $System_Drawing_Size +$labStartDate.TabIndex = 11 +$labStartDate.Text = "Start" +$labStartDate.add_Click($handler_labStartDate_Click) + +$formTrackLog.Controls.Add($labStartDate) + +$dgResults.AllowSorting = $true +$dgResults.Anchor = 15 +$dgResults.DataBindings.DefaultDataSourceUpdateMode = 0 +$dgResults.DataMember = "" +$dgResults.HeaderForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0) +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 9 +$System_Drawing_Point.Y = 108 +$dgResults.Location = $System_Drawing_Point +$dgResults.Name = "dgResults" +$dgResults.PreferredColumnWidth = 200 +$dgResults.ReadOnly = $True +$dgResults.RowHeadersVisible = $false +$dgResults.RowHeaderWidth = 60 +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 500 +$System_Drawing_Size.Width = 990 +$dgResults.Size = $System_Drawing_Size +$dgResults.TabIndex = 9 +$dgResults.add_Navigate($handler_dgResults_Navigate) + +$formTrackLog.Controls.Add($dgResults) + +$dateTimePickerEnd.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 360 +$System_Drawing_Point.Y = 33 +$dateTimePickerEnd.Location = $System_Drawing_Point +$dateTimePickerEnd.Name = "dateTimePicker2" +$System_Drawing_SizeEnd = New-Object System.Drawing.Size +$System_Drawing_SizeEnd.Height = 20 +$System_Drawing_SizeEnd.Width = 200 +$dateTimePickerEnd.Size = $System_Drawing_SizeEnd +$dateTimePickerEnd.TabIndex = 8 + +$formTrackLog.Controls.Add($dateTimePickerEnd) + +$dateTimePickerStart.CustomFormat = "MM/DD/YYYY 00:00:01" +$dateTimePickerStart.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 360 +$System_Drawing_Point.Y = 3 +$dateTimePickerStart.Location = $System_Drawing_Point +$dateTimePickerStart.Name = "dateTimePicker1" +$System_Drawing_SizeStart = New-Object System.Drawing.Size +$System_Drawing_SizeStart.Height = 20 +$System_Drawing_SizeStart.Width = 200 +$dateTimePickerStart.Size = $System_Drawing_SizeStart +$dateTimePickerStart.TabIndex = 7 + +$formTrackLog.Controls.Add($dateTimePickerStart) + +$txtBoxRecipients.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 40 +$System_Drawing_Point.Y = 30 +$txtBoxRecipients.Location = $System_Drawing_Point +$txtBoxRecipients.Name = "txtBoxRecipients" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 250 +$txtBoxRecipients.Size = $System_Drawing_Size +$txtBoxRecipients.TabIndex = 4 +$txtBoxRecipients.Text = "" +$formTrackLog.Controls.Add($txtBoxRecipients) + +$txtBoxSubject.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 65 +$System_Drawing_Point.Y = 65 +$txtBoxSubject.Location = $System_Drawing_Point +$txtBoxSubject.Name = "txtBoxSubject" +$System_Drawing_SizeSubject = New-Object System.Drawing.Size +$System_Drawing_SizeSubject.Height = 20 +$System_Drawing_SizeSubject.Width = 495 +$txtBoxSubject.Size = $System_Drawing_SizeSubject +$txtBoxSubject.TabIndex = 4 +$txtBoxSubject.Text = "" +$formTrackLog.Controls.Add($txtBoxSubject) + +$txtBoxSenders.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 40 +$System_Drawing_Point.Y = 3 +$txtBoxSenders.Location = $System_Drawing_Point +$txtBoxSenders.Name = "txtBoxSenders" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 250 +$txtBoxSenders.Size = $System_Drawing_Size +$txtBoxSenders.TabIndex = 3 +$txtBoxSenders.Text = "" +$formTrackLog.Controls.Add($txtBoxSenders) + +$buttonGo.DataBindings.DefaultDataSourceUpdateMode = 0 +$buttonGo.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0) + +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 755 +$System_Drawing_Point.Y = 3 +$buttonGo.Location = $System_Drawing_Point +$buttonGo.Name = "button1" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 25 +$System_Drawing_Size.Width = 240 +$buttonGo.Size = $System_Drawing_Size +$buttonGo.TabIndex = 1 +$buttonGo.Text = ">>> Run <<<" +$buttonGo.UseVisualStyleBackColor = $True +$buttonGo.add_Click($processData) + +$formTrackLog.Controls.Add($buttonGo) + +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 3 +$System_Drawing_Point.Y = 5 +$labFrom.Location = $System_Drawing_Point +$labFrom.Name = "labFrom" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 54 +$labFrom.Size = $System_Drawing_Size +$labFrom.TabIndex = 11 +$labFrom.Text = "From:" + +$formTrackLog.Controls.Add($labFrom) + +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 3 +$System_Drawing_Point.Y = 32 +$labTo.Location = $System_Drawing_Point +$labTo.Name = "labTo" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 54 +$labTo.Size = $System_Drawing_Size +$labTo.TabIndex = 11 +$labTo.Text = "To:" +$formTrackLog.Controls.Add($labTo) + +$System_Drawing_Point = New-Object System.Drawing.Point +$System_Drawing_Point.X = 3 +$System_Drawing_Point.Y = 67 +$labSubject.Location = $System_Drawing_Point +$labSubject.Name = "labSubject" +$System_Drawing_Size = New-Object System.Drawing.Size +$System_Drawing_Size.Height = 20 +$System_Drawing_Size.Width = 54 +$labSubject.Size = $System_Drawing_Size +$labSubject.TabIndex = 11 +$labSubject.Text = "Subject:" +$formTrackLog.Controls.Add($labSubject) + +$System_Drawing_SizeCSV = New-Object System.Drawing.Size +$System_Drawing_SizeCSV.Width = 84 +$System_Drawing_SizeCSV.Height = 24 +$chkBoxCSV.Size = $System_Drawing_SizeCSV +$chkBoxCSV.TabIndex = 1 +$chkBoxCSV.Text = "Export CSV" +$System_Drawing_PointCSV = New-Object System.Drawing.Point +$System_Drawing_PointCSV.X = 570 +$System_Drawing_PointCSV.Y = 64 +$chkBoxCSV.Location = $System_Drawing_PointCSV +$chkBoxCSV.DataBindings.DefaultDataSourceUpdateMode = 0 +$chkBoxCSV.Name = "chkBoxCSV" +$formTrackLog.Controls.Add($chkBoxCSV) + +$System_Drawing_SizeMail = New-Object System.Drawing.Size +$System_Drawing_SizeMail.Width = 90 +$System_Drawing_SizeMail.Height = 24 +$chkBoxMail.Size = $System_Drawing_SizeMail +$chkBoxMail.TabIndex = 1 +$chkBoxMail.Text = "Send by mail" +$System_Drawing_PointMail = New-Object System.Drawing.Point +$System_Drawing_PointMail.X = 570 +$System_Drawing_PointMail.Y = 34 +$chkBoxMail.Location = $System_Drawing_PointMail +$chkBoxMail.DataBindings.DefaultDataSourceUpdateMode = 0 +$chkBoxMail.Name = "chkBoxMail" +$formTrackLog.Controls.Add($chkBoxMail) + +$txtBoxMail.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_PointMail = New-Object System.Drawing.Point +$System_Drawing_PointMail.X = 660 +$System_Drawing_PointMail.Y = 34 +$txtBoxMail.Location = $System_Drawing_PointMail +$txtBoxMail.Name = "txtBoxMail" +$System_Drawing_SizeMail = New-Object System.Drawing.Size +$System_Drawing_SizeMail.Height = 20 +$System_Drawing_SizeMail.Width = 110 +$txtBoxMail.Size = $System_Drawing_SizeMail +$txtBoxMail.TabIndex = 3 +$txtBoxMail.Text = "SMTP Server" +$formTrackLog.Controls.Add($txtBoxMail) + +$txtBoxFromMail.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_PointFromMail = New-Object System.Drawing.Point +$System_Drawing_PointFromMail.X = 775 +$System_Drawing_PointFromMail.Y = 34 +$txtBoxFromMail.Location = $System_Drawing_PointFromMail +$txtBoxFromMail.Name = "txtBoxFromMail" +$System_Drawing_SizeFromMail = New-Object System.Drawing.Size +$System_Drawing_SizeFromMail.Height = 20 +$System_Drawing_SizeFromMail.Width = 110 +$txtBoxFromMail.Size = $System_Drawing_SizeFromMail +$txtBoxFromMail.TabIndex = 3 +$txtBoxFromMail.Text = "From" +$formTrackLog.Controls.Add($txtBoxFromMail) + +$txtBoxToMail.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_PointToMail = New-Object System.Drawing.Point +$System_Drawing_PointToMail.X = 890 +$System_Drawing_PointToMail.Y = 34 +$txtBoxToMail.Location = $System_Drawing_PointToMail +$txtBoxToMail.Name = "txtBoxToMail" +$System_Drawing_SizeToMail = New-Object System.Drawing.Size +$System_Drawing_SizeToMail.Height = 20 +$System_Drawing_SizeToMail.Width = 110 +$txtBoxToMail.Size = $System_Drawing_SizeToMail +$txtBoxToMail.TabIndex = 3 +$txtBoxToMail.Text = "To" +$formTrackLog.Controls.Add($txtBoxToMail) + +$txtBoxCSV.DataBindings.DefaultDataSourceUpdateMode = 0 +$System_Drawing_PointTXTCSV = New-Object System.Drawing.Point +$System_Drawing_PointTXTCSV.X = 660 +$System_Drawing_PointTXTCSV.Y = 65 +$txtBoxCSV.Location = $System_Drawing_PointTXTCSV +$txtBoxCSV.Name = "txtBoxCSV" +$System_Drawing_SizeTXTCSV = New-Object System.Drawing.Size +$System_Drawing_SizeTXTCSV.Height = 20 +$System_Drawing_SizeTXTCSV.Width = 250 +$txtBoxCSV.Size = $System_Drawing_SizeTXTCSV +$txtBoxCSV.TabIndex = 3 +$txtBoxCSV.Text = "Path to csv file" +$formTrackLog.Controls.Add($txtBoxCSV) + + +#endregion Generated Form Code + +#Save the initial state of the form +$InitialFormWindowState = $formTrackLog.WindowState +#Init the OnLoad event to correct the initial state of the form +$formTrackLog.add_Load($OnLoadForm_StateCorrection) +#Show the Form +$formTrackLog.ShowDialog()| Out-Null + +} #End Function + +#Call the Function +GenerateForm diff --git a/powershell/exchange/Set-Webserver.ps1 b/powershell/exchange/Set-Webserver.ps1 new file mode 100644 index 0000000..bd12f59 --- /dev/null +++ b/powershell/exchange/Set-Webserver.ps1 @@ -0,0 +1,252 @@ +<# + .SYNOPSIS + Configures IIS log file settings + + Thomas Stensitzki + + THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE + RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. + + Version 1.1, 2016-07-28 + + Ideas, comments and suggestions to support@granikos.eu + + Some parts (c) Michel de Rooij, michel@eightwone.com + + .LINK + http://www.granikos.eu/en/scripts + + + .DESCRIPTION + This script reconfigures the IIS log folder to target a different folder besides the + default C:\inetpub\logs folder. Additionally the log settings can be adjusted as well. + The script changes the default log file location and settings on a server level. By + default the settings are inherited by websites. If manual changes have been made on + a webite level, not all settings will be inherited. + + .NOTES + Requirements + - Windows Server 2008 R2 SP1, Windows Server 2012 or Windows Server 2012 R2 + + + Revision History + -------------------------------------------------------------------------------- + 1.0 Initial community release + 1.1 PowerShell hygiene applied, some typo fixes + + + .PARAMETER LogFolderPath + New IIS log folder path, i.e. D:\IISLogs. Default is an empty string. + + .PARAMETER LogFilePeriod + Log file period (interval), Hourly|Daily|Weekly|Monthly|MaxSize + MaxSize configuration not yet implemented + + .PARAMETER LocalTimeRollover + Boolean parameter indicating, if the local time shall be used for filenames and rollover + Default $FALSE + + .EXAMPLE + Change the IIS log file location to D:\IISLogs + .\Set-Webserver.ps1 -LogFolderPath D:\IISLogs + + .EXAMPLE + Change the IIS log period to an hourly period + .\Set-Webserver.ps1 -LogFilePeriod Hourly + + .EXAMPLE + Use the local time for filenames and log file rollover + .\Set-Webserver.ps1 -LocalTimeRollover $true + +#> + + +Param( + [parameter(Position=0,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='New IIS log folder path, i.e. D:\IISLogs')] + [string]$LogFolderPath = '', + [parameter(Position=1,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='Log file period (Hourly|Daily|Weekly|Monthly|MaxSize)')] + [string]$LogFilePeriod = '', + [parameter(Position=2,Mandatory=$false,ValueFromPipeline=$false,HelpMessage='$true/$false indicating, if the local time shall be used for filenames and rollover')] + [bool]$LocalTimeRollover=$false +) + +process{ + + # log file property settings + $lfpDirectory = 'directory' + $lfpPeriod = 'period' + $lfpLocalTimeRollover = 'localTimeRollover' + + # Check if folder exists, otherwise create folder + function Create-Folder + { + param + ( + [string] + $folderPath + ) + + Write-Verbose "Evaluating IIS folder path: $folderPath" + + if(-not ($folderPath -eq '')) + { + if(-not (Test-Path $folderPath)) + { + Write-Host "Creating IIS log folder path: $folderPath" + + New-Item -Path $folderPath -ItemType directory | Out-Null + } + else + { + Write-Host "Folder $folderPath already exsists" + } + } + } + + # Change IIS log file setting + function ChangeIisLogSetting + { + param + ( + [string] + $settingName, + + [string] + $settingValue + ) + + try + { + Write-Verbose "Configuring IIS log setting $settingsName to value $settingsValue" + + $logConfig = @{$settingName=$settingValue} + + Set-WebConfigurationProperty 'system.applicationHost/sites/siteDefaults' -Name logFile -Value $logConfig + } + catch [system.exception] + { + Write-Host 'An error occured while trying to write IIS settings. Please check permissions of your account and ensure that PowerShell is running from an elevated prompt.' -ForegroundColor Red + } + } + + # Change IIS default log location and other IIS log settings + function ChangeIisLogPath + { + param + ( + [string] + $folderPath + ) + + Write-Verbose "IIS Log Folder Path to configure: $folderPath" + Write-Host "Configuring IIS default log location to '$folderPath'" + + if(Test-Path $folderPath) + { + ChangeIisLogSetting $lfpDirectory $folderPath + } + else + { + Write-Host "New IIS log folder $folderPath does not exist. IIS log file folder configuration has not been changed" -ForegroundColor Red + } + } + + # Change IIS log period + function ChangeIisLogPeriod + { + param + ( + [string] + $logPeriod + ) + + if($AllowedLogFilePeriod -contains $logPeriod) + { + Write-Verbose "Changing IIS log periog to: $logPeriod" + + ChangeIisLogSetting $lfpPeriod $logPeriod + } + } + + # Change IIS LocalTimeRollover setting + function ChangeLocalTimeRollover + { + param + ( + [bool] + $logLocalTimeRollover + ) + + Write-Verbose "Changing IIS log local time rollover to: $logLocalTimeRollover" + + ChangeIisLogSetting $lfpLocalTimeRollover $logLocalTimeRollover + } + + + function CheckWindowsFeature + { + param + ( + [string] + $MajorOSVersion + ) + + $featureInstalled = $false + + If ($MajorOSVersion -eq '6.1') + { + Import-Module ServerManager + If(!(Get-Module ServerManager)) + { + Write-Error 'Problem loading ServerManager module' + Exit 'ServerManager module could not be loaded!' + } + } + + Write-Verbose "Checking, if Windows Feature 'Web-Server' is installed" + + $feature = Get-WindowsFeature Web-Server + $featureInstalled = [bool]($feature.Installed) + + Write-Verbose "Feature 'Web-Server' installed: $featureInstalled" + + return( $featureInstalled ) + } + + ## Main + Write-Verbose 'Script started' + + $MajorOSVersion= [string](Get-WmiObject Win32_OperatingSystem | Select-Object Version | Select-Object @{n='Major';e={($_.Version.Split('.')[0]+'.'+$_.Version.Split('.')[1])}}).Major + $AllowedLogFilePeriod = @('Hourly','Daily','Weekly','Monthly') # MaxSize not yet implemented + + if( CheckWindowsFeature($MajorOSVersion) ) + { + + Write-Verbose 'Configuring IIS log file settings' + + if($LogFolderPath -ne '') + { + # Create IIS Log File Folder, independent from server role + Create-Folder $LogFolderPath + + #Change log file settings + ChangeIisLogPath $LogFolderPath $LogFilePeriod + } + + if($LogFilePeriod -ne '') + { + ChangeIisLogPeriod $LogFilePeriod + } + + if($LocalTimeRollover -ne $null) + { + ChangeLocalTimeRollover $LocalTimeRollover + } + } + else + { + Write-Host 'IIS is currently not installed. Either add the windows feature manually or install Exchange first and adjust the IIS log file location afterwards.' -ForegroundColor Red + } + + Write-Verbose 'Script ended' +} #Process \ No newline at end of file diff --git a/powershell/exchange/enable-ad-user-mailbox.ps1 b/powershell/exchange/enable-ad-user-mailbox.ps1 new file mode 100644 index 0000000..521d5ec --- /dev/null +++ b/powershell/exchange/enable-ad-user-mailbox.ps1 @@ -0,0 +1,31 @@ + +. 'C:\Program Files\Microsoft\Exchange Server\V15\bin\RemoteExchange.ps1' +Connect-ExchangeServer -auto + +$LogFile="C:\add_ad_user_mail-$((Get-Date).ToString('yyyy-MM-dd')).log" +Start-Transcript -path $LogFile -append + +Write-Output "[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fZ'))] [Start]" + +Import-module activedirectory +# $since=(Get-Date).AddDays(-4).ToUniversalTime().ToString('yyyyMMddHHmmss.fZ') +$since=(Get-Date).AddMinutes(-20).ToUniversalTime().ToString('yyyyMMddHHmmss.fZ') +$users=Get-ADUser -LDAPfilter "(&(objectCategory=person)(objectClass=user)(Name=*)(!(!UserPrincipalName=*))(whenCreated>=$since))" -searchBase 'CN=Users,DC=bloks,DC=local' + +foreach($user in $users) +{ + Write-Output "[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fZ'))] [User] $($user.SamAccountName)" + if (Get-Mailbox -Identity $user.SamAccountName 2>$null) + { + Write-Output "[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fZ'))] [User] $($user.SamAccountName): It's alive" + } + else + { + Write-Output "[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fZ'))] [User] $($user.SamAccountName): enable mailbox" + Enable-Mailbox -Identity $user.SamAccountName + } +} + +Write-Output "[$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fZ'))] [End]" + +Stop-Transcript \ No newline at end of file diff --git a/shell/get_proc_mem.sh b/shell/get_proc_mem.sh index 538ad8c..283adae 100644 --- a/shell/get_proc_mem.sh +++ b/shell/get_proc_mem.sh @@ -32,6 +32,164 @@ COLOR_GRAY="${COLOR_GRAY:-\e[1;90m}" COLOR_OFF="${COLOR_OFF:-\e[0m}" NOCOLOR="${NOCOLOR:-false}" +time_data="" +mem_total_data="" +mem_free_data="" +mem_available_data="" +mem_rss_data="" +mem_pss_data="" +mem_uss_data="" +cpu_used_data="" + +trap trap::info 1 2 3 15 EXIT + +###################################################################################################### +# function +###################################################################################################### + +function trap::info() { + cat << EOF > pid_${PID}_line.json +option = { + title: { + text: '鍐呭瓨鐩戞帶', + subtext: 'PID: ${PID}' + }, + tooltip: { + trigger: 'axis' + }, + legend: { + data: ['mem_total', 'mem_free', 'mem_available', 'pid_mem_rss', 'pid_mem_pss', 'pid_mem_uss', 'pid_cpu_used'] + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + toolbox: { + feature: { + saveAsImage: {} + } + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: [${time_data/,}] + }, + yAxis: [ + { + type: "value", + name: "鍐呭瓨 / MB", + nameLocation: 'center', + nameGap: 45 + }, + { + type: "value", + name: "CPU浣跨敤鐜 / %", + nameLocation: 'center', + nameGap: 45 + } + ], + series: [ + { + name: 'mem_total', + type: 'line', + smooth: true, + data: [${mem_total_data/,}], + markPoint: { + data: [{ + name: '鏈澶у', + type: 'max' + }] + } + }, + { + name: 'mem_free', + type: 'line', + smooth: true, + data: [${mem_free_data/,}], + markPoint: { + data: [{ + name: '鏈灏忓', + type: 'min' + }] + } + }, + { + name: 'mem_available', + type: 'line', + smooth: true, + data: [${mem_available_data/,}], + markPoint: { + data: [{ + name: '鏈灏忓', + type: 'min' + }] + } + }, + { + name: 'pid_mem_rss', + type: 'line', + smooth: true, + data: [${mem_rss_data/,}], + markPoint: { + data: [{ + name: '鏈澶у', + type: 'max' + }] + } + }, + { + name: 'pid_mem_pss', + type: 'line', + smooth: true, + data: [${mem_pss_data/,}], + markPoint: { + data: [{ + name: '鏈澶у', + type: 'max' + }] + } + }, + { + name: 'pid_mem_uss', + type: 'line', + smooth: true, + data: [${mem_uss_data/,}], + markPoint: { + data: [{ + name: '鏈澶у', + type: 'max' + }] + } + }, + { + name: 'pid_cpu_used', + type: 'line', + smooth: true, + yAxisIndex: 1, + data: [${cpu_used_data/,}], + markPoint: { + data: [{ + name: '鏈澶у', + type: 'max' + }] + } + } + ] +}; +EOF + echo -e " +\n${COLOR_GREEN} + Config File: pid_${PID}_line.json + GO TO URL: https://echarts.apache.org/next/examples/en/editor.html +${COLOR_OFF} + " + trap '' EXIT + exit + +} + ###################################################################################################### # function ###################################################################################################### @@ -43,6 +201,7 @@ function get::meminfo() { pid_smaps=$(cat /proc/${PID}/smaps) [ "$pid_smaps" == "" ] && { echo -e "${COLOR_RED}[Error]${COLOR_OFF} /proc/${PID}/smaps is empty!"; exit 1; } + cpu_used=$(ps -opcpu= -p "${PID}") mem_info=$(cat /proc/meminfo) mem_total=$(printf "%s" "${mem_info}"| awk '/^MemTotal:/ {print $2}') @@ -77,7 +236,22 @@ function get::meminfo_loop() { get::pidinfo while [ $count -lt $RETRIES ] ; do get::meminfo - echo -e "Date: $(date +'%Y-%m-%d %T') ${COLOR_PURPLE}MemTotal: $((mem_total/1024))MB${COLOR_OFF} ${COLOR_GREEN}MemFree: $((mem_free/1024))MB${COLOR_OFF} ${COLOR_BLUE}MemAvailable: $((mem_available/1024))MB${COLOR_OFF} ${COLOR_YELLOW}RSS: $((${rss}/1024))MB${COLOR_OFF} ${COLOR_CYAN}PSS: $((${pss}/1024))MB${COLOR_OFF} ${COLOR_RED}USS: $(( (${private_clean} + ${private_dirty}) /1024 ))MB${COLOR_OFF}" + d=$(date +'%Y-%m-%d %T') + mem_total=$((mem_total/1024)) + mem_free=$((mem_free/1024)) + mem_available=$((mem_available/1024)) + mem_rss=$((${rss}/1024)) + mem_pss=$((${pss}/1024)) + mem_uss=$(( (${private_clean} + ${private_dirty}) /1024 )) + echo -e "Date: ${d} ${COLOR_PURPLE}MemTotal: ${mem_total}MB${COLOR_OFF} ${COLOR_GREEN}MemFree: ${mem_free}MB${COLOR_OFF} ${COLOR_BLUE}MemAvailable: ${mem_available}MB${COLOR_OFF} ${COLOR_YELLOW}RSS: ${mem_rss}MB${COLOR_OFF} ${COLOR_CYAN}PSS: ${mem_pss}MB${COLOR_OFF} ${COLOR_RED}USS: ${mem_uss}MB${COLOR_OFF} CPU: ${cpu_used}%" + time_data="${time_data},'${d}'" + mem_total_data="${mem_total_data},'${mem_total}'" + mem_free_data="${mem_free_data},'${mem_free}'" + mem_available_data="${mem_available_data},'${mem_available}'" + mem_rss_data="${mem_rss_data},'${mem_rss}'" + mem_uss_data="${mem_uss_data},'${mem_uss}'" + mem_pss_data="${mem_pss_data},'${mem_pss}'" + cpu_used_data="${cpu_used_data},'${cpu_used}'" sleep $WAIT count=$(($count + 1)) done diff --git a/shell/k8s/k8s-app-info.sh b/shell/k8s/k8s-app-info.sh index 0f68a75..3a37a72 100644 --- a/shell/k8s/k8s-app-info.sh +++ b/shell/k8s/k8s-app-info.sh @@ -61,7 +61,7 @@ function get::selector { echo -ne "Get Selector" if [[ "${SELECTOR}" == "" ]]; then selflink=$(kubectl -n $NAMESPACE get deployment $APPNAME -o yaml --ignore-not-found 2>/dev/null | awk '/selfLink:/ {print $2}') - SELECTOR=$(kubectl get --raw "${selflink}/scale" 2>/dev/null | sed 's/.*selector":"\(.*\)".*/\1/g') + SELECTOR=$(kubectl get --raw "${selflink}/scale" 2>/dev/null | sed 's/.*[S|s]elector":"\(.*\)".*/\1/g') fi if [[ "${SELECTOR}" == "" ]]; then