├── EndScript.ps1 ├── GlobalVariables.ps1 ├── Header.jpg ├── Headers ├── Hand.jpg ├── PowerCLIMan.jpg └── exchange.png ├── Plugins ├── 00 1st Plugin - Select Report Header Image.ps1 ├── 10 Exchange 20xx Load Snapin.ps1 ├── 11 Exchange 20xx Basic Server Information.ps1 ├── 12 Exchange 20xx Drive Details.ps1 ├── 13 Exchange 2010 Database Availablility Groups.ps1 ├── 14 Exchange 20xx DB Statistics.ps1 ├── 15 Exchange 2010 DB Status.ps1 ├── 16 Exchange 2010 Active DB not on Preferred Server .ps1 ├── 16.5 Exchange 20xx Mailbox VSS Writer Health.ps1 ├── 17 Exchange 20xx MAPI Connectivity.ps1 ├── 18 Exchange 20xx PF Statistics.ps1 ├── 19 Exchange 20xx Hub Transport Message Queues.ps1 ├── 20 Exchange 20xx Largest Mailboxes.ps1 ├── 21 Exchange 20xx Largest Dumpster.ps1 ├── 22 Exchange 20xx Largest Total Size.ps1 ├── 22.5 Exchange 20xx Mailboxes with High Item Counts.ps1 ├── 23 Exchange 20xx Services.ps1 ├── 24 Exchange 20xx Event Logs.ps1 ├── 25 Exchange 20xx Uncleared Move Requests.ps1 ├── 26 Exchange 20xx Disconnected Mailboxes.ps1 ├── 27 Exchange 20xx Forwarders.ps1 ├── Exchange 20xx Plugins Readme.txt ├── Plugins for Exchange not up to date or installed.ps1 └── Report on Plugins.ps1 ├── README.md ├── Select-Plugins.ps1 ├── vCheck.ps1 └── vCheckUtils.ps1 /EndScript.ps1: -------------------------------------------------------------------------------- 1 | # Everything in this script will run at the end of vCheck 2 | -------------------------------------------------------------------------------- /GlobalVariables.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/GlobalVariables.ps1 -------------------------------------------------------------------------------- /Header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Header.jpg -------------------------------------------------------------------------------- /Headers/Hand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Headers/Hand.jpg -------------------------------------------------------------------------------- /Headers/PowerCLIMan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Headers/PowerCLIMan.jpg -------------------------------------------------------------------------------- /Headers/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Headers/exchange.png -------------------------------------------------------------------------------- /Plugins/00 1st Plugin - Select Report Header Image.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/00 1st Plugin - Select Report Header Image.ps1 -------------------------------------------------------------------------------- /Plugins/10 Exchange 20xx Load Snapin.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/10 Exchange 20xx Load Snapin.ps1 -------------------------------------------------------------------------------- /Plugins/11 Exchange 20xx Basic Server Information.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/11 Exchange 20xx Basic Server Information.ps1 -------------------------------------------------------------------------------- /Plugins/12 Exchange 20xx Drive Details.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/12 Exchange 20xx Drive Details.ps1 -------------------------------------------------------------------------------- /Plugins/13 Exchange 2010 Database Availablility Groups.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 2010 DAG Info" 2 | $Header = "Exchange 2010 DAG Info" 3 | $Comments = "Exchange 2010 Database Availability Group Info" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.1 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Start of Settings 10 | # End of Settings 11 | 12 | # Changelog 13 | ## 2.0 : Sort by Server, Name 14 | ## Do nothing if running under Exchange 2007 Command Shell 15 | ## 2.1 : Add Server name filter, fix sort key 16 | 17 | function Get-DatabaseAvailabilityGroupDetails { 18 | $DAGs = Get-DatabaseAvailabilityGroup -Status | 19 | Where { $_.Servers -match $exServerFilter } | 20 | Sort Name 21 | If ($DAGs -ne $null) { 22 | foreach ($DAG in $DAGs) { 23 | $DAGServers = "" 24 | $ds = $DAG.Servers | Sort Name 25 | ForEach ($dagsrv in $ds) { $DAGServers += $dagsrv.Name + ", " } 26 | $OpServers = "" 27 | $os = $DAG.OperationalServers | Sort Name 28 | ForEach ($dagsrv in $os) { $OpServers += $dagsrv.Name + ", " } 29 | New-Object PSObject -Property @{ 30 | Name = $DAG.Name 31 | Servers = $DAGServers -replace '(.*), $','$1' 32 | "Operational Servers" = $OpServers -replace '(.*), $','$1' 33 | "Witness Server" = $DAG.WitnessServer 34 | "Witness Dir" = $DAG.WitnessDirectory 35 | "Alt Witness Server" = $DAG.AlternateWitnessServer 36 | "Alt Witness Dir" = $DAG.AlternateWitnessDirectory 37 | } 38 | } 39 | } 40 | } 41 | 42 | if ($2010snapin) { 43 | $dags=Get-DatabaseAvailabilityGroupDetails 44 | If ($dags -ne $null) { 45 | $dags | 46 | Select Name, 47 | Servers, 48 | "Operational Servers", 49 | "Witness Server", 50 | "Witness Dir", 51 | "Alt Witness Server", 52 | "Alt Witness Dir" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Plugins/14 Exchange 20xx DB Statistics.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 20xx DB Statistics" 2 | $Header = "Exchange Database Statistics" 3 | $Comments = "Exchange DB Statistics" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.2 7 | $PluginCategory = "Exchange2010" 8 | 9 | # based on http://www.mikepfeiffer.net/2010/03/exchange-2010-database-statistics-with-powershell/ 10 | 11 | # Start of Settings 12 | # End of Settings 13 | 14 | # Changelog 15 | ## 2.0 : Added Exchange 2007 support 16 | ## Sort by Server, Database 17 | ## Add last incremental backup date, Circular Logging 18 | ## Try to deal with unmounted databases sensibly 19 | ## 2.1 : Add Server name filter 20 | ## 2.2 : Add Exchange version 15 support 21 | 22 | function Get-MyDatabaseStatistics { 23 | $Databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue | 24 | Where { $_.Server -match $exServerFilter } | 25 | Sort Server, Name 26 | foreach($Database in $Databases) { 27 | If ($Database.DatabaseCreated) { 28 | $MBTot = 0 29 | $DelTot = 0 30 | $MBCount = 0 31 | $DBMounted = $Database.Mounted 32 | If ($DBMounted) { 33 | $MBCount = @(Get-MailboxStatistics -Database $Database).Count 34 | $MBSum = Get-MailboxStatistics -Database $Database | 35 | %{$_.TotalItemSize.value.ToBytes()} | 36 | Measure-Object -Sum 37 | $MBTot = $MBSum.Sum 38 | $DelSum = Get-MailboxStatistics -Database $Database | 39 | %{$_.TotalDeletedItemSize.value.ToBytes()} | 40 | Measure-Object -Sum 41 | $DelTot = $DelSum.Sum 42 | } 43 | If ($MBCount -eq 0) { 44 | $MBAvg = "" 45 | $DelAvg = "" 46 | } Else { 47 | $MBAvg = "{0:N3}" -f ($MBTot / $MBCount / 1MB) 48 | $DelAvg = "{0:N3}" -f ($DelTot / $MBCount / 1MB) 49 | } 50 | If ($Database.ExchangeVersion.ExchangeBuild.Major -eq 8) { 51 | # Exchange 2007 52 | # Use WMI (Based on code by Gary Siepser, http://bit.ly/kWWMb3) 53 | $DBSize = [long](get-wmiobject cim_datafile -computername $Database.Server.Name -filter ('name=''' + $Database.edbfilepath.pathname.replace("\","\\") + '''')).filesize 54 | if (!$DBSize) { 55 | [long]$DBSize = 0 56 | [long]$Whitespace = 0 57 | } else { 58 | $MailboxDeletedItemSizeB = $DelTot 59 | $MailBoxItemSizeB = $MBTot 60 | $Whitespace = ($DBSize - $MailboxItemSizeB - $MailboxDeletedItemSizeB) / 1GB 61 | if ($Whitespace -lt 0) { $Whitespace = 0 } 62 | } 63 | If ($DBMounted) { 64 | $Whitespace = "{0:N3}" -f $Whitespace 65 | } Else { 66 | $Whitespace = "" 67 | } 68 | } Elseif ($Database.ExchangeVersion.ExchangeBuild.Major -ge 14) { 69 | # Exchange 2010 70 | $DBSize = $Database.DatabaseSize.ToBytes() 71 | $Whitespace = $Database.AvailableNewMailboxSpace.ToBytes() / 1GB 72 | $Whitespace = "{0:N3}" -f $Whitespace 73 | } 74 | 75 | If (!$DBMounted) { 76 | $MBCount = "" 77 | } 78 | New-Object PSObject -Property @{ 79 | Server = $Database.Server.Name 80 | Database = $Database.Name 81 | "Mailbox Count" = $MBCount 82 | "Database Size (GB)" = "{0:N3}" -f ($DBSize / 1GB) 83 | "WhiteSpace (GB)" = $Whitespace 84 | "Avg Mailbox Size (MB)" = $MBAvg 85 | "Avg Del Items Size (MB)" = $DelAvg 86 | Mounted = $(If ($Database.Mounted) { "Yes" } Else { "No" }) 87 | "Circular Logging" = $(If ($Database.CircularLoggingEnabled) { "Yes" } Else { "No" }) 88 | "Last Full Backup" = $Database.LastFullBackup 89 | "Last Incremental Backup" = $Database.LastIncrementalBackup 90 | } 91 | } 92 | } 93 | } 94 | 95 | If ($2007Snapin -or $2010Snapin) { 96 | get-MyDatabaseStatistics | 97 | Sort Server, Database | 98 | Select Server, 99 | Database, 100 | "Mailbox Count", 101 | "Database Size (GB)", 102 | "Whitespace (GB)", 103 | "Avg Mailbox Size (MB)", 104 | "Avg Del Items Size (MB)", 105 | Mounted, 106 | "Circular Logging", 107 | "Last Full Backup", 108 | "Last Incremental Backup" 109 | } 110 | -------------------------------------------------------------------------------- /Plugins/15 Exchange 2010 DB Status.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 2010 DB Status" 2 | $Header = "Exchange 2010 DB Status" 3 | $Comments = "Exchange 2010 DB Status" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.2 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Start of Settings 10 | # End of Settings 11 | 12 | # Changelog 13 | ## 2.0 : Fixed Category 14 | ## Fixed to not run under Exchange 2007 Management Shell 15 | ## Sort by Server, Database 16 | ## 2.1 : Add Server name filter 17 | ## 2.2 : Allow Exchange version 15 18 | 19 | Function GetDBStatus() { 20 | $MBXservers=Get-MailboxServer -ErrorAction SilentlyContinue | 21 | Where { $_.AdminDisplayVersion -match "Version (14|15)" -and $_.Name -match $exServerFilter } | 22 | Sort Name | 23 | Select Name 24 | If ($MBXServers -ne $null) { 25 | ForEach ($Server in $MBXservers) { 26 | $ServerName=$Server.Name 27 | $Status = Get-MailboxDatabaseCopyStatus -server $ServerName 28 | foreach($State in $Status){ 29 | New-Object PSObject -Property @{ 30 | DatabaseName = $state.Name 31 | Server = $ServerName 32 | Status = $state.status 33 | "Copy Queue" = $state.CopyQueueLength 34 | "Replay Queue" = $state.ReplayQueueLength 35 | "Last Inspected Log Time" = $state.LastInspectedLogTime 36 | "Index State" = $state.ContentIndexState 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | If ($2010Snapin) { 44 | $status=GetDBStatus 45 | If ($status -ne $null) { 46 | $status | 47 | Sort DatabaseName | 48 | Select DatabaseName,Status,"Copy Queue","Replay Queue","Last Inspected Log Time","Index State" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Plugins/16 Exchange 2010 Active DB not on Preferred Server .ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 2010 Active DB Not on Preferred Server" 2 | $Header = "Exchange 2010 Active DB Not on Preferred Server" 3 | $Comments = "Exchange 2010 Active DB Not on Preferred Server" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.3 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Start of Settings 10 | # End of Settings 11 | 12 | # Changelog 13 | ## 2.0 : Initial release 14 | ## 2.1 : Simplify code 15 | ## 2.2 : Add server name filter 16 | ## 2.3 : Allow Exchange version 15 17 | 18 | Function GetDBActivation() { 19 | $DBs=Get-MailboxServer -ErrorAction SilentlyContinue | 20 | Where { $_.AdminDisplayVersion -match "Version (14|15)" -and $_.Name -match $exServerFilter } | 21 | Sort Name | 22 | Get-MailboxDatabase | 23 | Sort Identity -Unique | 24 | Select Identity, Server, ActivationPreference 25 | ForEach ($DB in $DBs) { 26 | $ServerName=$DB.Server.Name 27 | $A = $DB.ActivationPreference | Where { $_.Value -eq 1 } 28 | If ($A) { 29 | $PrefServer = $A.Key.Name 30 | If ($ServerName -ne $PrefServer ) { 31 | New-Object PSObject -Property @{ 32 | Database = $DB.Identity 33 | Server = $ServerName 34 | "Preferred Server" = $PrefServer 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | If ($2010Snapin) { 42 | GetDBActivation | Select Database, Server, "Preferred Server" 43 | } 44 | -------------------------------------------------------------------------------- /Plugins/16.5 Exchange 20xx Mailbox VSS Writer Health.ps1: -------------------------------------------------------------------------------- 1 | # Based on Get-Exchange-Server-VSSWriters-Status.ps1 2 | 3 | # http://smtp25.blogspot.co.uk/2014/01/use-to-retrieve-status-of-exchange.html 4 | 5 | # Start of Settings 6 | # End of Settings 7 | 8 | #======================================================================== 9 | # Created with: NotePad 10 | # Created on : 8/25/2013 9:21 AM 11 | # Author : Benjamin Bohn 12 | # Contributor : Oz Casey Dedeal 13 | # Organization: ZtekZone 14 | # Filename : Get-Exchange-Server-VSSWriters-Status 15 | # Synopsis : Use to retrieve the status of the Exchange Server related VSS Writers 16 | # Usage : You have to replace server names with your own servers/ You can use this script and change anything you like as professional 17 | # : courtesy please give us credits for our work http://smtp25.blogspot.com/ 18 | # 19 | #======================================================================== 20 | 21 | # Note: Run enable-psremoting on each of your Mailbox servers first 22 | 23 | # function to create a new object for the next VSS writer found 24 | function NewVSSObject { 25 | param( $server ) 26 | $tempObject = New-Object -TypeName PSObject 27 | Add-Member -InputObject $tempObject -MemberType NoteProperty -Name "Server" -Value $server 28 | return $tempObject 29 | } 30 | 31 | # build the array of servers to search 32 | $serverList = Get-MailBoxServer | Where { $_.Name -match $exServerFilter } 33 | 34 | # results array 35 | $allVSSWritersList = @() 36 | 37 | # loop through each server in the array 38 | foreach ( $server in $serverList ) { 39 | # Write-Host "Retrieving VSS status from $server......" -NoNewline 40 | 41 | # invoke the vssadmin utility remotely and parse the result string into an array, one element for each line 42 | $writers = Invoke-Command -ScriptBlock { $( vssadmin list writers ) -split "`n`r" } -ComputerName $server -ea 0 43 | 44 | # create a new object to store the results 45 | $px = NewVSSObject -Server $server 46 | 47 | # enumerate the results of the vssadmin 48 | for ( $lineNum = 0; $lineNum -lt $writers.length; $lineNum++ ) { 49 | 50 | # cleanup (trim the leading spaces) 51 | $line = $writers[ $lineNum ].trim() 52 | 53 | # pause reading the vssadmin results when we encounter a new writer 54 | if ( $line.StartsWith( "Writer name:" ) ) { 55 | 56 | # identify the writers we're interested in 57 | $writerName = $line.Substring( 14, $line.length-15 ) 58 | if ( $writerName -eq 'Microsoft Exchange Writer' ) { 59 | Add-Member -InputObject $px -MemberType NoteProperty -Name "Writer" -Value "Information Store" 60 | } 61 | elseif ( $writerName -eq 'Microsoft Exchange Replica Writer' ) { 62 | Add-Member -InputObject $px -MemberType NoteProperty -Name "Writer" -Value "Replication Writer" 63 | } 64 | 65 | # retrieve the rest of the data that we need 66 | if ( $writerName -like "Microsoft Exchange*" ) { 67 | $state = $writers[ $lineNum + 3 ].trim() 68 | $error = $writers[ $lineNum + 4 ].trim() 69 | Add-Member -InputObject $px -MemberType NoteProperty -Name "State" -Value $state.substring( 7, $state.length-7 ) 70 | Add-Member -InputObject $px -MemberType NoteProperty -Name "Error" -Value $error.substring( 12, $error.length-12 ) 71 | 72 | # add the resulting object to the array holding all the data 73 | $allVSSWritersList += $px 74 | 75 | # reset the results object for the next writer 76 | $px = NewVSSObject -Server $server 77 | } 78 | } 79 | } 80 | # Write-Host "done" 81 | } 82 | 83 | # display the results 84 | $allVSSWritersList 85 | 86 | $Title = "Exchange 20xx Mailbox VSS Writer Health" 87 | $Header = "Exchange 20xx Mailbox VSS Writer Health" 88 | $Comments = "Exchange 20xx Mailbox VSS Writer Health" 89 | $Display = "Table" 90 | $Author = "Phil Randal" 91 | $PluginVersion = 1.0 92 | $PluginCategory = "Exchange2010" 93 | -------------------------------------------------------------------------------- /Plugins/17 Exchange 20xx MAPI Connectivity.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/17 Exchange 20xx MAPI Connectivity.ps1 -------------------------------------------------------------------------------- /Plugins/18 Exchange 20xx PF Statistics.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 20xx PF Statistics" 2 | $Header = "Exchange Public Folder Statistics" 3 | $Comments = "Exchange Public Folder Statistics" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.3 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Based on Jonny's comment here: http://www.mikepfeiffer.net/2010/03/exchange-2010-database-statistics-with-powershell/ 10 | 11 | # Start of Settings 12 | # End of Settings 13 | 14 | # Changelog 15 | ## 2.0 : Sort by Server, Database 16 | ## Add elementary Exchange 2007 support 17 | ## 2.1 : Add Server name filter 18 | ## 2.2 : Test for versions >= 14 to work on Exchange 2013 19 | ## 2.3 : Minor corrections 20 | 21 | Function Get-DatabaseStatisticspublic { 22 | $Databases = Get-publicfolderdatabase -Status -ErrorAction SilentlyContinue | 23 | Where { $_.Server -match $exServerFilter } 24 | 25 | If ($Databases -ne $null) { 26 | foreach ($Database in $Databases) { 27 | If ($Database.ExchangeVersion.ExchangeBuild.Major -eq 8) { 28 | # Exchange 2007 29 | # Use WMI (Based on code by Gary Siepser, http://bit.ly/kWWMb3) 30 | $DBSize = [long](get-wmiobject cim_datafile -computername $Database.Server.Name -filter ('name=''' + $Database.edbfilepath.pathname.replace("\","\\") + '''')).filesize 31 | if (!$DBSize) { 32 | $DBSize = 0 33 | } 34 | $WhiteSpace = $null 35 | } Elseif ($Database.ExchangeVersion.ExchangeBuild.Major -ge 14) { 36 | # Exchange 2010 and 2013 37 | $DBSize = $Database.DatabaseSize.ToBytes() 38 | $Whitespace = ($Database.AvailableNewMailboxSpace.ToBytes() / 1GB) 39 | } 40 | 41 | New-Object PSObject -Property @{ 42 | Server = $Database.Server.Name 43 | Database = $Database.Name 44 | "Database Size (GB)" = "{0:n3}" -f ($DBSize / 1GB) 45 | "WhiteSpace (GB)" = $(If ($Whitespace -eq $null) { "" } Else { "{0:n3}" -f $Whitespace }) 46 | Mounted = $(If ($Database.Mounted) { "Yes" } Else { "No" }) 47 | "Circular Logging" = $(If ($Database.CircularLoggingEnabled) { "Yes" } Else { "No" }) 48 | "Last Full Backup" = $Database.LastFullBackup 49 | "Last Incremental Backup" = $Database.LastIncrementalBackup 50 | } 51 | } 52 | } 53 | } 54 | 55 | If ($2007Snapin -or $2010Snapin) { 56 | $pf=get-DatabaseStatisticspublic 57 | If ($pf -ne $null) { 58 | $pf | 59 | Sort Server,Database | 60 | Select Server, 61 | Database, 62 | "Database Size (GB)", 63 | "Whitespace (GB)", 64 | Mounted, 65 | "Circular Logging", 66 | "Last Full Backup", 67 | "Last Incremental Backup" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Plugins/19 Exchange 20xx Hub Transport Message Queues.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/19 Exchange 20xx Hub Transport Message Queues.ps1 -------------------------------------------------------------------------------- /Plugins/20 Exchange 20xx Largest Mailboxes.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/20 Exchange 20xx Largest Mailboxes.ps1 -------------------------------------------------------------------------------- /Plugins/21 Exchange 20xx Largest Dumpster.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/21 Exchange 20xx Largest Dumpster.ps1 -------------------------------------------------------------------------------- /Plugins/22 Exchange 20xx Largest Total Size.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/22 Exchange 20xx Largest Total Size.ps1 -------------------------------------------------------------------------------- /Plugins/22.5 Exchange 20xx Mailboxes with High Item Counts.ps1: -------------------------------------------------------------------------------- 1 | # Start of Settings 2 | # Report on the largest mailboxes - number to report on (if <= 0, will report on whole database) 3 | $NumHighItemFolders=0 4 | # Is the High Item CountReport per Database? 5 | $MailboxFolderItemCountReportPerDatabase=$False 6 | # Minimum Item Counts to report on 7 | $MinItemCount=50000 8 | # Exchange Database Name Filter (Largest) (regular expression, to select all use '.*') 9 | $exDBFilter=".*" 10 | # End of Settings 11 | 12 | # Changelog 13 | ## 0.1 'borrowed' code from Large Mailboxes by Phil Randal 14 | ## 1.0 Ready for Prod 15 | 16 | # See http://technet.microsoft.com/en-us/library/cc535025.aspx for Microsoft's 17 | # recommendations about folder item counts 18 | 19 | $Title = "Exchange 2010 Folder High Item Counts" 20 | $Author = "Kevin Maneschyn" 21 | $Comments = "Folders with High Item Counts" 22 | $PluginVersion = 1.0 23 | $PluginCategory = "Exchange2010" 24 | 25 | function Get-FolderHighItemCounts { 26 | $Databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue | 27 | Where { $_.Server -match $exServerFilter } | 28 | Where { $_.Name -match $exDBFilter } | 29 | Sort Server,Name 30 | If ($Databases) { 31 | foreach ($Database in $Databases) { 32 | If ($Database.DatabaseCreated) { 33 | $mbxs=Get-Mailbox -Database $Database -Resultsize Unlimited | 34 | Where { $_.Server -match $exServerFilter } | Select Identity 35 | $Details = $mbxs | Get-MailboxFolderStatistics | 36 | Where {($_.ItemsInFolder -ge $MinItemCount) -and ($_.FolderType -notlike "RecoverableItem*")} | 37 | Sort-Object -descending ItemsInFolder | 38 | Select @{n="Location";e={([string]$_.Identity).Substring(([string]$_.Identity).LastIndexOf('/')+1)}}, 39 | FolderType, 40 | @{n="Folder Size (GB)";e={("{0:N3}" -f ($_.FolderSize.ToMB()/1024)) }}, 41 | ItemsInFolder @Selection 42 | If ($null -ne $Details) { 43 | $Header = "Folders with Highest Item Counts on $($Database.Server) $($Larger)in $Database sorted by descending counts" 44 | $script:MyReport += Get-CustomHeader $Header $Comments 45 | $script:MyReport += Get-HTMLTable $Details 46 | $script:MyReport += Get-CustomHeaderClose 47 | } 48 | $mbxs = $null 49 | $Details = $null 50 | } 51 | } 52 | } 53 | } 54 | 55 | If ($2007Snapin -or $2010Snapin) { 56 | $Larger = "" 57 | If ($MinItemCount -gt 0) { 58 | $Larger = "larger than $MinItemCount " 59 | } 60 | $Selection = @{} 61 | If ($NumHighItemFolders -gt 0) { 62 | $Selection = @{ First = $NumHighItemFolders } 63 | } 64 | If ($MailboxFolderItemCountReportPerDatabase) { 65 | $Display = "None" 66 | Get-FolderHighItemCounts 67 | } Else { 68 | $Display = "Table" 69 | $Header = "Folders with Item Counts $($Larger)sorted by descending size" 70 | $mbxs=Get-Mailbox -Resultsize Unlimited | 71 | Where { $_.ServerName -match $exServerFilter } | Select Identity 72 | $mbxs | Get-MailboxFolderStatistics | 73 | Where {($_.ItemsInFolder -gt $MinItemCount) -and ($_.FolderType -notlike "RecoverableItem*") } | 74 | Sort ItemsInFolder -descending | 75 | Select @{n="Location";e={([string]$_.Identity).Substring(([string]$_.Identity).LastIndexOf('/')+1)}}, 76 | FolderType, 77 | @{n="Folder Size (GB)";e={("{0:N3}" -f ($_.FolderSize.ToMB()/1024)) }}, 78 | ItemsInFolder @Selection 79 | $mbxs = $null 80 | } 81 | } 82 | $Comments = "Mailbox Folders sorted by descending item count" 83 | -------------------------------------------------------------------------------- /Plugins/23 Exchange 20xx Services.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/23 Exchange 20xx Services.ps1 -------------------------------------------------------------------------------- /Plugins/24 Exchange 20xx Event Logs.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/24 Exchange 20xx Event Logs.ps1 -------------------------------------------------------------------------------- /Plugins/25 Exchange 20xx Uncleared Move Requests.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 20xx Uncleared Move Requests" 2 | $Header = "Uncleared Move Requests" 3 | $Comments = "Uncleared Move Requests" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.0 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Based on code in http://www.powershellneedfulthings.com/?page_id=281 10 | 11 | # Start of Settings 12 | # End of Settings 13 | 14 | # Changelog 15 | ## 2.0 : Initial Release 16 | 17 | If ($2007Snapin -or $2010Snapin) { 18 | Get-MoveRequest | 19 | Get-MoveRequestStatistics | 20 | Sort DisplayName | 21 | Select DisplayName,Status,@{Name="Total Mailbox Size (MB)";exp={$_.TotalMailBoxSize.ToMB()}},SourceDatabase,TargetDatabase,PercentComplete 22 | } 23 | -------------------------------------------------------------------------------- /Plugins/26 Exchange 20xx Disconnected Mailboxes.ps1: -------------------------------------------------------------------------------- 1 | $Title = "Exchange 20xx Disconnected Mailboxes" 2 | $Header = "Exchange 20xx Disconnected Mailboxes" 3 | $Comments = "Exchange 20xx Disconnected Mailboxes" 4 | $Display = "Table" 5 | $Author = "Phil Randal" 6 | $PluginVersion = 2.1 7 | $PluginCategory = "Exchange2010" 8 | 9 | # Start of Settings 10 | # End of Settings 11 | 12 | # Changelog 13 | ## 2.0 : Initial release 14 | ## 2.1 : Report GUID and disconnect reason 15 | 16 | ## If you need to delete these mailboxes from the database without waiting for them to be purged normally, see 17 | ## http://www.howexchangeworks.com/2010/09/purge-disconnected-or-soft-deleted.html 18 | 19 | ## e.g. in Exchange 2010 SP1 and later, you can type 20 | ## Remove-StoreMailbox -Database dbxx -identity "Mailbox GUID" -MailboxState SoftDeleted 21 | 22 | 23 | If ($2010Snapin -or $2007Snapin) { 24 | $exServers = Get-MailboxServer -ErrorAction SilentlyContinue | 25 | Where {$_.Name -match $exServerFilter } | 26 | Sort Name 27 | $disconn = @() 28 | ForEach ($Server in $exServers) { 29 | $disconn += Get-Mailboxstatistics -Server $Server | 30 | Where { $_.DisconnectDate -ne $null } 31 | } 32 | If ($disconn -ne $null) { 33 | $disconn | 34 | Sort DisplayName, ServerName, DatabaseName | 35 | Select DisplayName, ServerName, DatabaseName, DisconnectReason, DisconnectDate, MailboxGUID 36 | } 37 | } 38 | $disconn = $null 39 | -------------------------------------------------------------------------------- /Plugins/27 Exchange 20xx Forwarders.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/27 Exchange 20xx Forwarders.ps1 -------------------------------------------------------------------------------- /Plugins/Exchange 20xx Plugins Readme.txt: -------------------------------------------------------------------------------- 1 | Exchange Plugins for vCheck V2.1 2 | ================================ 3 | 4 | New in Exchange Plugins v2.1: 5 | ----------------------------- 6 | 7 | Six new plugins 8 | 9 | 19 Exchange 20xx Hub Transport Message Queues 10 | 23 Exchange 20xx Services 11 | 24 Exchange 20xx Event Logs 12 | 25 Exchange 20xx Uncleared Move Requests 13 | 26 Exchange 20xx Disconnected Mailboxes 14 | 27 Exchange 20xx Forwarders 15 | 16 | Beware, the Event Log reports can be huge and take forever to generate 17 | 18 | Enhancements: 19 | ------------- 20 | 21 | 10 Exchange 20xx Load Snapin 22 | Now has an exchange server name filter pattern (used in a -match comparison) which you can specify 23 | It is checked wherever a server-based plugin is run 24 | So you can do separate reports per server (or group of servers) if you wish 25 | Can now select Domain view / Entire Forest view (default is Domain) 26 | 27 | 11 Exchange 20xx Basic Server Information 28 | Sort hotfix rollups into ascending install date order 29 | 30 | 12 Exchange 20xx Drive Details 31 | Additional "critical" threshold parameter. 32 | Drives with free space under this threshold will be output in red. 33 | 34 | 20 Exchange 20xx Largest Mailboxes 35 | 21 Exchange 20xx Largest Dumpster 36 | 22 Exchange 20xx Largest Total Size 37 | Implemented database name filter pattern (used in a -match comparison) which you can specify 38 | independently for each report 39 | 40 | Fixes: 41 | ------ 42 | 43 | 11 Exchange 20xx Basic Server Information 44 | Report Server's Exchange rollups as "Unknown" of we can't connect to the remote registry 45 | 46 | 13 Exchange 2010 Database Availablility Groups 47 | Fix Sort keys 48 | 49 | 20 Exchange 20xx Largest Mailboxes 50 | Fix ServerName selection 51 | 52 | 21 Exchange 20xx Largest Dumpster 53 | Fix ServerName selection 54 | 55 | 22 Exchange 20xx Largest Total Size 56 | Fix ServerName selection 57 | Remove unnecessary Sort 58 | 59 | To Do: 60 | ------ 61 | Site-based reports. Not something I can do or test. 62 | 63 | Archive mailbox reporting. I don't use them, so can't test them. 64 | 65 | 66 | New in Exchange Plugins v2.0 (released 22 March 2012): 67 | ====================================================== 68 | 69 | Exchange 2007 support 70 | 71 | Report on drives with <= x% free space 72 | 73 | MAPI Latency report where latency is above user specified threshold 74 | 75 | Active DB not mounted on preferred server report 76 | 77 | Various bug fixes and code cleanups 78 | 79 | All the plugins have been renumbered into a more logical order 80 | 81 | Plus, a bonus plugin to select (via vCheck.ps1 -config) the report header image 82 | 83 | Added plugin "22 Exchange 20xx Largest Mailboxes by Total Size", like 20 and 21, 84 | but sorted by the sum of mailbox and dumpster sizes 85 | 86 | ------------------------------------------------------------------------------------- 87 | 88 | These plugins are based on ideas in http://www.stevieg.org/2011/06/exchange-environment-report/ 89 | and http://www.powershellneedfulthings.com/?page_id=281 90 | 91 | To configure, run vCheck.ps1 -config 92 | 93 | To select which plugins to run, use Select-Plugins.ps1 (which can be found in the latest vCheck) 94 | 95 | The server name parameter isn't used, except in the report, so put something descriptive in there. 96 | 97 | 98 | Plugins 99 | ======= 100 | 101 | 00 1st Plugin - Select Report Header Image 102 | Sets report header image 103 | For example, download the Exchange header from 104 | http://www.virtu-al.net/featured-scripts/vcheck/vcheck-headers/ 105 | and save as vCheck\Headers\Exchange.png, and this will work out of the box 106 | Falls back to vCheck\Header.jpg if specified header can't be found 107 | 108 | 10 Exchange 20xx Load Snapin.ps1 109 | Loads Exchange 2010 / Exchange 2007 powershel snapin 110 | 111 | 11 Exchange 20xx Basic Server Information.ps1 112 | Basic Exchange server info: OS & Service pack, Exchange version, hotfix rollups, 113 | Exchange Edition and Roles 114 | 115 | 12 Exchange 20xx Drive Details.ps1 116 | Drive details for each of the Exchange servers. Can be configured to report only 117 | on drives with less than a specified percentage free space 118 | 119 | 13 Exchange 2010 Database Availablility Groups.ps1 120 | Basic info about your DAG groups - Exchange 2010 only 121 | 122 | 14 Exchange 20xx DB Statistics.ps1 123 | Database statistics - number of mailboxes, sizes, circular logging, and last 124 | backup dates 125 | 126 | 15 Exchange 2010 DB Status.ps1 127 | Database status info 128 | 129 | 16 16 Exchange 2010 Active DB not on Preferred Server .ps1 130 | Reports on databases not mounted on their preferred servers 131 | 132 | 17 Exchange 20xx MAPI Connectivity.ps1 133 | List MAPI connectivity latencies - can be configured to only report on latencies 134 | above a specified level 135 | 136 | 18 Exchange 20xx PF Statistics.ps1 137 | Public Folder stats 138 | 139 | 19 Exchange 20xx Hub Transport Message Queues 140 | Report on Hub Transport Message Queues 141 | 142 | 20 Exchange 20xx Largest Mailboxes.ps1 143 | Report on largest mailboxes by Mailbox size 144 | 145 | 21 Exchange 20xx Largest Dumpster.ps1 146 | Report on largest mailboxes by Dumpster (deleted items) size 147 | 148 | 22 Exchange 20xx Largest Total Size.ps1 149 | Report on largest mailboxes by Total size (Mailbox + Dumpster) 150 | 151 | For each of the above three reports, you can report on the top n mailboxes by size 152 | either across organisation or per DB, and you can also specify a threshold size to 153 | report on. 154 | 155 | You can also use a regular expression match to report on a subset of databases. 156 | 157 | For obvious reasons, I wouldn't advise reporting on all mailboxes without 158 | a non-zero threshold 159 | 160 | 23 Exchange 20xx Services 161 | Report on Exchange-related services 162 | Either full list or a report on unexpected service state 163 | 164 | 24 Exchange 20xx Event Logs 165 | Report on Exchange Server Event Logs 166 | You can choose which logs to report on, and Errors and Warnings or Errors only 167 | And you can filter out events using regular-expression matching 168 | 169 | 25 Exchange 20xx Uncleared Move Requests 170 | Report on uncleared move requests 171 | 172 | 26 Exchange 20xx Disconnected Mailboxes 173 | List all disconnected mailboxes 174 | 175 | 27 Exchange 20xx Forwarders 176 | Mailboxes which forward emails to other addresses 177 | 178 | Plugins for Exchange not up to date or installed.ps1 179 | Report on out of date / missing plugins 180 | 181 | Report on Plugins.ps1 182 | Report on which plugins were invoked in current run 183 | -------------------------------------------------------------------------------- /Plugins/Plugins for Exchange not up to date or installed.ps1: -------------------------------------------------------------------------------- 1 | # Start of Settings 2 | # If you use a proxy to access the internet please specify the proxy address here, for example http://127.0.0.1:3128 else use $false 3 | $proxy ="$false" 4 | # End of Settings 5 | 6 | # Changelog 7 | ## 1.1 : Adding proxy support for Get-vCheckPlugin cmdlet 8 | ## 1.2 : Added support for only vSphere plugins 9 | ## 1.4 : Renamed plugin and changed its category to "Exchange2010" 10 | 11 | . $ScriptPath\vcheckutils.ps1 | Out-Null 12 | if ($proxy -eq "$false"){ 13 | $NotInstalled = Get-vCheckPlugin -NotInstalled | Where { $_.Category -eq "Exchange2010" } | Select Name, version, Status, Description 14 | } else { 15 | $NotInstalled = Get-vCheckPlugin -NotInstalled -Proxy $proxy | Where { $_.Category -eq "Exchange2010" } | Select Name, version, Status, Description 16 | } 17 | $NotInstalled 18 | 19 | $Title = "Exchange Plugins not up to date or not installed" 20 | $Header = "Exchange Plugins not up to date or not installed: $(@($NotInstalled).count)" 21 | $Comments = "The following Exchange Plugins are not up to date or not installed" 22 | $Display = "Table" 23 | $Author = "Alan Renouf, Jake Robinson, Frederic Martin" 24 | $PluginVersion = 1.4 25 | $PluginCategory = "Exchange2010" 26 | -------------------------------------------------------------------------------- /Plugins/Report on Plugins.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/Plugins/Report on Plugins.ps1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vCheck-Exchange 2 | =============== 3 | 4 | vCheck for Exchange 5 | 6 | vCheck is a PowerShell HTML framework script, the script is designed to run as a scheduled task before you get into the office to present you with key information via an email directly to your inbox in a nice easily readable format. 7 | 8 | Now you can have a daily email with your Exchange 2010 details and issues. 9 | 10 | @Philrandal has added 6 initial Exchange 2010 plugins which add some great details, these include: 11 | 12 | - Basic Server Information 13 | - Database Statistics 14 | - Database Status 15 | - Public Folder Statistics 16 | - Mailboxes larger than x amount of MB 17 | - Mailboxes with deleted items above x amount of MB 18 | - And much much more 19 | 20 | 21 | More Info 22 | ========= 23 | 24 | For more information please read here: http://www.virtu-al.net/vcheck-pluginsheaders/vcheck/ 25 | 26 | For an example vCheck for Exchange output (doesnt contain all info) click here http://virtu-al.net/vcheck/ExamplePages/acme.htm -------------------------------------------------------------------------------- /Select-Plugins.ps1: -------------------------------------------------------------------------------- 1 | # Select-Plugins.ps1 2 | 3 | # selectively enable / disable vCheck Plugins 4 | 5 | # presents a list of plugins whose names match *.ps1 or *.ps1.disabled 6 | # 7 | # disabled plugins will be renamed as appropriate to .ps1.disabled 8 | # enabled plugins will be renamed as appropriate to .ps1 9 | 10 | # To use, run from the vCheck directory 11 | # or, if you wish to be perverse, copy to the plugins directory and rename to 12 | # "ZZ Select Plugins for Next Run.ps1" and run vCheck as normal. 13 | 14 | # Great for testing plugins. When done, untick it... 15 | 16 | # If run as a plugin, it will affect the next vCheck run, not the current one, 17 | # as vCheck has already collected its list of plugins when it is invoked 18 | # so make it the very last plugin executed to avoid counter-intuitive behaviour 19 | 20 | # based on code from Select-GraphicalFilteredObject.ps1 in 21 | # "Windows Powershell Cookbook" by Lee Holmes. 22 | # Copyright 2007 Lee Holmes. 23 | # Published by O'Reilly ISBN 978-0-596-528492 24 | # and used under the 'free use' provisions specified on Preface page xxv 25 | 26 | $Title = "Plugin Selection Plugin" 27 | $Author = "Phil Randal" 28 | $PluginVersion = 2.0 29 | $Header = "Plugin Selection" 30 | $Comments = "Plugin Selection" 31 | $Display = "None" 32 | 33 | # Start of Settings 34 | # End of Settings 35 | 36 | $PluginPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) 37 | If ($PluginPath -notmatch 'plugins$') { 38 | $PluginPath += "\Plugins" 39 | } 40 | $plugins=get-childitem -Path $PluginPath | where {$_.name -match '.*\.ps1(?:\.disabled|)$'} | 41 | Sort Name | 42 | Select Name, 43 | @{Label="Plugin";expression={$_.Name -replace '(.*)\.ps1(?:\.disabled|)$', '$1'}}, 44 | @{Label="Enabled";expression={$_.Name -notmatch '.*\.disabled$'}} 45 | 46 | ## Load the Windows Forms assembly 47 | [void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 48 | 49 | ## Create the main form 50 | $form = New-Object Windows.Forms.Form 51 | $form.Size = New-Object Drawing.Size @(600,600) 52 | 53 | ## Create the listbox to hold the items from the pipeline 54 | $listbox = New-Object Windows.Forms.CheckedListBox 55 | $listbox.CheckOnClick = $true 56 | $listbox.Dock = "Fill" 57 | $form.Text = "Select the plugins you wish to enable" 58 | # create list box items from plugin list, tick as enabled where appropriate 59 | ForEach ($plugin in $Plugins) { 60 | $i=$listBox.Items.Add($plugin.Plugin) 61 | $listbox.SetItemChecked($i, $Plugin.Enabled) 62 | } 63 | 64 | ## Create the button panel to hold the OK and Cancel buttons 65 | $buttonPanel = New-Object Windows.Forms.Panel 66 | $buttonPanel.Size = New-Object Drawing.Size @(600,30) 67 | $buttonPanel.Dock = "Bottom" 68 | 69 | ## Create the Cancel button, which will anchor to the bottom right 70 | $cancelButton = New-Object Windows.Forms.Button 71 | $cancelButton.Text = "Cancel" 72 | $cancelButton.DialogResult = "Cancel" 73 | $cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 5 74 | $cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10 75 | $cancelButton.Anchor = "Right" 76 | 77 | ## Create the OK button, which will anchor to the left of Cancel 78 | $okButton = New-Object Windows.Forms.Button 79 | $okButton.Text = "Ok" 80 | $okButton.DialogResult = "Ok" 81 | $okButton.Top = $cancelButton.Top 82 | $okButton.Left = $cancelButton.Left - $okButton.Width - 5 83 | $okButton.Anchor = "Right" 84 | 85 | ## Add the buttons to the button panel 86 | $buttonPanel.Controls.Add($okButton) 87 | $buttonPanel.Controls.Add($cancelButton) 88 | 89 | ## Add the button panel and list box to the form, and also set 90 | ## the actions for the buttons 91 | $form.Controls.Add($listBox) 92 | $form.Controls.Add($buttonPanel) 93 | $form.AcceptButton = $okButton 94 | $form.CancelButton = $cancelButton 95 | $form.Add_Shown( { $form.Activate() } ) 96 | 97 | ## Show the form, and wait for the response 98 | $result = $form.ShowDialog() 99 | 100 | ## If they pressed OK (or Enter,) 101 | ## enumerate list of plugins and rename those whose status has changed 102 | if($result -eq "OK") { 103 | $i = 0 104 | ForEach ($plugin in $plugins) { 105 | $oldname = $plugin.Name 106 | $newname = $plugin.Plugin + $(If ($listbox.GetItemChecked($i)) {'.ps1'} else {'.ps1.disabled'}) 107 | If ($newname -ne $oldname) { 108 | If (Test-Path ($PluginPath + "\" + $newname)) { 109 | Write-Host "Attempting to rename ""$oldname"" to ""$newname"", which already exists - please delete or rename the superfluous file and try again" 110 | } Else { 111 | Rename-Item ($PluginPath + "\" + $oldname) $newname 112 | } 113 | } 114 | $i++ 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /vCheck.ps1: -------------------------------------------------------------------------------- 1 | param ([Switch]$config, $Outputpath) 2 | ############################### 3 | # vCheck - Daily Error Report # 4 | ############################### 5 | # Thanks to all who have commented on my blog to help improve this project 6 | # all beta testers and previous contributors to this script. 7 | # 8 | $Version = "6.15" 9 | 10 | function Write-CustomOut ($Details){ 11 | $LogDate = Get-Date -Format T 12 | Write-Host "$($LogDate) $Details" 13 | #write-eventlog -logname Application -source "Windows Error Reporting" -eventID 12345 -entrytype Information -message "vCheck: $Details" 14 | } 15 | 16 | Function Invoke-Settings ($Filename, $GB) { 17 | $file = Get-Content $filename 18 | $OriginalLine = ($file | Select-String -Pattern "# Start of Settings").LineNumber 19 | $EndLine = ($file | Select-String -Pattern "# End of Settings").LineNumber 20 | if (($OriginalLine +1) -eq $EndLine) { 21 | } Else { 22 | $Array = @() 23 | $Line = $OriginalLine 24 | do { 25 | $Question = $file[$Line] 26 | $Line ++ 27 | $Split= ($file[$Line]).Split("=") 28 | $Var = $Split[0] 29 | $CurSet = $Split[1] 30 | 31 | # Check if the current setting is in speach marks 32 | $String = $false 33 | if ($CurSet -match '"') { 34 | $String = $true 35 | $CurSet = $CurSet.Replace('"', '') 36 | } 37 | $NewSet = Read-Host "$Question [$CurSet]" 38 | If (-not $NewSet) { 39 | $NewSet = $CurSet 40 | } 41 | If ($String) { 42 | $Array += $Question 43 | $Array += "$Var=`"$NewSet`"" 44 | } Else { 45 | $Array += $Question 46 | $Array += "$Var=$NewSet" 47 | } 48 | $Line ++ 49 | } Until ( $Line -ge ($EndLine -1) ) 50 | $Array += "# End of Settings" 51 | 52 | $out = @() 53 | $out = $File[0..($OriginalLine -1)] 54 | $out += $array 55 | $out += $File[$Endline..($file.count -1)] 56 | if ($GB) { $out[$SetupLine] = '$SetupWizard =$False' } 57 | $out | Out-File $Filename 58 | } 59 | } 60 | 61 | # Add all global variables. 62 | $ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) 63 | $PluginsFolder = $ScriptPath + "\Plugins\" 64 | $Plugins = Get-ChildItem -Path $PluginsFolder -filter "*.ps1" | Sort Name 65 | $GlobalVariables = $ScriptPath + "\GlobalVariables.ps1" 66 | 67 | $file = Get-Content $GlobalVariables 68 | 69 | $Setup = ($file | Select-String -Pattern '# Set the following to true to enable the setup wizard for first time run').LineNumber 70 | $SetupLine = $Setup ++ 71 | $SetupSetting = invoke-Expression (($file[$SetupLine]).Split("="))[1] 72 | if ($config) { 73 | $SetupSetting = $true 74 | } 75 | If ($SetupSetting) { 76 | cls 77 | Write-Host 78 | Write-Host -ForegroundColor Yellow "Welcome to vCheck by Virtu-Al http://virtu-al.net" 79 | Write-Host -ForegroundColor Yellow "=================================================" 80 | Write-Host -ForegroundColor Yellow "This is the first time you have run this script or you have re-enabled the setup wizard." 81 | Write-Host 82 | Write-Host -ForegroundColor Yellow "To re-run this wizard in the future please use vCheck.ps1 -Config" 83 | Write-Host -ForegroundColor Yellow "To define a path to store each vCheck report please use vCheck.ps1 -Outputpath C:\tmp" 84 | Write-Host 85 | Write-Host -ForegroundColor Yellow "Please complete the following questions or hit Enter to accept the current setting" 86 | Write-Host -ForegroundColor Yellow "After completing ths wizard the vCheck report will be displayed on the screen." 87 | Write-Host 88 | 89 | Invoke-Settings -Filename $GlobalVariables -GB $true 90 | Foreach ($plugin in $Plugins) { 91 | Invoke-Settings -Filename $plugin.Fullname 92 | } 93 | } 94 | 95 | . $GlobalVariables 96 | 97 | $DspHeader0 = " 98 | BORDER-RIGHT: #bbbbbb 1px solid; 99 | PADDING-RIGHT: 0px; 100 | BORDER-TOP: #bbbbbb 1px solid; 101 | DISPLAY: block; 102 | PADDING-LEFT: 0px; 103 | FONT-WEIGHT: bold; 104 | FONT-SIZE: 8pt; 105 | MARGIN-BOTTOM: -1px; 106 | MARGIN-LEFT: 0px; 107 | BORDER-LEFT: #bbbbbb 1px solid; 108 | COLOR: #$($TitleTxtColour); 109 | MARGIN-RIGHT: 0px; 110 | PADDING-TOP: 4px; 111 | BORDER-BOTTOM: #bbbbbb 1px solid; 112 | FONT-FAMILY: Tahoma; 113 | POSITION: relative; 114 | HEIGHT: 2.25em; 115 | WIDTH: 95%; 116 | TEXT-INDENT: 10px; 117 | BACKGROUND-COLOR: #$($Colour1); 118 | " 119 | 120 | $DspHeader1 = " 121 | BORDER-RIGHT: #bbbbbb 1px solid; 122 | PADDING-RIGHT: 0px; 123 | BORDER-TOP: #bbbbbb 1px solid; 124 | DISPLAY: block; 125 | PADDING-LEFT: 0px; 126 | FONT-WEIGHT: bold; 127 | FONT-SIZE: 8pt; 128 | MARGIN-BOTTOM: -1px; 129 | MARGIN-LEFT: 0px; 130 | BORDER-LEFT: #bbbbbb 1px solid; 131 | COLOR: #$($TitleTxtColour); 132 | MARGIN-RIGHT: 0px; 133 | PADDING-TOP: 4px; 134 | BORDER-BOTTOM: #bbbbbb 1px solid; 135 | FONT-FAMILY: Tahoma; 136 | POSITION: relative; 137 | HEIGHT: 2.25em; 138 | WIDTH: 95%; 139 | TEXT-INDENT: 10px; 140 | BACKGROUND-COLOR: #$($Colour2); 141 | " 142 | 143 | $dspcomments = " 144 | BORDER-RIGHT: #bbbbbb 1px solid; 145 | PADDING-RIGHT: 0px; 146 | BORDER-TOP: #bbbbbb 1px solid; 147 | DISPLAY: block; 148 | PADDING-LEFT: 0px; 149 | FONT-WEIGHT: bold; 150 | FONT-SIZE: 8pt; 151 | MARGIN-BOTTOM: -1px; 152 | MARGIN-LEFT: 0px; 153 | BORDER-LEFT: #bbbbbb 1px solid; 154 | COLOR: #$($TitleTxtColour); 155 | MARGIN-RIGHT: 0px; 156 | PADDING-TOP: 4px; 157 | BORDER-BOTTOM: #bbbbbb 1px solid; 158 | FONT-FAMILY: Tahoma; 159 | POSITION: relative; 160 | HEIGHT: 2.25em; 161 | WIDTH: 95%; 162 | TEXT-INDENT: 10px; 163 | BACKGROUND-COLOR:#FFFFE1; 164 | COLOR: #000000; 165 | FONT-STYLE: ITALIC; 166 | FONT-WEIGHT: normal; 167 | FONT-SIZE: 8pt; 168 | " 169 | 170 | $filler = " 171 | BORDER-RIGHT: medium none; 172 | BORDER-TOP: medium none; 173 | DISPLAY: block; 174 | BACKGROUND: none transparent scroll repeat 0% 0%; 175 | MARGIN-BOTTOM: -1px; 176 | FONT: 100%/8px Tahoma; 177 | MARGIN-LEFT: 43px; 178 | BORDER-LEFT: medium none; 179 | COLOR: #ffffff; 180 | MARGIN-RIGHT: 0px; 181 | PADDING-TOP: 4px; 182 | BORDER-BOTTOM: medium none; 183 | POSITION: relative 184 | " 185 | 186 | $dspcont =" 187 | BORDER-RIGHT: #bbbbbb 1px solid; 188 | BORDER-TOP: #bbbbbb 1px solid; 189 | PADDING-LEFT: 0px; 190 | FONT-SIZE: 8pt; 191 | MARGIN-BOTTOM: -1px; 192 | PADDING-BOTTOM: 5px; 193 | MARGIN-LEFT: 0px; 194 | BORDER-LEFT: #bbbbbb 1px solid; 195 | WIDTH: 95%; 196 | COLOR: #000000; 197 | MARGIN-RIGHT: 0px; 198 | PADDING-TOP: 4px; 199 | BORDER-BOTTOM: #bbbbbb 1px solid; 200 | FONT-FAMILY: Tahoma; 201 | POSITION: relative; 202 | BACKGROUND-COLOR: #f9f9f9 203 | " 204 | 205 | Function Get-Base64Image ($Path) { 206 | $pic = Get-Content $Path -Encoding Byte 207 | [Convert]::ToBase64String($pic) 208 | } 209 | 210 | $HeaderImg = Get-Base64Image ((Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) + "\Header.jpg") 211 | 212 | Function Get-CustomHTML ($Header){ 213 | $Report = @" 214 | 215 | $($Header) 216 | 217 | 218 | 246 | 247 | 248 |
249 |

250 | 253 | 254 | vCheck 255 | 256 |

257 |
258 |
vCheck v$($version) by Alan Renouf (http://virtu-al.net) generated on $($ENV:Computername) 259 |
260 | "@ 261 | Return $Report 262 | } 263 | 264 | Function Get-CustomHeader0 ($Title){ 265 | $Report = @" 266 |
267 | 268 |

$($Title)

269 | 270 |
271 | "@ 272 | Return $Report 273 | } 274 | 275 | Function Get-CustomHeader ($Title, $cmnt){ 276 | $Report = @" 277 |

$($Title)

278 | "@ 279 | If ($Comments) { 280 | $Report += @" 281 |
$($cmnt)
282 | "@ 283 | } 284 | $Report += @" 285 |
286 | "@ 287 | Return $Report 288 | } 289 | 290 | Function Get-CustomHeaderClose{ 291 | 292 | $Report = @" 293 |
294 |
295 | "@ 296 | Return $Report 297 | } 298 | 299 | Function Get-CustomHeader0Close{ 300 | $Report = @" 301 |
302 | "@ 303 | Return $Report 304 | } 305 | 306 | Function Get-CustomHTMLClose{ 307 | $Report = @" 308 | 309 | 310 | 311 | 312 | "@ 313 | Return $Report 314 | } 315 | 316 | Function Get-HTMLTable { 317 | param([array]$Content) 318 | $HTMLTable = $Content | ConvertTo-Html -Fragment 319 | $HTMLTable = $HTMLTable -Replace '', '
' 320 | $HTMLTable = $HTMLTable -Replace '
', '' 321 | $HTMLTable = $HTMLTable -Replace '', '' 322 | $HTMLTable = $HTMLTable -replace '<', "<" 323 | $HTMLTable = $HTMLTable -replace '>', ">" 324 | Return $HTMLTable 325 | } 326 | 327 | Function Get-HTMLDetail ($Heading, $Detail){ 328 | $Report = @" 329 | 330 | 331 | 332 | 333 | 334 |
$Heading$($Detail)
335 | "@ 336 | Return $Report 337 | } 338 | 339 | # Adding all plugins 340 | $TTRReport = @() 341 | $MyReport = Get-CustomHTML "$Server vCheck" 342 | $MyReport += Get-CustomHeader0 ($Server) 343 | $Plugins | Foreach { 344 | $TTR = [math]::round((Measure-Command {$Details = . $_.FullName}).TotalSeconds, 2) 345 | $TTRTable = "" | Select Plugin, TimeToRun 346 | $TTRTable.Plugin = $_.Name 347 | $TTRTable.TimeToRun = $TTR 348 | $TTRReport += $TTRTable 349 | $ver = "{0:N1}" -f $PluginVersion 350 | Write-CustomOut "..finished calculating $Title by $Author v$Ver" 351 | If ($Details) { 352 | If ($Display -eq "List"){ 353 | $MyReport += Get-CustomHeader $Header $Comments 354 | $AllProperties = $Details | Get-Member -MemberType Properties 355 | $AllProperties | Foreach { 356 | $MyReport += Get-HTMLDetail $_.Name $Details.($_.Name) 357 | } 358 | $MyReport += Get-CustomHeaderClose 359 | } 360 | If ($Display -eq "Table") { 361 | $MyReport += Get-CustomHeader $Header $Comments 362 | $MyReport += Get-HTMLTable $Details 363 | $MyReport += Get-CustomHeaderClose 364 | } 365 | } 366 | } 367 | $MyReport += Get-CustomHeader ("This report took " + [math]::round(((Get-Date) - $Date).TotalMinutes,2) + " minutes to run all checks.") "The following plugins took longer than $PluginSeconds seconds to run, there may be a way to optimize these or remove them if not needed" 368 | $TTRReport = $TTRReport | Where { $_.TimeToRun -gt $PluginSeconds } 369 | $TTRReport | Foreach {$MyReport += Get-HTMLDetail $_.Plugin $_.TimeToRun} 370 | $MyReport += Get-CustomHeaderClose 371 | $MyReport += Get-CustomHeader0Close 372 | $MyReport += Get-CustomHTMLClose 373 | 374 | if ($DisplayToScreen -or $SetupSetting) { 375 | Write-CustomOut "..Displaying HTML results" 376 | $Filename = $Env:TEMP + "\" + $Server + "vCheck" + "_" + $Date.Day + "-" + $Date.Month + "-" + $Date.Year + ".htm" 377 | $MyReport | out-file -encoding ASCII -filepath $Filename 378 | Invoke-Item $Filename 379 | } 380 | 381 | if ($SendAttachment) { 382 | $Filename = $Env:TEMP + "\" + $Server + "vCheck" + "_" + $Date.Day + "-" + $Date.Month + "-" + $Date.Year + ".htm" 383 | $MyReport | out-file -encoding ASCII -filepath $Filename 384 | } 385 | 386 | if ($Outputpath) { 387 | $DateHTML = Get-Date -Format "yyyyMMddHH" 388 | $ArchiveFilePath = $Outputpath + "\Archives\" + $VIServer 389 | if (-not (Test-Path -PathType Container $ArchiveFilePath)) { New-Item $ArchiveFilePath -type directory | Out-Null } 390 | $Filename = $ArchiveFilePath + "\" + $VIServer + "_vCheck_" + $DateHTML + ".htm" 391 | $MyReport | out-file -encoding ASCII -filepath $Filename 392 | } 393 | 394 | if ($SendEmail) { 395 | Write-CustomOut "..Sending Email" 396 | If ($SendAttachment) { 397 | send-Mailmessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body "vCheck attached to this email" -Attachments $Filename 398 | } Else { 399 | send-Mailmessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body $MyReport -BodyAsHtml 400 | } 401 | } 402 | 403 | if ($SendAttachment -eq $true -and $DisplaytoScreen -ne $true) { 404 | Write-CustomOut "..Removing temporary file" 405 | Remove-Item $Filename -Force 406 | } 407 | 408 | $End = $ScriptPath + "\EndScript.ps1" 409 | . $End -------------------------------------------------------------------------------- /vCheckUtils.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanrenouf/vCheck-Exchange/a061684015c71f7ee4e4c878932be77987144405/vCheckUtils.ps1 --------------------------------------------------------------------------------