From 3b420f4b6201665a7412034006a0d64e00a4c499 Mon Sep 17 00:00:00 2001 From: Bill Wall Date: Wed, 8 Feb 2017 07:47:47 -0600 Subject: [PATCH 01/29] Update 82 License Report.ps1 --- Plugins/10 vCenter/82 License Report.ps1 | 47 +++++++++++++----------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/Plugins/10 vCenter/82 License Report.ps1 b/Plugins/10 vCenter/82 License Report.ps1 index 62e178d0..b9ac315b 100644 --- a/Plugins/10 vCenter/82 License Report.ps1 +++ b/Plugins/10 vCenter/82 License Report.ps1 @@ -1,37 +1,42 @@ + # Start of Settings # Enable License Reporting? $licenseReport = $true +# Display Eval licenses? +$licenseEvals = $true # End of Settings # Changelog ## 1.0 : Initial Release +## 1.1 : Added the ability to exclude evaluation licenses, clean up whitespace (@thebillness) if ($licenseReport) { - -$vSphereLicInfo = @() -$ServiceInstance = Get-View ServiceInstance -Foreach ($LicenseMan in Get-View ($ServiceInstance | Select -First 1).Content.LicenseManager) { - Foreach ($License in ($LicenseMan | Select -ExpandProperty Licenses)) { - $Details = "" |Select VC, Name, Key, Total, Used, ExpirationDate , Information - $Details.VC = ([Uri]$LicenseMan.Client.ServiceUrl).Host - $Details.Name= $License.Name - $Details.Key= $License.LicenseKey - $Details.Total= $License.Total - $Details.Used= $License.Used - $Details.Information= $License.Labels | Select -expand Value - $Details.ExpirationDate = $License.Properties | Where { $_.key -eq "expirationDate" } | Select -ExpandProperty Value - $vSphereLicInfo += $Details + $vSphereLicInfo = @() + $ServiceInstance = Get-View ServiceInstance + Foreach ($LicenseMan in Get-View ($ServiceInstance | Select -First 1).Content.LicenseManager) { + Foreach ($License in ($LicenseMan | Select -ExpandProperty Licenses)) { + $Details = "" |Select VC, Name, Key, Total, Used, ExpirationDate , Information + $Details.VC = ([Uri]$LicenseMan.Client.ServiceUrl).Host + $Details.Name= $License.Name + $Details.Key= $License.LicenseKey + $Details.Total= $License.Total + $Details.Used= $License.Used + $Details.Information= $License.Labels | Select -expand Value + $Details.ExpirationDate = $License.Properties | Where { $_.key -eq "expirationDate" } | Select -ExpandProperty Value + If ($Details.Name -notmatch "Evaluation") { + $vSphereLicInfo += $Details + } elseif ($licenseEvals) { + $vSphereLicInfo += $Details + } + } } -} - -$vSphereLicInfo - + $vSphereLicInfo } $Title = "vCenter License Report" $Header = "License Report" -$Comments = "The following displays licenses registered with this server and usage." +$Comments = "The following displays licenses registered with this server and usage. Include Evals: $licenseEvals" $Display = "Table" -$Author = "Justin Mercier" -$PluginVersion = 1.0 +$Author = "Justin Mercier, Bill Wall" +$PluginVersion = 1.1 $PluginCategory = "vSphere" From 2e537b5922394345d6532174beed25f05e3ba3bb Mon Sep 17 00:00:00 2001 From: Bill Wall Date: Wed, 8 Feb 2017 07:48:02 -0600 Subject: [PATCH 02/29] Update 82 License Report.ps1 --- Plugins/10 vCenter/82 License Report.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Plugins/10 vCenter/82 License Report.ps1 b/Plugins/10 vCenter/82 License Report.ps1 index b9ac315b..ba747dff 100644 --- a/Plugins/10 vCenter/82 License Report.ps1 +++ b/Plugins/10 vCenter/82 License Report.ps1 @@ -1,4 +1,3 @@ - # Start of Settings # Enable License Reporting? $licenseReport = $true From a0eac5c182fb2d26598bd2b1e3541aa48aa3448a Mon Sep 17 00:00:00 2001 From: Pall Valmundsson Date: Fri, 16 Jun 2017 17:27:14 +0000 Subject: [PATCH 03/29] Fix templocation on Linux --- Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 index 2d359912..d25bcff6 100644 --- a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 +++ b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 @@ -110,8 +110,8 @@ switch ($platform.OSFamily) { Get-Module -ListAvailable PowerCLI* | Import-Module } "Linux" { - $Outputpath = $templocation $templocation = "/tmp" + $Outputpath = $templocation Get-Module -ListAvailable PowerCLI* | Import-Module } "Windows" { From 566fcaf9b4093728d27aefa28bd07dac4b086696 Mon Sep 17 00:00:00 2001 From: Jim Jones Date: Wed, 6 Sep 2017 13:49:57 -0400 Subject: [PATCH 04/29] Update README.md Added link to the Installing section so header navigation works. Updated wording on the Installing section to instruct it to run vCheck.ps1 -Config --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 94fd9406..11ebbcd9 100644 --- a/README.md +++ b/README.md @@ -103,10 +103,13 @@ The following items are included as part of the vCheck vSphere download, they ar - Inaccessible VMs - Much, Much more....... + + # Installing [*Back to top*](#Title) -Copy the vCheck files to the desired location. Run the script `vCheckUtils.ps1` and call the function `Schedule-vCheck`. Answer the prompts to configure the scheduled job. +Copy the vCheck files to the desired location. Run the script 'vCheck.ps1' -Config to do initial configuration of the script. + From e4d89d6393fbc77955b5dae802ddeab7f9741195 Mon Sep 17 00:00:00 2001 From: thisdanguy <31424141+thisdanguy@users.noreply.github.com> Date: Fri, 8 Sep 2017 15:47:33 -0500 Subject: [PATCH 05/29] Update CONTRIBUTING.md fixing a couple typos --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fa3cae52..d9328feb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ If the changes are related to an existing GitHub issue, please reference the iss One of the easiest ways to contribute to a PowerShell project is by helping to write and edit documentation. All of our documentation hosted on GitHub is written using [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/) -*We are at lest working on shifting things over to GFM, for 'core' documentation. Some things may still live on the GitHub wiki, but using GFM allows the documentation to exist in the repo, so you always have a local copy to reference ;)* +*We are at least working on shifting things over to GFM, for 'core' documentation. Some things may still live on the GitHub wiki, but using GFM allows the documentation to exist in the repo, so you always have a local copy to reference ;)* To [edit an existing file](https://help.github.com/articles/editing-files-in-another-user-s-repository/), simply navigate to it and click the "Edit" button. GitHub will automatically create your own fork of our repository where you can make your changes. @@ -98,7 +98,7 @@ When contributing to this repository, please follow the following guidelines: * For all indentation, use 4 spaces instead of tab stops * Make sure all files are encoding using UTF-8. -* When writing Markdown, if a paragraph includes more than one setence, end each sentence with a newline. +* When writing Markdown, if a paragraph includes more than one sentence, end each sentence with a newline. GitHub will still render the sentences as a single paragraph, but the readability of `git diff` will be greatly improved. From ac352f9b8f33770ac82120bd61a1099d613df704 Mon Sep 17 00:00:00 2001 From: danbarr Date: Thu, 21 Sep 2017 19:31:14 -0400 Subject: [PATCH 06/29] Update 40 VCB Garbage.ps1 Renamed plugin title to "Backup Garbage". Moved snapshot names to a setting and added Commvault to defaults. Corrected -contains to -match for regex compare. --- Plugins/60 VM/40 VCB Garbage.ps1 | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Plugins/60 VM/40 VCB Garbage.ps1 b/Plugins/60 VM/40 VCB Garbage.ps1 index 070bee97..34e8da2b 100644 --- a/Plugins/60 VM/40 VCB Garbage.ps1 +++ b/Plugins/60 VM/40 VCB Garbage.ps1 @@ -1,13 +1,18 @@ -$Title = "VCB/Veeam/NetBackup Garbage" -$Header = "VCB/Veeam/Netbackup Garbage: [count]" -$Comments = "The following snapshots have been left over from using VCB/Veeam or Netbackup, you may wish to investigate if these are still needed" +$Title = "Backup Garbage" +$Header = "Backup Garbage: [count]" +$Comments = "The following VMs have snapshots left over from backup products. You may wish to investigate if these are still needed." $Display = "Table" -$Author = "Alan Renouf, Frederic Martin" -$PluginVersion = 1.3 +$Author = "Alan Renouf, Frederic Martin, Dan Barr" +$PluginVersion = 1.4 $PluginCategory = "vSphere" # Start of Settings +# Names used in backup product snapshots. Defaults include VCB, Veeam, NetBackup, and Commvault +$BackupNames = "VCB|Consolidate|veeam|NBU_SNAPSHOT|GX_BACKUP" # End of Settings -$FullVM | Where-Object {$_.snapshot | Foreach-Object {$_.rootsnapshotlist | Where-Object {$_.name -contains "VCB|Consolidate|veeam|NBU_SNAPSHOT"}}} | Sort-Object Name | Select-Object Name \ No newline at end of file +$FullVM | Where-Object {$_.snapshot | Foreach-Object {$_.rootsnapshotlist | Where-Object {$_.name -match $BackupNames}}} | Sort-Object Name | Select-Object Name + +# Change Log +## 1.4 : Renamed to "Backup Garbage" to be more generic. Moved snapshot names to a setting and added Commvault to defaults. Corrected -contains to -match for regex compare. \ No newline at end of file From 7c7070321d03fc503fa29a63f25c777d4351ae04 Mon Sep 17 00:00:00 2001 From: danbarr Date: Thu, 21 Sep 2017 19:45:18 -0400 Subject: [PATCH 07/29] Update 98 vSwitch Security.ps1 First round of edits to this plugin: 1. Ignore ForgedTransmits on vDS Uplink port groups. 2. Expanded ForEach-Object aliases and ran thru VS Code's formatter. 3. Fixed several logic bugs which prevented some checks from returning expected results. 4. Removed unused $vSwitchSecurityCheck setting and redundant plugin variable block. --- Plugins/50 Network/98 vSwitch Security.ps1 | 141 ++++++++++----------- 1 file changed, 68 insertions(+), 73 deletions(-) diff --git a/Plugins/50 Network/98 vSwitch Security.ps1 b/Plugins/50 Network/98 vSwitch Security.ps1 index 08f4aff9..da07a179 100644 --- a/Plugins/50 Network/98 vSwitch Security.ps1 +++ b/Plugins/50 Network/98 vSwitch Security.ps1 @@ -1,14 +1,12 @@ $Title = "vSwitch Security" $Header = "vSwitch and portgroup security settings" -$Comments = "All security options for standard vSwitches should be set to REJECT. Distributed vSwitches may require ForgedTrasmits in the default portgroup but should be disabled in other VM Network portgroups unless expressly required." +$Comments = "All security options for standard and distributed switches and port groups should be set to REJECT unless explicitly required, except for ForgedTrasmits which is required on vDS uplink port groups." $Display = "Table" -$Author = "Justin Mercier, Sam McGeown, John Sneddon" -$PluginVersion = 1.4 +$Author = "Justin Mercier, Sam McGeown, John Sneddon, Ben Hocker, Dan Barr" +$PluginVersion = 1.5 $PluginCategory = "vSphere" # Start of Settings -# Enable Checking of vSwitch security settings? -$vSwitchSecurityCheck = $true # Warn for AllowPromiscuous enabled? $AllowPromiscuousPolicy = $true # Warn for ForgedTransmits enabled? @@ -18,7 +16,6 @@ $MacChangesPolicy = $true # End of Settings # Update settings where there is an override -$vSwitchSecurityCheck = Get-vCheckSetting $Title "vSwitchSecurityCheck" $vSwitchSecurityCheck $AllowPromiscuousPolicy = Get-vCheckSetting $Title "AllowPromiscuousPolicy" $AllowPromiscuousPolicy $ForgedTransmitsPolicy = Get-vCheckSetting $Title "ForgedTransmitsPolicy" $ForgedTransmitsPolicy $MacChangesPolicy = Get-vCheckSetting $Title "MacChangesPolicy" $MacChangesPolicy @@ -28,89 +25,87 @@ $VersionOK = $false if (((Get-PowerCLIVersion) -match "VMware.* PowerCLI (.*) build ([0-9]+)")) { - if ([int]($Matches[2]) -ge 1012425) { - $VersionOK = $true - if ([int]($Matches[2]) -ge 2548067) { - #PowerCLI 6+ - if(!(Get-Module -Name VMware.VimAutomation.Vds -ErrorAction SilentlyContinue)) { - Import-Module VMware.VimAutomation.Vds + if ([int]($Matches[2]) -ge 1012425) { + $VersionOK = $true + if ([int]($Matches[2]) -ge 2548067) { + #PowerCLI 6+ + if (!(Get-Module -Name VMware.VimAutomation.Vds -ErrorAction SilentlyContinue)) { + Import-Module VMware.VimAutomation.Vds + } } - } - else { - # Add required Snap-In - if (!(Get-PSSnapin -name VMware.VimAutomation.Vds -ErrorAction SilentlyContinue)) { - Add-PSSnapin VMware.VimAutomation.Vds + else { + # Add required Snap-In + if (!(Get-PSSnapin -name VMware.VimAutomation.Vds -ErrorAction SilentlyContinue)) { + Add-PSSnapin VMware.VimAutomation.Vds + } } - } - } + } } if ($VersionOK) { - [array] $results = $null + [array] $results = $null - Get-VirtualSwitch | % { - $Output = "" | Select-Object Host,Type,vSwitch,Portgroup,AllowPromiscuous,ForgedTransmits,MacChanges - if($_.ExtensionData.Summary -ne $null) { - $Output.Type = "vDS" - $Output.Host = "*" - } else { - $Output.Type = "vSS" - $Output.Host = $_.VMHost - } - $Output.vSwitch = $_.Name - $Output.Portgroup = "(none)" - $Output.AllowPromiscuous = ($_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.AllowPromiscuous.Value -and $_ExtensionData.Spec.Policy.Security.AllowPromiscuous) - $Output.ForgedTransmits = ($_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.ForgedTransmits.Value -and $_ExtensionData.Spec.Policy.Security.AllowPromiscuous) - $Output.MacChanges = ($_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.MacChanges.Value -and $_ExtensionData.Spec.Policy.Security.AllowPromiscuous) - $results += $Output - } + Get-VirtualSwitch | ForEach-Object { + $Output = "" | Select-Object Host, Type, vSwitch, Portgroup, AllowPromiscuous, ForgedTransmits, MacChanges + if ($_.ExtensionData.Summary -ne $null) { + $Output.Type = "vDS" + $Output.Host = "*" + } + else { + $Output.Type = "vSS" + $Output.Host = $_.VMHost + } + $Output.vSwitch = $_.Name + $Output.Portgroup = "(none)" + $Output.AllowPromiscuous = ($_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.AllowPromiscuous.Value -or $_.ExtensionData.Spec.Policy.Security.AllowPromiscuous) + $Output.ForgedTransmits = ($_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.ForgedTransmits.Value -or $_.ExtensionData.Spec.Policy.Security.ForgedTransmits) + $Output.MacChanges = ($_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.MacChanges.Value -or $_.ExtensionData.Spec.Policy.Security.MacChanges) + $results += $Output + } - Get-VDPortGroup | % { - $Output = "" | Select-Object Host,Type,vSwitch,Portgroup,AllowPromiscuous,ForgedTransmits,MacChanges - $Output.Host = "*" - $Output.Type = "vDS Port Group" - $Output.vSwitch = $_.VDSwitch - $Output.Portgroup = $_.Name - $Output.AllowPromiscuous = $_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.AllowPromiscuous.Value - $Output.ForgedTransmits = $_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.ForgedTransmits.Value - $Output.MacChanges = $_.Extensiondata.Config.DefaultPortConfig.SecurityPolicy.MacChanges.Value - $results += $Output - } + Get-VDPortGroup | ForEach-Object { + $Output = "" | Select-Object Host, Type, vSwitch, Portgroup, AllowPromiscuous, ForgedTransmits, MacChanges + $Output.Host = "*" + if ($_.ExtensionData.Config.Uplink -eq $true) { + $Output.Type = "vDS Uplink Port Group" + } + else { + $Output.Type = "vDS Port Group" + } + $Output.vSwitch = $_.VDSwitch + $Output.Portgroup = $_.Name + $Output.AllowPromiscuous = $_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.AllowPromiscuous.Value + $Output.ForgedTransmits = $_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.ForgedTransmits.Value + $Output.MacChanges = $_.ExtensionData.Config.DefaultPortConfig.SecurityPolicy.MacChanges.Value + $results += $Output + } - $VMH | Where-Object { $_.ConnectionState -eq "Connected" } | % { - $VMHost = $_ - Get-VirtualPortGroup -VMHost $_ -Standard | % { - $Output = "" | Select-Object Host,Type,vSwitch,Portgroup,AllowPromiscuous,ForgedTransmits,MacChanges - $Output.Host = $VMHost.Name - $Output.Type = "vSS Port Group" - $Output.vSwitch = $_.VirtualSwitch - $Output.Portgroup = $_.Name - $Output.AllowPromiscuous = $_.ExtensionData.Spec.Policy.Security.AllowPromiscuous -and ($portgroup.Spec.Policy.Security.MacChanges -eq $null) - $Output.ForgedTransmits = $_.ExtensionData.Spec.Policy.Security.ForgedTransmits -and ($portgroup.Spec.Policy.Security.MacChanges -eq $null) - $Output.MacChanges = $_.Spec.ExtensionData.Policy.Security.MacChanges -and ($portgroup.Spec.Policy.Security.MacChanges -eq $null) - $results += $Output - } - } + $VMH | Where-Object { $_.ConnectionState -eq "Connected" } | ForEach-Object { + $VMHost = $_ + Get-VirtualPortGroup -VMHost $_ -Standard | ForEach-Object { + $Output = "" | Select-Object Host, Type, vSwitch, Portgroup, AllowPromiscuous, ForgedTransmits, MacChanges + $Output.Host = $VMHost.Name + $Output.Type = "vSS Port Group" + $Output.vSwitch = $_.VirtualSwitch + $Output.Portgroup = $_.Name + $Output.AllowPromiscuous = $_.ExtensionData.Spec.Policy.Security.AllowPromiscuous -and ($_.Spec.Policy.Security.AllowPromiscuous -eq $null) + $Output.ForgedTransmits = $_.ExtensionData.Spec.Policy.Security.ForgedTransmits -and ($_.Spec.Policy.Security.ForgedTransmits -eq $null) + $Output.MacChanges = $_.ExtensionData.Spec.Policy.Security.MacChanges -and ($_.Spec.Policy.Security.MacChanges -eq $null) + $results += $Output + } + } - if ($results.Host) { $results | Where-Object { ($_.AllowPromiscuous -eq $AllowPromiscuousPolicy) -or ($_.ForgedTransmits -eq $ForgedTransmitsPolicy) -or ($_.MacChanges -eq $MacChangesPolicy) } | Sort-Object vSwitch,PortGroup } + if ($results.Host) { $results | Where-Object { ($_.AllowPromiscuous -eq $AllowPromiscuousPolicy) -or ($_.ForgedTransmits -eq $ForgedTransmitsPolicy -and $_.Type -ne "vDS Uplink Port Group") -or ($_.MacChanges -eq $MacChangesPolicy) } | Sort-Object vSwitch, PortGroup } } else { - Write-Warning "PowerCLi version installed is lower than 5.1 Release 2" - New-Object PSObject -Property @{"Message"="PowerCLi version installed is lower than 5.1 Release 2, please update to use this plugin"} + Write-Warning "PowerCLi version installed is lower than 5.1 Release 2" + New-Object PSObject -Property @{"Message" = "PowerCLi version installed is lower than 5.1 Release 2, please update to use this plugin"} } -$Title = "vSwitch Security" -$Header = "vSwitch and portgroup security settings" -$Comments = "All security options for standard vSwitches should be set to REJECT. Distributed vSwitches may require ForgedTrasmits in the default portgroup but should be disabled in other VM Network portgroups unless expressly required." -$Display = "Table" -$Author = "Justin Mercier, Sam McGeown, John Sneddon, Ben Hocker" -$PluginVersion = 1.3 -$PluginCategory = "vSphere" - # Changelog ## 1.0 : Initial Release ## 1.1 : Re-written for performance improvements ## 1.2 : Added version check (Issue #71) ## 1.3 : Add Get-vCheckSetting ## 1.4 : Fix Version checking for PowerCLI 6.5 - vSphere is no longer in the product name (Issue #514) - +## 1.5 : Ignore ForgedTransmits on vDS Uplink port groups, removed redundant plugin variable block, fixed logic bugs From 47b3df91b8633c99fbaf73fc828aae1713dda95f Mon Sep 17 00:00:00 2001 From: Dan Barr Date: Sat, 23 Sep 2017 15:50:35 -0400 Subject: [PATCH 08/29] Fix settings logic Fixes the logic so a vSwitch or port group only appears if one of the conditions you've selected to report on is true. --- Plugins/50 Network/98 vSwitch Security.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/50 Network/98 vSwitch Security.ps1 b/Plugins/50 Network/98 vSwitch Security.ps1 index da07a179..c86cccff 100644 --- a/Plugins/50 Network/98 vSwitch Security.ps1 +++ b/Plugins/50 Network/98 vSwitch Security.ps1 @@ -95,7 +95,7 @@ if ($VersionOK) { } } - if ($results.Host) { $results | Where-Object { ($_.AllowPromiscuous -eq $AllowPromiscuousPolicy) -or ($_.ForgedTransmits -eq $ForgedTransmitsPolicy -and $_.Type -ne "vDS Uplink Port Group") -or ($_.MacChanges -eq $MacChangesPolicy) } | Sort-Object vSwitch, PortGroup } + if ($results.Host) { $results | Where-Object { ($_.AllowPromiscuous -and $AllowPromiscuousPolicy) -or ($_.ForgedTransmits -and $ForgedTransmitsPolicy -and $_.Type -ne "vDS Uplink Port Group") -or ($_.MacChanges -and $MacChangesPolicy) } | Sort-Object vSwitch, PortGroup } } else { Write-Warning "PowerCLi version installed is lower than 5.1 Release 2" From 8b7b7405dab34cf9cf738102d13f294fda11a22c Mon Sep 17 00:00:00 2001 From: Sneddo Date: Tue, 3 Oct 2017 10:05:42 +1100 Subject: [PATCH 09/29] Update 620 CBT Status.ps1 Updated logic as per issue #585. Also changed the Header/Comments to reflect the actual setting. --- Plugins/60 VM/620 CBT Status.ps1 | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Plugins/60 VM/620 CBT Status.ps1 b/Plugins/60 VM/620 CBT Status.ps1 index d2868355..9cdf36fd 100644 --- a/Plugins/60 VM/620 CBT Status.ps1 +++ b/Plugins/60 VM/620 CBT Status.ps1 @@ -1,11 +1,3 @@ -$Title = "VM - Display all VMs with CBT not enabled" -$Header = "VM with CBT disabled: [count]" -$Comments = "List all VMs with CBT status disabled. It's not a good option for backup!" -$Display = "Table" -$Author = "Cyril Epiney" -$PluginVersion = 1.1 -$PluginCategory = "vSphere" - # Start of Settings # Should CBT be enabled (true/false) $CBTEnabled = $false @@ -14,7 +6,24 @@ $CBTEnabled = $false # Update settings where there is an override $CBTEnabled = Get-vCheckSetting $Title "CBTEnabled" $CBTEnabled -$FullVm | Where-object {$_.Config.ChangeTrackingEnabled -eq $CBTEnabled} | Select-Object Name, @{Name="Change Block Tracking";Expression={if ($_.Config.ChangeTrackingEnabled) { "enabled" } else { "disabled" }}} | Sort-Object Name +$FullVm | Where-object {$_.Config.ChangeTrackingEnabled -ne $CBTEnabled} | Select-Object Name, @{Name="Change Block Tracking";Expression={if ($_.Config.ChangeTrackingEnabled) { "enabled" } else { "disabled" }}} | Sort-Object Name + +if ($CBTEnabled) +{ + $Header = "VM with CBT disabled: [count]" + $Comments = "List all VMs with CBT status disabled. It's not a good option for backup!" +} +else +{ + $Header = "VM with CBT enabled: [count]" + $Comments = "List all VMs with CBT status enabled." +} + +$Title = "VM - Display all VMs with CBT unexpected status" +$Display = "Table" +$Author = "Cyril Epiney" +$PluginVersion = 1.2 +$PluginCategory = "vSphere" # Change Log -## 1.1 : Added Get-vCheckSetting \ No newline at end of file +## 1.1 : Added Get-vCheckSetting From 714b5b51d3cf5af6a7937adeddee586f5a4d0aa8 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Fri, 6 Oct 2017 21:34:02 -0700 Subject: [PATCH 10/29] Clarity Style --- ...ind VMs in Uncontrolled Snapshot Mode.psd1 | 12 +- Lang/en-GB/vCheck.psd1 | 72 +- .../00 Connection Plugin for vCenter.psd1 | 40 +- ...Find VMs in Uncontrolled Snapshot Mode.ps1 | 100 +- Styles/Clarity/Header-vmware.png | Bin 0 -> 2716 bytes Styles/Clarity/Header.jpg | Bin 0 -> 11190 bytes Styles/Clarity/Style.ps1 | 11710 ++++++++++++++++ Using-Culture.ps1 | 102 +- vCheck.ps1 | 2126 +-- 9 files changed, 12936 insertions(+), 1226 deletions(-) create mode 100644 Styles/Clarity/Header-vmware.png create mode 100644 Styles/Clarity/Header.jpg create mode 100644 Styles/Clarity/Style.ps1 diff --git a/Lang/en-GB/79 Find VMs in Uncontrolled Snapshot Mode.psd1 b/Lang/en-GB/79 Find VMs in Uncontrolled Snapshot Mode.psd1 index 7ca13a50..259c0e3a 100644 --- a/Lang/en-GB/79 Find VMs in Uncontrolled Snapshot Mode.psd1 +++ b/Lang/en-GB/79 Find VMs in Uncontrolled Snapshot Mode.psd1 @@ -1,6 +1,6 @@ -# culture="en-US" -ConvertFrom-StringData @' - pluginActivity = Searching Datastores - pluginStatus = [{0} of {1}] {2} - Complete = completed -'@ +# culture="en-US" +ConvertFrom-StringData @' + pluginActivity = Searching Datastores + pluginStatus = [{0} of {1}] {2} + Complete = completed +'@ diff --git a/Lang/en-GB/vCheck.psd1 b/Lang/en-GB/vCheck.psd1 index 494f54eb..f007d9b6 100644 --- a/Lang/en-GB/vCheck.psd1 +++ b/Lang/en-GB/vCheck.psd1 @@ -1,36 +1,36 @@ -# culture="en-US" -ConvertFrom-StringData @' - setupMsg01 = - setupMsg02 = Welcome to vCheck by Virtu-Al http://virtu-al.net - setupMsg03 = ================================================= - setupMsg04 = This is the first time you have run this script or you have re-enabled the setup wizard. - setupMsg05 = - setupMsg06 = To re-run this wizard in the future please use vCheck.ps1 -Config - setupMsg07 = To get usage information, please use Get-Help vCheck.ps1 - setupMsg08 = - setupMsg09 = Please complete the following questions or hit Enter to accept the current setting - setupMsg10 = After completing this wizard the vCheck report will be displayed on the screen. - setupMsg11 = - configMsg01 = After you have exported the new settings from the configuration interface, - configMsg02 = import the settings CSV file using Import-vCheckSettings -csvfile C:\\path\\to\\vCheckSettings.csv - configMsg03 = NOTE: If vCheckSettings.csv is stored in the vCheck folder, simply run Import-vCheckSettings - resFileWarn = Image File not found for {0}! - pluginInvalid = Plugin does not exist: {0} - pluginpathInvalid = Plugin path "{0}" is invalid, defaulting to {1} - gvInvalid = Global Variables path invalid in job specification, defaulting to {0} - varUndefined = Variable `${0} is not defined in GlobalVariables.ps1 - pluginActivity = Evaluating plugins - pluginStatus = [{0} of {1}] {2} - Complete = Complete - pluginBegin = \nBegin Plugin Processing - pluginStart = ..start calculating {0} by {1} v{2} [{3} of {4}] - pluginEnd = ..finished calculating {0} by {1} v{2} [{3} of {4}] - repTime = This report took {0} minutes to run all checks, completing on {1} at {2} - repPRTitle = Plugin Report - repTTRTitle = Time to Run - slowPlugins = The following plugins took longer than {0} seconds to run, there may be a way to optimize these or remove them if not needed - emailSend = ..Sending Email - emailAtch = vCheck attached to this email - HTMLdisp = ..Displaying HTML results -'@ - +# culture="en-US" +ConvertFrom-StringData @' + setupMsg01 = + setupMsg02 = Welcome to vCheck by Virtu-Al http://virtu-al.net + setupMsg03 = ================================================= + setupMsg04 = This is the first time you have run this script or you have re-enabled the setup wizard. + setupMsg05 = + setupMsg06 = To re-run this wizard in the future please use vCheck.ps1 -Config + setupMsg07 = To get usage information, please use Get-Help vCheck.ps1 + setupMsg08 = + setupMsg09 = Please complete the following questions or hit Enter to accept the current setting + setupMsg10 = After completing this wizard the vCheck report will be displayed on the screen. + setupMsg11 = + configMsg01 = After you have exported the new settings from the configuration interface, + configMsg02 = import the settings CSV file using Import-vCheckSettings -csvfile C:\\path\\to\\vCheckSettings.csv + configMsg03 = NOTE: If vCheckSettings.csv is stored in the vCheck folder, simply run Import-vCheckSettings + resFileWarn = Image File not found for {0}! + pluginInvalid = Plugin does not exist: {0} + pluginpathInvalid = Plugin path "{0}" is invalid, defaulting to {1} + gvInvalid = Global Variables path invalid in job specification, defaulting to {0} + varUndefined = Variable `${0} is not defined in GlobalVariables.ps1 + pluginActivity = Evaluating plugins + pluginStatus = [{0} of {1}] {2} + Complete = Complete + pluginBegin = \nBegin Plugin Processing + pluginStart = ..start calculating {0} by {1} v{2} [{3} of {4}] + pluginEnd = ..finished calculating {0} by {1} v{2} [{3} of {4}] + repTime = This report took {0} minutes to run all checks, completing on {1} at {2} + repPRTitle = Plugin Report + repTTRTitle = Time to Run + slowPlugins = The following plugins took longer than {0} seconds to run, there may be a way to optimize these or remove them if not needed + emailSend = ..Sending Email + emailAtch = vCheck attached to this email + HTMLdisp = ..Displaying HTML results +'@ + diff --git a/Lang/en-US/00 Connection Plugin for vCenter.psd1 b/Lang/en-US/00 Connection Plugin for vCenter.psd1 index 6e298075..9644e449 100644 --- a/Lang/en-US/00 Connection Plugin for vCenter.psd1 +++ b/Lang/en-US/00 Connection Plugin for vCenter.psd1 @@ -1,21 +1,21 @@ -# culture="en-US" - -ConvertFrom-StringData @' - connReuse = Re-using connection to VI Server - connOpen = Connecting to VI Server - connError = Unable to connect to vCenter, please ensure you have altered the vCenter server address correctly. To specify a username and password edit the connection string in the file $GlobalVariables - custAttr = Adding Custom properties - collectVM = Collecting VM Objects - collectHost = Collecting VM Host Objects - collectCluster = Collecting Cluster Objects - collectDatastore = Collecting Datastore Objects - collectDVM = Collecting Detailed VM Objects - collectTemplate = Collecting Template Objects - collectDVIO = Collecting Detailed VI Objects - collectAlarm = Collecting Detailed Alarm Objects - collectDHost = Collecting Detailed VMHost Objects - collectDCluster = Collecting Detailed Cluster Objects - collectDDatastore = Collecting Detailed Datastore Objects - collectDDatastoreCluster = Collecting Detailed Datastore Cluster Objects - collectAlarms = Collecting Alarm Definitions +# culture="en-US" + +ConvertFrom-StringData @' + connReuse = Re-using connection to VI Server + connOpen = Connecting to VI Server + connError = Unable to connect to vCenter, please ensure you have altered the vCenter server address correctly. To specify a username and password edit the connection string in the file $GlobalVariables + custAttr = Adding Custom properties + collectVM = Collecting VM Objects + collectHost = Collecting VM Host Objects + collectCluster = Collecting Cluster Objects + collectDatastore = Collecting Datastore Objects + collectDVM = Collecting Detailed VM Objects + collectTemplate = Collecting Template Objects + collectDVIO = Collecting Detailed VI Objects + collectAlarm = Collecting Detailed Alarm Objects + collectDHost = Collecting Detailed VMHost Objects + collectDCluster = Collecting Detailed Cluster Objects + collectDDatastore = Collecting Detailed Datastore Objects + collectDDatastoreCluster = Collecting Detailed Datastore Cluster Objects + collectAlarms = Collecting Alarm Definitions '@ \ No newline at end of file diff --git a/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 b/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 index b94fb653..53d724c1 100644 --- a/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 +++ b/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 @@ -1,51 +1,51 @@ -#region Internationalization -################################################################################ -# Internationalization # -################################################################################ -# Default language en-US -Import-LocalizedData -BaseDirectory ($ScriptPath + '\lang') -BindingVariable pLang -UICulture en-US -ErrorAction SilentlyContinue - -# Override the default (en-US) if it exists in lang directory -Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable pLang -ErrorAction SilentlyContinue - -#endregion Internationalization - -$Title = "VMs in uncontrolled snapshot mode" -$Header = "VMs in uncontrolled snapshot mode: [count]" -$Comments = "The following VMs are in snapshot mode, but vCenter isn't aware of it. See http://kb.vmware.com/kb/1002310" -$Display = "Table" -$Author = "Rick Glover, Matthias Koehler, Dan Rowe" -$PluginVersion = 1.5 -$PluginCategory = "vSphere" - -# Start of Settings -# End of Settings - -$i=0; -foreach ($eachDS in ($Datastores | Where-Object {$_.State -eq "Available"})) { - Write-Progress -ID 2 -Parent 1 -Activity $pLang.pluginActivity -Status ($pLang.pluginStatus -f $i, $Datastores.count, $eachDS.Name) -PercentComplete ($i*100/$Datastores.count) - $eachDS.Name - $FilePath = $eachDS.DatastoreBrowserPath + '\*\*delta.vmdk*' - $fileList = @(Get-ChildItem -Path "$FilePath" | Select-Object Name, FolderPath, FullName) - $FilePath = $eachDS.DatastoreBrowserPath + '\*\-*-flat.vmdk' - $fileList += Get-ChildItem -Path "$FilePath" | Select-Object Name, FolderPath, FullName - - $i++ - - foreach ($vmFile in $filelist | Sort-Object FolderPath) - { - $vmFile.FolderPath -match '^\[([^\]]+)\] ([^/]+)' > $null - $VMName = $matches[2] - $eachVM = $FullVM | Where-Object {$_.Name -eq $VMName} - if (!$eachVM.snapshot) - { - # Only process VMs without snapshots - New-Object -TypeName PSObject -Property @{ - VM = $eachVM.Name - Datacenter = $eachDS.Datacenter - Path = $vmFile.FullName - } - } - } -} +#region Internationalization +################################################################################ +# Internationalization # +################################################################################ +# Default language en-US +Import-LocalizedData -BaseDirectory ($ScriptPath + '\lang') -BindingVariable pLang -UICulture en-US -ErrorAction SilentlyContinue + +# Override the default (en-US) if it exists in lang directory +Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable pLang -ErrorAction SilentlyContinue + +#endregion Internationalization + +$Title = "VMs in uncontrolled snapshot mode" +$Header = "VMs in uncontrolled snapshot mode: [count]" +$Comments = "The following VMs are in snapshot mode, but vCenter isn't aware of it. See http://kb.vmware.com/kb/1002310" +$Display = "Table" +$Author = "Rick Glover, Matthias Koehler, Dan Rowe" +$PluginVersion = 1.5 +$PluginCategory = "vSphere" + +# Start of Settings +# End of Settings + +$i=0; +foreach ($eachDS in ($Datastores | Where-Object {$_.State -eq "Available"})) { + Write-Progress -ID 2 -Parent 1 -Activity $pLang.pluginActivity -Status ($pLang.pluginStatus -f $i, $Datastores.count, $eachDS.Name) -PercentComplete ($i*100/$Datastores.count) + $eachDS.Name + $FilePath = $eachDS.DatastoreBrowserPath + '\*\*delta.vmdk*' + $fileList = @(Get-ChildItem -Path "$FilePath" | Select-Object Name, FolderPath, FullName) + $FilePath = $eachDS.DatastoreBrowserPath + '\*\-*-flat.vmdk' + $fileList += Get-ChildItem -Path "$FilePath" | Select-Object Name, FolderPath, FullName + + $i++ + + foreach ($vmFile in $filelist | Sort-Object FolderPath) + { + $vmFile.FolderPath -match '^\[([^\]]+)\] ([^/]+)' > $null + $VMName = $matches[2] + $eachVM = $FullVM | Where-Object {$_.Name -eq $VMName} + if (!$eachVM.snapshot) + { + # Only process VMs without snapshots + New-Object -TypeName PSObject -Property @{ + VM = $eachVM.Name + Datacenter = $eachDS.Datacenter + Path = $vmFile.FullName + } + } + } +} Write-Progress -ID 1 -Activity $pLang.pluginActivity -Status $pLang.Complete -Completed \ No newline at end of file diff --git a/Styles/Clarity/Header-vmware.png b/Styles/Clarity/Header-vmware.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7f4bb0927e164bba35e9016643a58b6a54d779 GIT binary patch literal 2716 zcmcgtXE+;-8cn~V_)66%HL6;xYE_B7Ly3qTq_Iazkl1^#irHcN>}pF=LTrknc7%qQ zMYXuK_eas|`+NV~=l;3pIp;mkbKW2CpZ9rAk}>oN6C*bx003ap*8`bedex;o=&xSR zd$YA7mqvrsG}HtD28Zb_>PT;hSmJuxX(DGgR>4Yo&` z?2j}#WVARRYjetKbIWSpmIZOk>2L#ec!0XRa$0;qZ9X7~UtWh-UYAed$sI*Keiehe z3cB}|p4?T^7f=QZspt!;L4?%}MKqwInnn^@Mh~=%A8MP3gG?oK%p`Tqq@I`q4PcK9 ztYrv=2il;6>@Xp2Zf;IKAr3yFj=o{ezM)9}aA*Gr zmw?D;fsx3dC{%E?TX2kfNGvKe&Lb=y6OrH(o#Yq&+%GmcAU-uHJ|#FIH8>$HSW92_1UpPZarTwMJA{rmDE z|DP~W%-OuW$AHnZ_5}c#yZ+IYrWdGx!rOj2R(@t^SHD099~S_|!OhE0(#yr~uB@cA zxyP18Q%^}C$XUTL6MB5t0gtKDtH|dnbPMLsAu;817WNf>* zqcx#KkL}?*Q5PE>Y;phKX`!;Ni6i8KyW@C;ly4nYa8M8$)VeO0VsE?;vdW`m;=_9hMU^6C%bH^j6ZKNRU#N7_MR8k-*!6s)zuewsuSglEHNSQPV@l-lynqv3_< zzkhC_MuiOIAf*rgvg^9@xF!Rv?lE@beDeBPF;&2?zV6NNq(b54-k$m`clu>w( zve(Jfa#}q9K`H4eH{}m76lv?g$*Y2|}R*zqj>3WwQ zuVXoFM$j<9=m4)bIkep@GO%m-PUs5k&6W%)VS0GD^Z90(dB=*d8)#%)u!P=2d8kHv zLeZ}$XoPv>`;u1dY-Wp1xgEW-uS|LI&k-F5Agv7Ah`lcE$m>3zepOu~hNp7`jU6y= z|7{GRgFocedklq(F1KwPSH4KA&mE7g_R$3oxvQ;8#U_a~#_9SK{MRY0NY3Z!Zf4N0 z1q_l0VEE>*Ihusralp;{kkwW<2@U%zl$+`T`O;foKvnxvi2=~%duK-EfntBt7c@md zzjC;(RM=EI1YC2Mg$(sjC9c7%jDHXt-(vMMCc#{XU*|Fe__Zuc4|Tjq3!>YeDxtVc zx>G-|IGxoO?4h*$7K>#2=&x+9a`NgJAnvM75YKoE_B!3w!Fdj6lDd$vCcM>~5Qu0f z>@hCmGECHm3$*c4UrtIlH|>uE?_;`?2YFYs2*0y_fHhM}2f^pJ*+=cLO*1u^o8*wF zoIh%-3TnzNR+u`(3X|_NiwsZ^>qpB<3ZsKkZMT!V0qa#8jiH?1d~zyEEB$pb^8?H9 zat%(L%&jU61N&*@v(3Ksyl~dT3~i5QY3fAYvCSk{TQw#vSJ1t8nhmoS&)uV3XuGx` z{x7ib+B{8jFyc@AFYoECl*h+|)-m;_fA1zv*T+aPbj^C>leI5W`$Z-Y+v=STg@Jap z=>%eEolw7lf7(OawY=p92P4G+8vLrS!pPrF+S7=hROJu#XuTLi+X~P0MJLicc*_{4 zQO`KMMm7?`A4I+C#Tk(+D{;Hk{MJzn$~}Bk>@3r`F15CKK3|}+BU7pAYpCQoZ3`4( z(L3~d?oO${$GZdER5Pnc^FQ3za@k#6TI+^8$ z^rJc$rv_-e)4ky6Idh0h=bc2?X9~f^EQ$4k}e!9pP_{^N=%b+T#_-at3cn@8X zbakUM@mcQKbhOqwv4%}FJQJp8gRdi$lhh>c7=3=3=S}~Ki;!fnxF8Aa)IkQoO0MxqXRy*!8ZYf=` V`o>bfS>8VtuCD_HebBOx`8RMC1El}} literal 0 HcmV?d00001 diff --git a/Styles/Clarity/Header.jpg b/Styles/Clarity/Header.jpg new file mode 100644 index 0000000000000000000000000000000000000000..86b903749e49327ee4c881d7579d956a004802d0 GIT binary patch literal 11190 zcmeHtX;c$w_h%H9O^hI$pu`Oj5NHKN5t6oPK!|{VY(YpPjcfvqfCdPO$`S}5uM5!4 zVnmj(h!NRZjEU?mv_@G4glsCiR4IgNvM}X4=e+Y}&YYR~f15Lvb1RiRwLJH^_x|oy z8}B!Gh@CEu&W?ypn>HbSfj@{1J|f(K5OE2CaCJo(BM=A$#FkAuh^_F^CYaEhbpLbg zxalxL?%)4!h6j%#;5|YWCO4SAZ@<9w{hEJ&{}vn(lVBDTny}B(><6=>C*dbeBJ96E z_rJ7#zsY~;2H!8*tRP`8D)X zSa?Kad_v;YYlNiawDgQjN>(=Y&fR+jg+;&rQT(u?vZ|V1Q(O16skx=K?aycJon75M zy?uYZ>K_~$9vK~bKR&@12tR)MJTp5t4}Sf&vMO4G{#pOd*CvGAf0pcj%9kq4*XAu- zFZrQs3=r*;}9`eC)yAB+?p`iZL?efMB#e)`}fX2lu@02tTS@QM4 z@2vfsv;RHDZvJ0!_8-Rn2VWD2?Q)x7;mN5YFbJ9T>_e*TI;NojP2H0Ejf032$Y`n2^<%l$4^zCR>5}b z)cFS;FRPpkoN8$3NdH+n7RFJHto zBVW?8F-=;%>}(nWZ9bdF-#}z-Ae3zVYayo|9m2QiL2SLeN<{tanzg3rX(W0txZ@A2 zqAXjBN2bhk3qh#imDjj&5+_L&-BTm$BJWL*o)R53iREKCTC!`g>D0P`~nxE(pFw?ldd6Xvcyi`EA2O>Dbc`%<>QIn}*W2Tcc$r7A8}4qpT* z)I{>p7{*MYKQk*N&MH{jD?K#3oYKwFay1xhtKj@proBLT8C<;ISvM0!>?ZG5Mz-kvgWaPHyOW_u%+wNQ$8MGWP6dZP-0_jwNK&9v8;C8l zARQJa<#P$2T0q!EV?sR2~q=~@kqG`%@NX&sk8c-zYVYLN|PQ`9t zD8wPVxEh4xqQ8UC&jlvRubmv2A2$%O%j5Ju)Kl70AQ;A{ekZ*izqRsU-CZZ)khYt2R+-?#?KK~jwPrB zy%`h!wYY51^6Uk5@NIhHI{HsqbmO>mAvD;prNEw}bek^W^-xhX?7E=eY~j+8<($Yc%?uGFIPj z-0ZW}Gp)i=mFa7GvJ*Set6Z{x6YOCV7f!O`*N;kDFuz2{3h`+e-d))dp!J#9n)I56 zv<-xX#e(EQ1)N)$X8G=AImtFCgh6y2N@P%Rt@P0Ny7{e4dm&rzcv=ddu@uZFZ-?sr zpA4;{c6=b*aXsZy+`mNLOO9l}9}=uBQtBW>*{;cD3h5M-3i`r&Z4X=%(mKrb!r2;V zc}dVa=E?e3e6IQjNWO1fPFg4ucLp8I3{>tR?HN6?g4ZBikxZZ&!{!H`fM)eP#@U8-@_db^8$VcC8V98^$pe`zlGwqPJA9lQ4(4%af1$mDNewf7Hf*Fa}Dt%RKSla zd98e*q$~NOAr?fZ{kBe~GEI~0a)b2WCS(pbFSn4ENmDa21yEyoF)tW=gM^Gs+3(rE zu*V%(+WF%Gx*Z3~$u!HRTQSX-pgv@``twV7 z)S|u(iS9#7uzWw<6`3?k{~(9FFc(d}03Fy%CKZx|mN9bVYYS1z^Fg9jH3yjN*4qSr z&fvWc;0LbQ$g*T76QKB7=*}Cg?rZCy5(xIE-}$g|L}+(E3#lgU(p4rvUuM6 zBwgRBkZ!YPXf*8F{m&3Db`c4N2vJ=nv3Y1CaIC|8oW>(HBt41?l;uSCb)YfyMYY^f9(blJv0@>jl^h49I6TNoh)_W4J8ui zTwRSygb@1?Gg?+MK`zEd~sZ? z17(`V9m2F40&FOmphsyTL)7#7VNps7nNAy@fK+vp033#dieKu|yXIEIEA_q#lO zV%-0dS1)%{;ouh4e-W$-J*b5SdFl)|^&kH9Yft<|7}QMvsk9&k>rEl8L^vDFzp>Jd zJO5AUenE@37zC^twcj5G(b?Tp-uk?E-m9P85 z)>kOKE3py>$v$8@N58#=OeW@+07LcKWvnTKhiEIt!E5v~ph zP<_EbTL-x*tQ)_L@D($@f#_OzOmOUXr?B=7Nv!!&3vWQ4vTx`S(XI+YzFco@cr=Im z`Mxfg^mcIQ9Ibn7f^&TX(V@@&WXJ{FHxS}eoa^NEr?!}&Hu2s7;(jI!^n;Q^t$`c^n*3B{6Ey1T==Szn9qsss`#IlT7sSz`n3~Ieki<)G!C?^%H9zXp zek%>o6+MtXw6z0@gsl~!wX@e```?)*(h$>g6FEc+3Vvu&Vek;tQ!BA?{=#U^Ny5pG zCa_y179gEh)I%O06X2<6B=ItEybV|uIHfT(Ie4J=`5rx@UV?LcC^?uMVmE#FaX7B! z-rGnVGHb;|iv4kHrKH3)z+srNGWk5JHW3p($);LwAey!MlsS3A^Z48%jHc`W7WA;H z!EwEF1j>XqpAAGhOILj9mU+_!`^VCD!&bkCx46G#9Ny7q80p4Tz6Kpmafz*;s}YhoH=^b5I(1%^jGO#19Jq!wlIzCmv^fsB>-oR}va(5g0Xd zwPx2X=53tPO_?gyQ0-$TrU4qjGFyKmTSSS%WIHy4?o=igw*L##DpRI={q(#C!QBlq z;m0aZT=;MS8}$J4;qrXD@`Ad~3_d9K_w4P*PK9}nK8{{W_xZM9(_3Gggte1=V(+d$ z+g_FY-WGjO^@Iz7s%~;+h;k@+$;i<)%IM_rsmXT+OX_!yzK#ff0xkMzDZ5n~@f%N2 z_l!gu->qG&cT;@sc$DgL+&Qo%%*Z-^mOLRZx&&p2PdES<*LvGn8lx+ieA*U?F8Gb# zmTjw57qnobN9WOYbLz$e#)ATbU?W>6=UA$EISe=FYZ9)c#J0@Zly08>^gDJJa~dW6 z9ldXX_H?-$H6fbu1n3mBDnUy?OJ^xYEDMySK^Rq{6I3r~FqrAtMX=zDN~&PJEBgei zjzl92lgA855~O-{w%cW1k1iitHTA_0TP~?BFK-|+BWg%62mmhRER5WuAJwVMi@d0AoNOLMul>62)Ro=cSuWCr9PCV}}F9`u-bC^G_SGMCb zZS8Aulh~>VTx#DRZ(xvci&;W%Aw-E_guD4$_;JARgRGCc9S_b97hmWBc0H_X*@`80cF=*erU7e(Zz*3K_JUBM&Xs34jV6D5t z2U61>SHCf0-GH)#x7+lgl!Oh6Jr;_Qrz-XyJLs`9Mcr3EYn$sXt%|8AW@YZeQO(CwLrNryo%Wi=?!&xK$(3WLn|em?OoD*4JoIVs0h!T3j62A zzVs#Zroa#3GHYn(%xSQUvsf$kCF%ofJ;;000DHRXYT{g+GNa#Ds6J7$HiW=?55=xmZeVa;=)QyQYup|Du-x$kVMsL8>FrX52`|0y$;YGfVwfJ`)o_`V zey0P5{0b>OT4D$U2q?KNjGHuN^j2aF1Q$ng+s&7@f&qdI$_+FU3ULH;?;E+wcd+!w zR?EhDB# zpfA^wykma-aamMNWUby#W&c~5EPB4$g^J+e8 z9qi%)jWZzYIkJ0g2#aiNeQIkF4P+-aDqHpYlYdFAJmiVf0#NUPNH|;Tu-QQH9{md{ ze$N3O54|Kmy;V{1!96PK!s^T1F?ag{D8vbVk)3 zdS&-$pJUC%#yed@z31y4e=9Vf|B`c9@qbyP+) zdOuOG8Ou2Hi;vlfKgK*=Dv!Mv&g}LSxu83*-aGKmi^`J)Zb=R)hO2luP7A?k5R+lG zg)`rdeYRe_C}cW*dxCOah-m&D+MM*3t7VJlk<;+oiI~(B@tN>s@{J|dA;~$=suvb+ zJ*z16v30twVb}&j(J(L375NirE87W-R)z4F2=8T=NyF?XiO|`I1V^;YT8(Mb4@0YDm+o8{0KMfyAY=#9-ZMIXJ z@qPEX#0&TbE

cqL5GE5|T8`8xI{^mZ*kx4b)5

xr@jz@DeY*0<`^U%ylG>m4P z9q%XRtu?8gM$4wAW(?CuUkrTj4C+qKE*0y)XjcQsO_nMG6m$rVvQgOKhgj*Umv}7< zkH%JIT5G&2ba-p)Q4jSA$z89#{0D`{l8(0}(=%89X^wVG$oadyDIh%bQUHP2LaEKJ za2^%i`{eNX&$=Bs)&D#@y1VDH4Xq`fZENV^tgq+fdgaPDTZ2EQjNuo~@8mGd`w{_nNtYk1Ms#Z)BWV$} zp3F|)bY6fOXHelX z(3YpI((7zqK`Xnt6c3y`ys|*Mj?rjI)gzBza)HdgZ5y4QLl zajL?9>b2eTs#`tYHFO>CGHYr9GITX|s`uVeB+}+-cZyqV#14P&J0Cx}m{HS;mYo|4 zdEtWzGg)1A1M=Hl8df>eE-hz|#GRjgWd8J%$SXci0Xq@jrk@r6k<>#R;$a{I$`VQl zJNpep6SprVfSWGc1(}NN89mBn76NuRBRko;i%fwHjh#+bwam}dGa8otKp5&hI;Xb> zpieSA8peD0dy&{-rjvlJ-PxuJUF(|2tWX@{q(cWqzEC}g7t(Xln~6I4U3jfk_Lh0H zm24bbyCFFZE4>m4wFQ#*xZl@}(PoeiK@Tx1;X%5PwMz53C?in5Ht#P?xnsJuww`FU2|Rr~ zrkv}(o0_T9YH*=x0})-HR^e7=?6H%G6}o=+uv&jz6s zcO;VmnmsDJ!Ka;CQs{+6dFGibn$5mzV(~oIH|QIR0r^UxTh-CJ(JeIM9hgV(X!WZqkla^R9MT3(pMChgY>A^dbw*?#8P}m%IT1E-NW5wIKx<)2_;McOUd-Th(gSBn z57qym(oFr<0M(P;8R6casC47R$hYV9t<4fooG^N0+EsYW^FmvmgO+Yb|AnMXl(LI@ zWbnMlOle(jgVKp2YM#|r*P*&Pn}m-q7?uLQS=er`E1sW<(+_(%XiO&sJE))A^uHka zac&c8NeF%uxj-RetmF{ne2pJ2F%+MdU{FZbzP7C9-B!^dKep-kQf+NmM$ol-hZ?^N z1gGARPMI}Wl)0m2CzI-`+g{Cj#fa=RmoZ z;aH)O!&S#72CmA+vhIB89Di8os21IOFVc0m_~zW};~&LMDK3OP2mgC!Jrt%r|niI;upHTLCB{gMb3=@9F>S^ zBRyo&0qc7H@NgN!4Zoxd>$yI(AU;hzHOx{9pH_zC#7Bu+z-^2Q!rfk!9OhSi8fh=I z8(8R*9R|eN31n5GL*U+3lqO;Qo9q2kr(T?L17oQzWMo8g<{y>w$v!zDHFH<3ecpWG`qD{h)jtW|=H&2lz_@gJ%_bjX{UT*P!FFKJY&%lJfABYvo9*5A&#|GjOc`HgOV!b6T z61^D;OA=t0+zE?KysN%)XKEIVA@J43l?~`TrvG zsT+u=%>6Lp)XQ|7WIFlccrnHg#yzo26Dcu4LU5UR96!OHgcBM#*64)KRG3*06Oknc zHV~7jN&FlJ9=f=J$WA0qIYicYdZcTds2M6QJs5q^a?&&C+Fi1ReAeR`&x%?@nNVl+g52%FVZE(miD4z(HbohaZLx-;eh}Ym zz2qfWp3rgytTPOtB*2R|(}l{6C)^ajBrB1&5UqxYiZDduKvD@`#iTb#f7mKzfVNZO z1omZ1{G)j03e}AQWR}bpyjA;Y7MEIG7EhlOf#Zz7&!6v8rsUzHbv(RJR-LLJcZ;Zf z?I^ijdx&c}eE!pGleX6;b4Ox~6H?RbT?aqqo(z0E*Fjf`xjIvD;zUnp$=&p%-~o*R z-P8dfU-S=D0xa;RD2pH{$7jD>o=D@xW+v`U!9h`gP;?Kt^jxTnPs4F?5_L^|d#$Z8FleWJbW78^S2Ed=#!eaG{N~CzT`>rr49k=yh7cBLat7o(m*wsG%bNbsTyw2yLj7G~zB(>y`iC|qh{`gT zgbEouA?mmDV^)(TfmeE3j5bR`9o$EL`0%xMF_>d`e7f~>O=+aFqogiACo-1X)h07-%nNg#^>VOY-nulr0yO=rj~ihV2NYqve)qA z2d$Y3kC$UB)m|SVH!m+KGDstEcQO>5>0xby!g%JpCHC!UeqaNoGZq<1QU|Kpg5I`U zcJaMUvpShC7+qlkvUX6#0?X{#;Agl4L{kl%w^@jIGLO4o5tqn2?{mA{MU8%VC^yul zsIhvmDbM`cT5{~xSDJU*T?73;^!pWm%RA%ts7hz##fvkqrd--yh^MZWc-^_;bH(k= zoJsN;TnPdhJup69>w}Y;X8G%;&8IlJ5-lj3kIH84Y6h(erYrc9jGY9FfTpqL`qV3( zUFQZF49nW(BNwT%X^z*>autSZU8OL9VTK4KY-4E)w()acf8&_6nyF`#Jat~^X9j;Nj21Q?vhS~X zR2%%;N+`!AcfZMr>bq+fxfXerljj8!uD!*%ffb2g>Z1d6bGlD^H3U_ii8rz`1Yeu^ zy{UJ0c_esTVRP$>bN}mjmkq=j*%-bB)s3387iB_eVl2^Xkh%XM;hVr+a)3P9j=$b4 z4RHa=7?S2mx+?vcy5pQa{goPP@vFH&kZ6;eMV!+z`>J zUaoXrP~ld3P{AQu`}E;nlUELha*c;;aaArOzs~qPn(?gs^oV})RsWS(X5}R5@;XO! zP}+>PAiX3vyQi}w@^2iQRU<5PCdX1O@r&--0a3~MH;Jwfp|-9G3T4$4)iZJY1_Qbz z#trhVTh~`JZjYMs(z=VyGV1=id%XBIhQn1Qk_Frx)J|d|i0zwTvi5;v-3)E0q;J}C zjDAZyY?SFQ+#9*pjXp3^ojGwLT2SOMvZ_1KN1ZB!Oyiv?^C%B)X;0#tWE5~ffJtMTMx~N#o%jtFf{;j( zF8HPKp{a zqHvrp?)aK09twL%JOG))UHYAb`^pLuF98;{J;F4pc3!q0+%l0oH8n3Y8DrXC3pV{s z-OzvDH>p%*FB^P_1xxV#Bo_)=7v0>dbW2BPpgtci0o_5DI5b1ClG@GHqA2 zu5%;7Y^a|)pi&h4WM1!Mn5TxZH>?87!?h>BeGTlJ&_}yR#9Y$=^~+n$kM{^l$C}G* zyp22_&KZ3peaSOEKZ1$quby)r^xkVCwlXC7jBqMR1~NGEAeoRjm?q@nkigWWQ}e!@ z^e*iC6X5XT*@$H{#=jK|V!-A;*{7+82FVhGYisSY^2$j3FQW;wrjk?A>~bh&F*ypm zqe|Lv#S=ULMT^}aF+XZ+zrHLNk|ogp3i?oq-fT#q- zX35lp2-bY-KKC2QpHtFr;tQEWt zo`B+6cWJ(@27U9GC+QH|=`GP$=$@^2YK*iW_KAUC@+#oY7S88xUFLoW(A&C6wa=WYFQR*00M@AO?7B<*MsG zYRdGlhea#`dnox#JX~!mlYU{>MA#I)w<@zyCc}~J#aWV`Sg&}ntCk#f;c1ZJQMg!; z0kw!N37=!LPzttZ+jlT(q1ZsciMmpTbsn_!;Bm4rNTzp8;e8V*5j^;ETEB6GXeeCX zM7Te3*ed{_Q4{jKoZ5>=yUqoKx7b;`y=Vq;#xL^B!;FL8>?oabcH3dp>-OlAXGO#m zI_ZsPQORY_f`;^wiP*nxSm|F>}_Q?Ne*>>NRG$C< literal 0 HcmV?d00001 diff --git a/Styles/Clarity/Style.ps1 b/Styles/Clarity/Style.ps1 new file mode 100644 index 00000000..a1e89e73 --- /dev/null +++ b/Styles/Clarity/Style.ps1 @@ -0,0 +1,11710 @@ +# Start of Settings +# Show table of centents in report? +$ShowTOC = $true +# Number of columns in table of contents +$ToCColumns = 1 +# End of Settings + +$StyleVersion = 1.4 + +# Define Chart Colours +$ChartColours = @("377C2B", "0A77BA", "1D6325", "89CBE1") +$ChartBackground = "FFFFFF" + +# Set Chart dimensions (WidthxHeight) +$ChartSize = "200x200" + +# Header Images +Add-ReportResource "Header-vCheck" ($StylePath + "\Header.jpg") -Used $true +Add-ReportResource "Header-VMware" ($StylePath + "\Header-vmware.png") -Used $true + +# Hash table of key/value replacements +if ($GUIConfig) { + $StyleReplace = @{"_HEADER_" = ("'$reportHeader'"); + "_SCRIPT_" = "Get-ConfigScripts"; + "_CONTENT_" = "Get-ReportContentHTML"; + "_CONFIGEXPORT_" = ("'

'") + "_TOC_" = ("''")} +} else { + $StyleReplace = @{"_HEADER_" = ("'$reportHeader'"); + "_CONTENT_" = "Get-ReportContentHTML"; + "_CONFIGEXPORT_" = ("''") + "_TOC_" = "Get-ReportTOC"} +} + +#region Function Definitions +<# + Get-ReportHTML - *REQUIRED* + Returns the HTML for the report +#> +function Get-ReportHTML { + foreach ($replaceKey in $StyleReplace.Keys.GetEnumerator()) { + $ReportHTML = $ReportHTML -replace $replaceKey, (Invoke-Expression $StyleReplace[$replaceKey]) + } + + return $reportHTML +} + +<# + Get-ReportContentHTML + Called to replace the content section of the HTML template +#> +function Get-ReportContentHTML { + $ContentHTML = "" + + foreach ($pr in $PluginResult) { + if ($pr.Details) { + $ContentHTML += Get-PluginHTML $pr + } + } + return $ContentHTML +} +<# + Get-PluginHTML + Called to populate the plugin content in the report +#> +function Get-PluginHTML { + param ($PluginResult) + + $FinalHTML = $PluginHTML -replace "_TITLE_", $PluginResult.Header + $FinalHTML = $FinalHTML -replace "_COMMENTS_", $PluginResult.Comments + $PluginResult.Details = $PluginResult.Details -replace "", "" + $PluginResult.Details = $PluginResult.Details -replace "", "" + $FinalHTML = $FinalHTML -replace "_PLUGINCONTENT_", $PluginResult.Details + $FinalHTML = $FinalHTML -replace "_PLUGINID_", $PluginResult.PluginID + + return $FinalHTML +} + +<# + Get-ReportTOC + Generate table of contents +#> +function Get-ReportTOC { + $TOCHTML = @" +
" + + return $TOCHTML +} +#endregion + +# Report HTML structure +$ReportHTML = @" + + + + _HEADER_ + + + + + +
+
+
_HEADER_
+
+
+
+ + + + + +
+ vCheck + + VMware +
+ _CONTENT_ + _CONFIGEXPORT_ +
+ _TOC_ +
+ +
 
+
+

vCheck v$($vCheckVersion) by Alan Renouf generated on $($ENV:Computername) on $($Date.ToLongDateString()) at $($Date.ToLongTimeString())

+
+
+ + +"@ + +# Structure of each Plugin +$PluginHTML = @" + +
 
+ + + + +
_TITLE_
_COMMENTS_
_PLUGINCONTENT_
+ +"@ diff --git a/Using-Culture.ps1 b/Using-Culture.ps1 index 6c44be2a..0fd8b161 100644 --- a/Using-Culture.ps1 +++ b/Using-Culture.ps1 @@ -1,51 +1,51 @@ -<# -.NOTES - Use-Culture.ps1 - - This script allows you to test your internationalisation files. - With this script there is no need to switch your complete OS to another language. - Run the script from the folder where the vCheck.ps1 file is located. - - The script is based on following blog posts: - - Using-Culture -Culture culture -Script {scriptblock} - https://blogs.msdn.microsoft.com/powershell/2006/04/25/using-culture-culture-culture-script-scriptblock/ - - Windows PowerShell 2.0 String Localization - https://rkeithhill.wordpress.com/2009/10/21/windows-powershell-2-0-string-localization/ - - Use a language code from https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx - - Examples: # Run vCheck with the French language code - Using-Culture -culture fr-FR -script {.\vCheck.ps1} - - # Run vCheck with the Spanish language code - Using-Culture -culture es-ES -script {.\vCheck.ps1} - - Changelog - ============================ - 1.0 - Luc Dekens - - initial version -#> - -function Using-Culture ([System.Globalization.CultureInfo]$culture =(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}"), - [ScriptBlock]$script=(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}")) -{ - $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture - $OldUICulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture - try { - [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture - [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture - Invoke-Command $script - } - finally { - [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture - [System.Threading.Thread]::CurrentThread.CurrentUICulture = $OldUICulture - } -} - -Using-Culture -culture en-US -script {.\vCheck.ps1} - -#Using-Culture -culture af-ZA -script {.\vCheck.ps1} -#Using-Culture -culture de-DE -script {.\vCheck.ps1} -#Using-Culture -culture fr-FR -script {.\vCheck.ps1} +<# +.NOTES + Use-Culture.ps1 + + This script allows you to test your internationalisation files. + With this script there is no need to switch your complete OS to another language. + Run the script from the folder where the vCheck.ps1 file is located. + + The script is based on following blog posts: + + Using-Culture -Culture culture -Script {scriptblock} + https://blogs.msdn.microsoft.com/powershell/2006/04/25/using-culture-culture-culture-script-scriptblock/ + + Windows PowerShell 2.0 String Localization + https://rkeithhill.wordpress.com/2009/10/21/windows-powershell-2-0-string-localization/ + + Use a language code from https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx + + Examples: # Run vCheck with the French language code + Using-Culture -culture fr-FR -script {.\vCheck.ps1} + + # Run vCheck with the Spanish language code + Using-Culture -culture es-ES -script {.\vCheck.ps1} + + Changelog + ============================ + 1.0 - Luc Dekens + - initial version +#> + +function Using-Culture ([System.Globalization.CultureInfo]$culture =(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}"), + [ScriptBlock]$script=(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}")) +{ + $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture + $OldUICulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture + try { + [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture + [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture + Invoke-Command $script + } + finally { + [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture + [System.Threading.Thread]::CurrentThread.CurrentUICulture = $OldUICulture + } +} + +Using-Culture -culture en-US -script {.\vCheck.ps1} + +#Using-Culture -culture af-ZA -script {.\vCheck.ps1} +#Using-Culture -culture de-DE -script {.\vCheck.ps1} +#Using-Culture -culture fr-FR -script {.\vCheck.ps1} diff --git a/vCheck.ps1 b/vCheck.ps1 index 0e423536..1fb6ed84 100644 --- a/vCheck.ps1 +++ b/vCheck.ps1 @@ -1,1063 +1,1063 @@ -<# -.SYNOPSIS - vCheck is a PowerShell HTML framework script, 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. -.DESCRIPTION - vCheck Daily Report for vSphere - - 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. - - This script picks on the key known issues and potential issues scripted as - plugins for various technologies written as powershell scripts and reports - it all in one place so all you do in the morning is check your email. - - One of they key things about this report is if there is no issue in a particular - place you will not receive that section in the email, for example if there are - no datastores with less than 5% free space (configurable) then the disk space - section in the virtual infrastructure version of this script, it will not show - in the email, this ensures that you have only the information you need in front - of you when you get into the office. - - This script is not to be confused with an Audit script, although the reporting - framework can also be used for auditing scripts too. I dont want to remind you - that you have 5 hosts and what there names are and how many CPUs they have each - and every day as you dont want to read that kind of information unless you need - it, this script will only tell you about problem areas with your infrastructure. - -.NOTES - File Name : vCheck.ps1 - Author : Alan Renouf - @alanrenouf - Version : 6.24 - - Thanks to all who have commented on my blog to help improve this project - all beta testers and previous contributors to this script. - -.LINK - http://www.virtu-al.net/vcheck-pluginsheaders/vcheck -.LINK - https://github.com/alanrenouf/vCheck-vSphere/ - -.INPUTS - No inputs required -.OUTPUTS - HTML formatted email, Email with attachment, HTML File - -.PARAMETER config - If this switch is set, run the setup wizard - -.PARAMETER Outputpath - This parameter specifies the output location for files. - -.PARAMETER job - This parameter lets you specify an xml config file for this invokation -#> -#Requires -Version 3.0 -[CmdletBinding()] -param ( - [Switch]$config, - - [Switch]$GUIConfig, - - [ValidateScript({ Test-Path $_ -PathType 'Container' })] - [string]$Outputpath=$Env:TEMP, - - [ValidateScript({ Test-Path $_ -PathType 'Leaf' })] - [string]$job -) - -$vCheckVersion = "6.24" -$Date = Get-Date - -# Setup all paths required for script to run -$ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) -$PluginsFolder = $ScriptPath + "\Plugins\" - -#region Internationalization -################################################################################ -# Internationalization # -################################################################################ -# Default language en-US -Import-LocalizedData -BaseDirectory ($ScriptPath + '\lang') -BindingVariable lang -UICulture en-US -ErrorAction SilentlyContinue - -# Override the default (en-US) if it exists in lang directory -Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable lang -ErrorAction SilentlyContinue - -#endregion Internationalization - -#region functions -################################################################################ -# Functions # -################################################################################ -<# Write timestamped output to screen #> -function Write-CustomOut ($Details) { - $LogDate = Get-Date -Format "HH:mm:ss" - Write-OutPut "[$($LogDate)] $Details" -} - -<# Placeholder for now, just return the setting passed to it. Eventually this - will be used for new settings handling #> -function Get-vCheckSetting -{ - param - ( - [string]$Module, - [string]$Setting, - $default - ) - - return $default -} - -<# Search $file_content for name/value pair with ID_Name and return value #> -Function Get-ID-String ($file_content, $ID_name) { - if ($file_content | Select-String -Pattern "\$+$ID_name\s*=") { - $value = (($file_content | Select-String -pattern "\$+${ID_name}\s*=").toString().split("=")[1]).Trim(' "') - return ($value) - } -} - -<# Get basic information abount a plugin #> -Function Get-PluginID ($Filename) { - # Get the identifying information for a plugin script - $file = Get-Content $Filename - $Title = Get-ID-String $file "Title" - if (!$Title) { $Title = $Filename } - $PluginVersion = Get-ID-String $file "PluginVersion" - $Author = Get-ID-String $file "Author" - $Ver = "{0:N1}" -f $PluginVersion - - return @{ "Title" = $Title; "Version" = $Ver; "Author" = $Author } -} - - -Function Invoke-Settings { - - <# - .DESCRIPTION - Run through settings for specified file, expects question on one line, and variable/value on following line - .NOTES - Updated: 20150428 - Updated By: Kevin Kirkpatrick (@vScripter - Twitter/GitHub) - Update Notes: - - Remove Write-Host in favor of Write-Warning; this was based on setting the color of Write-Host to 'warning' colors - - converted function to advanced function - - moved parameters out of function declaration and into the param declaration - - moved all code into the PROCESS block - - improved code spacing for improved readability - - added comment based help section for notes/comments - #> - - [CmdletBinding(PositionalBinding = $true)] - param ( - [parameter(Position = 0)] - $Filename, - [parameter(Position = 1)] - $GB - ) - - PROCESS { - - $file = Get-Content $filename - $OriginalLine = ($file | Select-String -Pattern "# Start of Settings").LineNumber - $EndLine = ($file | Select-String -Pattern "# End of Settings").LineNumber - - if (!(($OriginalLine + 1) -eq $EndLine)) { - - $Array = @() - $Line = $OriginalLine - $PluginName = (Get-PluginID $Filename).Title - - If ($PluginName.EndsWith(".ps1", 1)) { - - $PluginName = ($PluginName.split("\")[-1]).split(".")[0] - - } # end if - - Write-Warning -Message "`n$PluginName" - - do { - - $Question = $file[$Line] - $Line++ - $Split = ($file[$Line]).Split("=") - $Var = $Split[0] - $CurSet = $Split[1].Trim() - - # Check if the current setting is in speech marks - $String = $false - if ($CurSet -match '"') { - $String = $true - $CurSet = $CurSet.Replace('"', '').Trim() - } # end if - - $NewSet = Read-Host "$Question [$CurSet]" - - If (-not $NewSet) { - $NewSet = $CurSet - } # end if - - If ($String) { - $Array += $Question - $Array += "$Var= `"$NewSet`"" - } Else { - $Array += $Question - $Array += "$Var= $NewSet" - } # end if/else - - $Line++ - - } Until ($Line -ge ($EndLine - 1)) - - $Array += "# End of Settings" - - $out = @() - $out = $File[0..($OriginalLine - 1)] - $out += $array - $out += $File[$Endline..($file.count - 1)] - - if ($GB) { - $out[$SetupLine] = '$SetupWizard = $False' - } # end if - - $out | Out-File $Filename - - } # end if - - } # end PROCESS block - -} # end Function Invoke-Settings - -Function Invoke-HTMLSettings { - - <# - .DESCRIPTION - Run through settings for specified file, expects question on one line, and variable/value on following line. - Outputs settings to HTML file, which accepts input, and can create a configuration file. - .NOTES - Updated: 20160830 - Updated By: David Seibel - Update Notes: - - Initial creation - #> - - [CmdletBinding(PositionalBinding = $true)] - param ( - [parameter(Position = 0)] - $Filename, - [parameter(Position = 1)] - $GB - ) - - PROCESS { - - $file = Get-Content $filename - $OriginalLine = ($file | Select-String -Pattern "# Start of Settings").LineNumber - $EndLine = ($file | Select-String -Pattern "# End of Settings").LineNumber - - if (!(($OriginalLine + 1) -eq $EndLine)) { - - $Line = $OriginalLine - $PluginInfo = Get-PluginID $Filename - $PluginName = $PluginInfo.Title - - $htmlOutput = "" - If ($PluginName.EndsWith(".ps1", 1)) { - $PluginName = ($PluginName.split("\")[-1]).split(".")[0] - } # end if - - $htmlOutput += "" - - do { - $Question = $file[$Line] - $QuestionWithoutHash = $Question.Replace("# ", "") - $Line++ - $Split = ($file[$Line]).Split("=") - $Var = $Split[0].Trim() - if ($Split.count -gt 1) { - $CurSet = $Split[1].Trim() - # Check if the current setting is in speech marks - $String = $false - if ($CurSet -match '"') { - $String = $true - $CurSet = $CurSet.Replace('"', '').Trim() - } # end if - - $htmlOutput += "`n" - } - } Until ($Line -ge ($EndLine - 1)) - - $htmlOutput += "
$QuestionWithoutHash
" - $PluginConfig += New-Object PSObject -Property @{ - "Details" = $htmlOutput; - "Header" = $PluginName; - "PluginID" = $PluginName; - } - - return $PluginConfig - } # end if - - } # end PROCESS block - -} # end Function Invoke-HTMLSettings - -<# Replace HTML Entities in string. Used to stop
tags from being mangled in tables #> -function Format-HTMLEntities { - param ([string]$content) - - $replace = @{ - "<" = "<"; - ">" = ">"; - } - - foreach ($r in $replace.Keys.GetEnumerator()) { - $content = $content -replace $r, $replace[$r] - } - return $content -} - -<# Takes an array of content, and optional formatRules and generated HTML table #> -Function Get-HTMLTable { - param ($Content, $FormatRules) - - # Use an XML object for ease of use - $XMLTable = [xml]($content | ConvertTo-Html -Fragment) - $XMLTable.table.SetAttribute("width", "100%") - - # If only one column, fix up the table header - if (($content | Get-Member -MemberType Properties).count -eq 1) - { - $XMLTable.table.tr[0].th = (($content | Get-Member -MemberType Properties) | Select-Object -ExpandProperty Name -First 1).ToString() - } - - # If format rules are specified - if ($FormatRules) { - # Check each cell to see if there are any format rules - for ($RowN = 1; $RowN -lt $XMLTable.table.tr.count; $RowN++) { - for ($ColN = 0; $ColN -lt $XMLTable.table.tr[$RowN].td.count; $ColN++) { - if ($FormatRules.keys -contains $XMLTable.table.tr[0].th[$ColN]) { - # Current cell has a rule, test to see if they are valid - foreach ($rule in $FormatRules[$XMLTable.table.tr[0].th[$ColN]]) { - if ($XMLTable.table.tr[$RowN].td[$ColN]."#text") - { - $value = $XMLTable.table.tr[$RowN].td[$ColN]."#text" - } - else - { - $value = $XMLTable.table.tr[$RowN].td[$ColN] - } - if ($value -notmatch "^[0-9.]+$") { - $value = """$value""" - } - if (Invoke-Expression ("{0} {1}" -f $value, [string]$rule.Keys)) { - # Find what to - $RuleScope = ([string]$rule.Values).split(",")[0] - $RuleActions = ([string]$rule.Values).split(",")[1].split("|") - - switch ($RuleScope) { - "Row" { - for ($TRColN = 0; $TRColN -lt $XMLTable.table.tr[$RowN].td.count; $TRColN++) { - $XMLTable.table.tr[$RowN].selectSingleNode("td[$($TRColN + 1)]").SetAttribute($RuleActions[0], $RuleActions[1]) - } - } - "Cell" { - if ($RuleActions[0] -eq "cid") { - # Do Image - create new XML node for img and clear #text - $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]")."#text" = "" - $elem = $XMLTable.CreateElement("img") - $elem.SetAttribute("src", ("cid:{0}" -f $RuleActions[1])) - # Add img size if specified - if ($RuleActions[2] -match "(\d+)x(\d+)") { - $elem.SetAttribute("width", $Matches[1]) - $elem.SetAttribute("height", $Matches[2]) - } - - $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]").AppendChild($elem) | Out-Null - # Increment usage counter (so we don't have .bin attachments) - Set-ReportResource $RuleActions[1] - } else { - $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]").SetAttribute($RuleActions[0], $RuleActions[1]) - } - } - } - } - } - } - } - } - } - return (Format-HTMLEntities ([string]($XMLTable.OuterXml))) -} - -<# Takes an array of content, and returns HTML table with header column #> -Function Get-HTMLList { - param ([array]$content) - - if ($content.count -gt 0) { - # Create XML doc from HTML. Remove colgroup and header row - if ($content.count -gt 1) { - [xml]$XMLTable = $content | ConvertTo-HTML -Fragment - $XMLTable.table.RemoveChild($XMLTable.table.colgroup) | out-null - $XMLTable.table.RemoveChild($XMLTable.table.tr[0]) | out-null - $XMLTable.table.SetAttribute("width", "100%") - } else { - [xml]$XMLTable = $content | ConvertTo-HTML -Fragment -As List - } - - # Replace the first column td with th - for ($i = 0; $i -lt $XMLTable.table.tr.count; $i++) { - $node = $XMLTable.table.tr[$i].SelectSingleNode("/table/tr[$($i + 1)]/td[1]") - $elem = $XMLTable.CreateElement("th") - $elem.InnerText = $node."#text" - $trNode = $XMLTable.SelectSingleNode("/table/tr[$($i + 1)]") - $trNode.ReplaceChild($elem, $node) | Out-Null - } - - # If only one column, fix up the table header - if (($content | Get-Member -MemberType Properties).count -eq 1) - { - $XMLTable.table.tr[0].th = (($content | Get-Member -MemberType Properties) | Select-Object -ExpandProperty Name -First 1).ToString() - } - - return (Format-HTMLEntities ([string]($XMLTable.OuterXml))) - } -} - -<# Returns HTML fragment for chart. Calls Get-ChartResource to generate chart image #> -function Get-HTMLChart { - param ( - [string]$cidbase, - [Object[]]$ChartObjs - ) - $html = "" - $i = 0 - foreach ($ChartObj in $ChartObjs) { - $i++ - $base64 = Get-ChartResource $ChartObj - $cid = $cidbase + "-" + $i - Add-ReportResource -cid $cid -ResourceData $Base64 -Type "Base64" -Used $true - $html += "" - } - return $html -} - -<# Create a new Chert object, this will get fed back down the output stream as part - of plugin processing. This allows us to keep the same interface for plugins content #> -function New-Chart { - param ( - [int]$height, - [int]$width, - [Parameter(Mandatory = $true)] - [Hashtable[]]$data, - [string]$title, - [string]$titleX, - [string]$titleY, - [ValidateSet("Area", "Bar", "BoxPlot", "Bubble", "Candlestick", "Column", "Doughnut", "ErrorBar", "FastLine", - "FastPoint", "Funnel", "Kagi", "Line", "Pie", "Point", "PointAndFigure", "Polar", "Pyramid", - "Radar", "Range", "RangeBar", "RangeColumn", "Renko", "Spline", "SplineArea", "SplineRange", - "StackedArea", "StackedArea100", "StackedBar", "StackedBar100", "StackedColumn", - "StackedColumn100", "StepLine", "Stock", "ThreeLineBreak")] - $ChartType = "bar" - ) - - # If chartsize is specified in style, use it unless explicitly set - if ($ChartSize -and (-not $height -and -not $width)) { - if ($ChartSize -match "(\d+)x(\d+)") { - $height = $Matches[1] - $width = $Matches[2] - } - } - # if size not set in style or function call, default to 400x400 (maybe make this a globalVariable?) - if (-not $ChartSize -and (-not $height -and -not $width)) { - $height = 400 - $width = 400 - } - - return New-Object PSObject -Property @{ - "height" = $height; - "width" = $width; - "data" = $data; - "title" = $title; - "titleX" = $titleX; - "titleY" = $titleY; - "ChartType" = $ChartType - } -} - -<# Creates a chart Image #> -function Get-ChartResource { - param ( - $ChartDef - ) - [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization") - - # Create a new chart object - $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart - $Chart.Width = $ChartDef.width - $Chart.Height = $ChartDef.height - $Chart.AntiAliasing = "All" - - # Create a chartarea to draw on and add to chart - $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea - $Chart.ChartAreas.Add($ChartArea) - - # Set title and axis labels - if ($ChartDef.title) { - $titleRef = $Chart.Titles.Add($ChartDef.title) - } - if ($ChartDef.titleX) { - $ChartArea.AxisX.Title = $ChartDef.titleX - } - if ($ChartDef.titleY) { - $ChartArea.AxisY.Title = $ChartDef.titleY - } - - # change chart colours - if ($ChartBackground) { - $Chart.BackColor = Get-ChartColours $ChartBackground - $ChartArea.BackColor = Get-ChartColours $ChartBackground - } else { - $Chart.BackColor = [System.Drawing.Color]::Transparent - $ChartArea.BackColor = [System.Drawing.Color]::Transparent - } - # If we have style - if ($ChartColours) { - $Chart.PaletteCustomColors = Get-ChartColours $ChartColours - $Chart.Palette = [System.Windows.Forms.DataVisualization.Charting.ChartColorPalette]::None - } - - if ($ChartFontColour) { - $Chart.ForeColor = Get-ChartColours $ChartFontColour - } - - # Add data to chart and set chart type - for ($i = 0; $i -lt $ChartDef.data.count; $i++) { - [void]$Chart.Series.Add("Data$i") - $Chart.Series["Data$i"].Points.DataBindXY($ChartDef.data[$i].Keys, $ChartDef.data[$i].Values) - $Chart.Series["Data$i"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::($ChartDef.ChartType) - } - - # Do some funky work to increase the DPI so charts look nice. Default 96 DPI looks terrible :( - [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") - - $bmp = New-Object System.Drawing.Bitmap(($ChartDef.width), ($ChartDef.height)) - $bmp.SetResolution(384, 384); - if ($ChartArea.BackColor -eq [System.Drawing.Color]::Transparent) { - $bmp.MakeTransparent() - } - $chart.DrawToBitmap($bmp, (new-object System.Drawing.Rectangle(0, 0, $ChartDef.width, $ChartDef.height))) - $ms = new-Object IO.MemoryStream - $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); - $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null - $byte = New-Object byte[] $ms.Length - $ms.read($byte, 0, $ms.length) | Out-Null - - return ("png|{0}" -f [System.Convert]::ToBase64String($byte)) -} - -<# Takes Array of HTML colour codes and returns Color object #> -function Get-ChartColours { - param ( - [string[]]$ChartColours - ) - - foreach ($colour in $ChartColours) { - [System.Drawing.Color]::FromArgb([Convert]::ToInt32($colour.Substring(0, 2), 16), - [Convert]::ToInt32($colour.Substring(2, 2), 16), - [Convert]::ToInt32($colour.Substring(4, 2), 16)); - } -} - -<# Adds a resource to the resource array, to be included in report. - At the moment, only "File" types are supported- this will be expanded to include - SystemIcons and raw byte data (so images can be packaged completely in styles if desired - #> -function Add-ReportResource { - param ( - $cid, - $ResourceData, - [ValidateSet("File", "SystemIcons", "Base64")] - $Type = "File", - $Used = $false - ) - - # If cid does not exist, add it - if ($global:ReportResources.Keys -notcontains $cid) { - $global:ReportResources.Add($cid, @{ - "Data" = ("{0}|{1}" -f $Type, $ResourceData); - "Uses" = 0 - }) - } - - # Update uses count if $Used set (Should normally be incremented with Set-ReportResource) - # Useful for things like headers where they are always required. - if ($Used) { - ($global:ReportResources[$cid].Uses)++ - } -} - -Function Set-ReportResource { - param ( - $cid - ) - - # Increment use - ($global:ReportResources[$cid].Uses)++ -} - -<# Gets a resource in the specified ReturnType (eventually support both a -base64 encoded string, and Linked Resource for email #> -function Get-ReportResource { - param ( - $cid, - [ValidateSet("embed", "linkedresource")] - $ReturnType = "embed" - ) - - $data = $global:ReportResources[$cid].Data.Split("|") - - # Process each resource type differently - switch ($data[0]) { - "File" { - # Check the path exists - if (Test-Path $data[1] -ErrorAction SilentlyContinue) { - if ($ReturnType -eq "embed") { - # return a MIME/Base64 combo for embedding in HTML - $imgData = Get-Content ($data[1]) -Encoding Byte - $type = $data[1].substring($data[1].LastIndexOf(".") + 1) - return ("data:image/{0};base64,{1}" -f $type, [System.Convert]::ToBase64String($imgData)) - } - if ($ReturnType -eq "linkedresource") { - # return a linked resource to be added to mail message - $lr = New-Object system.net.mail.LinkedResource($data[1]) - $lr.ContentId = $cid - return $lr; - } - } else { - Write-Warning ($lang.resFileWarn -f $cid) - } - } - "SystemIcons" { - # Take the SystemIcon Name - see http://msdn.microsoft.com/en-us/library/system.drawing.systemicons(v=vs.110).aspx - # Load the image into a MemoryStream in PNG format (to preserve transparency) - [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") - $bmp = ([System.Drawing.SystemIcons]::($data[1])).toBitmap() - $bmp.MakeTransparent() - $ms = new-Object IO.MemoryStream - $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::PNG) - $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null - - if ($ReturnType -eq "embed") { - # return a MIME/Base64 combo for embedding in HTML - $byte = New-Object byte[] $ms.Length - $ms.read($byte, 0, $ms.length) | Out-Null - return ("data:image/png;base64," + [System.Convert]::ToBase64String($byte)) - } - if ($ReturnType -eq "linkedresource") { - # return a linked resource to be added to mail message - $lr = New-Object system.net.mail.LinkedResource($ms) - $lr.ContentId = $cid - return $lr; - } - } - "Base64" { - if ($ReturnType -eq "embed") { - return ("data:image/{0};base64,{1}" -f $data[1], $data[2]) - } - if ($ReturnType -eq "linkedresource") { - $w = [system.convert]::FromBase64String($data[2]) - $ms = new-Object IO.MemoryStream - $ms.Write($w, 0, $w.Length); - $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null - $lr = New-Object system.net.mail.LinkedResource($ms) - $lr.ContentId = $cid - return $lr; - } - } - } -} - - -function Get-ConfigScripts { - return "function createCSV() { - var inputs = document.getElementsByTagName('input'); - - var strsplit = null - //var output = 'filename,question,var\n' - var output = '\n' - for (var i = 0; i < inputs.length; i += 1) { - strsplit = inputs[i].name.split('|') - output += '\t\n' - output += '\t\t' - output += strsplit[0] - output += '\n' - output += '\t\t' - output += strsplit[1] - output += '\n' - output += '\t\t' - output += strsplit[2] - output += '\n' - output += '\t\t""' - output += inputs[i].value - output += '""\n' - output += '\t\n' - } - output += '' - downloadFile('vCheckSettings.xml', output) - } - function downloadFile(filename, rows) { - var fileContent = ''; - for (var i = 0; i < rows.length; i++) { - fileContent += rows[i]; - } - - var blob = new Blob([fileContent], { type: 'text/xml;charset=utf-8;' }); - if (navigator.msSaveBlob) { // IE 10+ - navigator.msSaveBlob(blob, filename); - } else { - var link = document.createElement('a'); - if (link.download !== undefined) { // feature detection - // Browsers that support HTML5 download attribute - var url = URL.createObjectURL(blob); - link.setAttribute('href', url); - link.setAttribute('download', filename); - link.style.visibility = 'hidden'; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - } - } - }" -} -#endregion functions - -#region initialization -################################################################################ -# Initialization # -################################################################################ -# if we have the job parameter set, get the paths from the config file. -if ($job) { - [xml]$jobConfig = Get-Content $job - - # Use GlobalVariables path if it is valid, otherwise use default - if (Test-Path $jobConfig.vCheck.globalVariables) { - $GlobalVariables = (Get-Item $jobConfig.vCheck.globalVariables).FullName - } else { - $GlobalVariables = $ScriptPath + "\GlobalVariables.ps1" - Write-Warning ($lang.gvInvalid -f $GlobalVariables) - } - - # Get Plugin paths - $PluginPaths = @() - if ($jobConfig.vCheck.plugins.path) { - foreach ($PluginPath in ($jobConfig.vCheck.plugins.path -split ";")) { - if (Test-Path $PluginPath) { - $PluginPaths += (Get-Item $PluginPath).Fullname - $PluginPaths += Get-Childitem $PluginPath -Recurse | ?{ $_.PSIsContainer } | Select-Object -ExpandProperty FullName - } else { - $PluginPaths += $ScriptPath + "\Plugins" - Write-Warning ($lang.pluginpathInvalid -f $PluginPath, ($ScriptPath + "\Plugins")) - } - } - $PluginPaths = $PluginPaths | Sort-Object -unique - - # Get all plugins and test they are correct - $vCheckPlugins = @() - foreach ($plugin in $jobConfig.vCheck.plugins.plugin) { - $testedPaths = 0 - foreach ($PluginPath in $PluginPaths) { - $testedPaths++ - if (Test-Path ("{0}\{1}" -f $PluginPath, $plugin)) { - $vCheckPlugins += Get-Item ("{0}\{1}" -f $PluginPath, $plugin) - break; - } - # Plugin not found in any search path - elseif ($testedPaths -eq $PluginPaths.Count) { - Write-Warning ($lang.pluginInvalid -f $plugin) - } - } - } - } - # if no valid plugins specified, fall back to default - if (!$vCheckPlugins) { - $vCheckPlugins = Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Sort-Object FullName - } -} else { - $ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) } - $vCheckPlugins = @(Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Where-Object { $_.Directory -match "initialize" } | Sort-Object $ToNatural) - $PluginsSubFolder = Get-ChildItem -Path $PluginsFolder | Where-Object { ($_.PSIsContainer) -and ($_.Name -notmatch "initialize") -and ($_.Name -notmatch "finish") } - $vCheckPlugins += $PluginsSubFolder | % { Get-ChildItem -Path $_.FullName -filter "*.ps1" | Sort-Object $ToNatural } - $vCheckPlugins += Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Where-Object { $_.Directory -match "finish" } | Sort-Object $ToNatural - $GlobalVariables = $ScriptPath + "\GlobalVariables.ps1" -} - -## Determine if the setup wizard needs to run -$file = Get-Content $GlobalVariables -$Setup = ($file | Select-String -Pattern '# Set the following to true to enable the setup wizard for first time run').LineNumber -$SetupLine = $Setup++ -$SetupSetting = Invoke-Expression (($file[$SetupLine]).Split("="))[1] - - -## Include GlobalVariables and validate settings (at the moment just check they exist) -. $GlobalVariables - -$vcvars = @("SetupWizard", "reportHeader", "SMTPSRV", "EmailFrom", "EmailTo", "EmailSubject", "DisplaytoScreen", "SendEmail", "SendAttachment", "TimeToRun", "PluginSeconds", "Style", "Date") -foreach ($vcvar in $vcvars) { - if (!($(Get-Variable -Name "$vcvar" -Erroraction 'SilentlyContinue'))) { - Write-Error ($lang.varUndefined -f $vcvar) - } -} - -# Create empty array of resources (i.e. Images) -$global:ReportResources = @{ } - -## Set the StylePath and include it -$StylePath = $ScriptPath + "\Styles\" + $Style -if (!(Test-Path ($StylePath))) { - # The path is not valid - # Use the default style - Write-Warning "Style path ($($StylePath)) is not valid" - $StylePath = $ScriptPath + "\Styles\VMware" - Write-Warning "Using $($StylePath)" -} - -# Import the Style -. ("$($StylePath)\Style.ps1") - - -if ($SetupSetting -or $config -or $GUIConfig) { - #Clear-Host - - ($lang.GetEnumerator() | Where-Object { $_.Name -match "setupMsg[0-9]*" } | Sort-Object Name) | ForEach-Object { - Write-Warning -Message "$($_.value)" - } - - if ($GUIConfig) { - $PluginResult = @() - - # Set the output filename - if (-not (Test-Path -PathType Container $Outputpath)) { New-Item $Outputpath -type directory | Out-Null } - $Filename = ("{0}\{1}_vCheck-Config_{2}.html" -f $Outputpath, $Server, (Get-Date -Format "yyyyMMdd_HHmm")) - - #$configHTML = "" - #$configHTML += Invoke-HTMLSettings -Filename $GlobalVariables - $PluginResult += Invoke-HTMLSettings -Filename $GlobalVariables - Foreach ($plugin in $vCheckPlugins) { - #$configHTML += Invoke-HTMLSettings -Filename $plugin.Fullname - $PluginResult += Invoke-HTMLSettings -Filename $plugin.Fullname - } - - # Run Style replacement - $MyConfig = Get-ReportHTML - # Always generate the report with embedded images - $embedConfig = $MyConfig - # Loop over all CIDs and replace them - Foreach ($cid in $global:ReportResources.Keys) { - $embedConfig = $embedConfig -replace ("cid:{0}" -f $cid), (Get-ReportResource $cid -ReturnType "embed") - } - - $embedConfig | Out-File $Filename - Invoke-Item $Filename - ($lang.GetEnumerator() | Where-Object { $_.Name -match "configMsg[0-9]*" } | Sort-Object Name) | ForEach-Object { - Write-Warning -Message "$($_.value)" - } - - } elseif ($config) { - Invoke-Settings -Filename $GlobalVariables -GB $true - Foreach ($plugin in $vCheckPlugins) { - Invoke-Settings -Filename $plugin.Fullname - } - } -} - -#endregion initialization -if (-not $GUIConfig) { - - #region scriptlogic - ################################################################################ - # Script logic # - ################################################################################ - # Start generating the report - $PluginResult = @() - - Write-Warning -Message $lang.pluginBegin - - # Loop over all enabled plugins - $p = 0 - $vCheckPlugins | Foreach { - $TableFormat = $null - $PluginInfo = Get-PluginID $_.Fullname - $p++ - Write-CustomOut ($lang.pluginStart -f $PluginInfo["Title"], $PluginInfo["Author"], $PluginInfo["Version"], $p, $vCheckPlugins.count) - $pluginStatus = ($lang.pluginStatus -f $p, $vCheckPlugins.count, $_.Name) - Write-Progress -ID 1 -Activity $lang.pluginActivity -Status $pluginStatus -PercentComplete (100 * $p/($vCheckPlugins.count)) - $TTR = [math]::round((Measure-Command { $Details = @(. $_.FullName)}).TotalSeconds, 2) - - Write-CustomOut ($lang.pluginEnd -f $PluginInfo["Title"], $PluginInfo["Author"], $PluginInfo["Version"], $p, $vCheckPlugins.count) - # Do a replacement for [count] for number of items returned in $header - $Header = $Header -replace "\[count\]", $Details.count - - $PluginResult += New-Object PSObject -Property @{ - "Title" = $Title; - "Author" = $PluginInfo["Author"]; - "Version" = $PluginInfo["Version"]; - "Details" = $Details; - "Display" = $Display; - "TableFormat" = $TableFormat; - "Header" = $Header; - "Comments" = $Comments; - "TimeToRun" = $TTR; - } - } - Write-Progress -ID 1 -Activity $lang.pluginActivity -Status $lang.Complete -Completed - - # Add report on plugins - if ($reportOnPlugins) { - $Comments = "Plugins in numerical order" - $Plugins = @() - foreach ($Plugin in (Get-ChildItem $PluginsFolder -Include *.ps1, *.ps1.disabled -Recurse)) { - $Plugins += New-Object PSObject -Property @{ - "Name" = (Get-PluginID $Plugin.FullName).Title; - "Enabled" = (($vCheckPlugins | Select-Object -ExpandProperty FullName) -Contains $plugin.FullName) - } - } - - if ($ListEnabledPluginsFirst) { - $Plugins = $Plugins | Sort-Object -property @{ Expression = "Enabled"; Descending = $true } - $Comments = "Plugins in numerical order, enabled plugins listed first" - } - - $PluginResult += New-Object PSObject -Property @{ - "Title" = $lang.repPRTitle; - "Author" = "vCheck"; - "Version" = $vCheckVersion; - "Details" = $Plugins; - "Display" = "Table"; - "TableFormat" = $null; - "Header" = $lang.repPRTitle; - "Comments" = $Comments; - "TimeToRun" = 0; - } - } - - # Add Time to Run detail for plugins - if specified in GlobalVariables.ps1 - if ($TimeToRun) { - $Finished = Get-Date - $PluginResult += New-Object PSObject -Property @{ - "Title" = $lang.repTTRTitle; - "Author" = "vCheck"; - "Version" = $vCheckVersion; - "Details" = ($PluginResult | Where-Object { $_.TimeToRun -gt $PluginSeconds } | Select-Object Title, TimeToRun | Sort-Object TimeToRun -Descending); - "Display" = "List"; - "TableFormat" = $null; - "Header" = ($lang.repTime -f [math]::round(($Finished - $Date).TotalMinutes, 2), ($Finished.ToLongDateString()), ($Finished.ToLongTimeString())); - "Comments" = ($lang.slowPlugins -f $PluginSeconds); - "TimeToRun" = 0; - } - } - - #endregion scriptlogic - - #region output - ################################################################################ - # Output # - ################################################################################ - # Loop over plugin results and generate HTML from style - $emptyReport = $true - $p = 1 - Foreach ($pr in $PluginResult) { - If ($pr.Details) { - $emptyReport = $false - switch ($pr.Display) { - "List" { $pr.Details = Get-HTMLList $pr.Details } - "Table" { $pr.Details = Get-HTMLTable $pr.Details $pr.TableFormat } - "Chart" { $pr.Details = Get-HTMLChart "plugin$($p)" $pr.Details } - default { $pr.Details = $null } - } - $pr | Add-Member -Type NoteProperty -Name pluginID -Value "plugin-$p" - $p++ - } - if ($pr.Details -ne $null) - { - $emptyReport = $false - } - } - - # Run Style replacement - $MyReport = Get-ReportHTML - - # Set the output filename - if (-not (Test-Path -PathType Container $Outputpath)) { New-Item $Outputpath -type directory | Out-Null } - $Filename = ("{0}\{1}_vCheck_{2}.htm" -f $Outputpath, $VIServer, (Get-Date -Format "yyyyMMdd_HHmm")) - - # Always generate the report with embedded images - $embedReport = $MyReport - # Loop over all CIDs and replace them - Foreach ($cid in $global:ReportResources.Keys) { - $embedReport = $embedReport -replace ("cid:{0}" -f $cid), (Get-ReportResource $cid -ReturnType "embed") - } - $embedReport | Out-File -encoding ASCII -filepath $Filename - - # Display to screen - if ($DisplayToScreen -and (!($emptyReport -and !$DisplayReportEvenIfEmpty))) { - Write-CustomOut $lang.HTMLdisp - Invoke-Item $Filename - } - - # Generate email - if ($SendEmail -and (!($emptyReport -and !$EmailReportEvenIfEmpty))) { - Write-CustomOut $lang.emailSend - $msg = New-Object System.Net.Mail.MailMessage ($EmailFrom, $EmailTo) - # If CC address specified, add - If ($EmailCc -ne "") { - $msg.CC.Add($EmailCc) - } - $msg.subject = $EmailSubject - - # if send attachment, just send plaintext email with HTML report attached - If ($SendAttachment) { - $msg.Body = $lang.emailAtch - $attachment = new-object System.Net.Mail.Attachment $Filename - $msg.Attachments.Add($attachment) - } - # Otherwise send the HTML email - else { - $msg.IsBodyHtml = $true; - $html = [System.Net.Mail.AlternateView]::CreateAlternateViewFromString($MyReport, $null, 'text/html') - $msg.AlternateViews.Add($html) - - # Loop over all CIDs and replace them - Foreach ($cid in $global:ReportResources.Keys) { - if ($global:ReportResources[$cid].Uses -gt 0) { - $lr = (Get-ReportResource $cid -ReturnType "linkedresource") - $html.LinkedResources.Add($lr); - } - } - } - # Send the email - $smtpClient = New-Object System.Net.Mail.SmtpClient - - # Find the VI Server and port from the global settings file - $smtpClient.Host = ($SMTPSRV -Split ":")[0] - if (($SMTPSRV -split ":")[1]) { - $smtpClient.Port = ($SMTPSRV -split ":")[1] - } - - if ($EmailSSL -eq $true) { - $smtpClient.EnableSsl = $true - } - $smtpClient.UseDefaultCredentials = $true; - $smtpClient.Send($msg) - If ($SendAttachment) { $attachment.Dispose() } - $msg.Dispose() - } - - # Run EndScript once everything else is complete - if (Test-Path ($ScriptPath + "\EndScript.ps1")) { - . ($ScriptPath + "\EndScript.ps1") - } - - #endregion output -} +<# +.SYNOPSIS + vCheck is a PowerShell HTML framework script, 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. +.DESCRIPTION + vCheck Daily Report for vSphere + + 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. + + This script picks on the key known issues and potential issues scripted as + plugins for various technologies written as powershell scripts and reports + it all in one place so all you do in the morning is check your email. + + One of they key things about this report is if there is no issue in a particular + place you will not receive that section in the email, for example if there are + no datastores with less than 5% free space (configurable) then the disk space + section in the virtual infrastructure version of this script, it will not show + in the email, this ensures that you have only the information you need in front + of you when you get into the office. + + This script is not to be confused with an Audit script, although the reporting + framework can also be used for auditing scripts too. I dont want to remind you + that you have 5 hosts and what there names are and how many CPUs they have each + and every day as you dont want to read that kind of information unless you need + it, this script will only tell you about problem areas with your infrastructure. + +.NOTES + File Name : vCheck.ps1 + Author : Alan Renouf - @alanrenouf + Version : 6.24 + + Thanks to all who have commented on my blog to help improve this project + all beta testers and previous contributors to this script. + +.LINK + http://www.virtu-al.net/vcheck-pluginsheaders/vcheck +.LINK + https://github.com/alanrenouf/vCheck-vSphere/ + +.INPUTS + No inputs required +.OUTPUTS + HTML formatted email, Email with attachment, HTML File + +.PARAMETER config + If this switch is set, run the setup wizard + +.PARAMETER Outputpath + This parameter specifies the output location for files. + +.PARAMETER job + This parameter lets you specify an xml config file for this invokation +#> +#Requires -Version 3.0 +[CmdletBinding()] +param ( + [Switch]$config, + + [Switch]$GUIConfig, + + [ValidateScript({ Test-Path $_ -PathType 'Container' })] + [string]$Outputpath=$Env:TEMP, + + [ValidateScript({ Test-Path $_ -PathType 'Leaf' })] + [string]$job +) + +$vCheckVersion = "6.24" +$Date = Get-Date + +# Setup all paths required for script to run +$ScriptPath = (Split-Path ((Get-Variable MyInvocation).Value).MyCommand.Path) +$PluginsFolder = $ScriptPath + "\Plugins\" + +#region Internationalization +################################################################################ +# Internationalization # +################################################################################ +# Default language en-US +Import-LocalizedData -BaseDirectory ($ScriptPath + '\lang') -BindingVariable lang -UICulture en-US -ErrorAction SilentlyContinue + +# Override the default (en-US) if it exists in lang directory +Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable lang -ErrorAction SilentlyContinue + +#endregion Internationalization + +#region functions +################################################################################ +# Functions # +################################################################################ +<# Write timestamped output to screen #> +function Write-CustomOut ($Details) { + $LogDate = Get-Date -Format "HH:mm:ss" + Write-OutPut "[$($LogDate)] $Details" +} + +<# Placeholder for now, just return the setting passed to it. Eventually this + will be used for new settings handling #> +function Get-vCheckSetting +{ + param + ( + [string]$Module, + [string]$Setting, + $default + ) + + return $default +} + +<# Search $file_content for name/value pair with ID_Name and return value #> +Function Get-ID-String ($file_content, $ID_name) { + if ($file_content | Select-String -Pattern "\$+$ID_name\s*=") { + $value = (($file_content | Select-String -pattern "\$+${ID_name}\s*=").toString().split("=")[1]).Trim(' "') + return ($value) + } +} + +<# Get basic information abount a plugin #> +Function Get-PluginID ($Filename) { + # Get the identifying information for a plugin script + $file = Get-Content $Filename + $Title = Get-ID-String $file "Title" + if (!$Title) { $Title = $Filename } + $PluginVersion = Get-ID-String $file "PluginVersion" + $Author = Get-ID-String $file "Author" + $Ver = "{0:N1}" -f $PluginVersion + + return @{ "Title" = $Title; "Version" = $Ver; "Author" = $Author } +} + + +Function Invoke-Settings { + + <# + .DESCRIPTION + Run through settings for specified file, expects question on one line, and variable/value on following line + .NOTES + Updated: 20150428 + Updated By: Kevin Kirkpatrick (@vScripter - Twitter/GitHub) + Update Notes: + - Remove Write-Host in favor of Write-Warning; this was based on setting the color of Write-Host to 'warning' colors + - converted function to advanced function + - moved parameters out of function declaration and into the param declaration + - moved all code into the PROCESS block + - improved code spacing for improved readability + - added comment based help section for notes/comments + #> + + [CmdletBinding(PositionalBinding = $true)] + param ( + [parameter(Position = 0)] + $Filename, + [parameter(Position = 1)] + $GB + ) + + PROCESS { + + $file = Get-Content $filename + $OriginalLine = ($file | Select-String -Pattern "# Start of Settings").LineNumber + $EndLine = ($file | Select-String -Pattern "# End of Settings").LineNumber + + if (!(($OriginalLine + 1) -eq $EndLine)) { + + $Array = @() + $Line = $OriginalLine + $PluginName = (Get-PluginID $Filename).Title + + If ($PluginName.EndsWith(".ps1", 1)) { + + $PluginName = ($PluginName.split("\")[-1]).split(".")[0] + + } # end if + + Write-Warning -Message "`n$PluginName" + + do { + + $Question = $file[$Line] + $Line++ + $Split = ($file[$Line]).Split("=") + $Var = $Split[0] + $CurSet = $Split[1].Trim() + + # Check if the current setting is in speech marks + $String = $false + if ($CurSet -match '"') { + $String = $true + $CurSet = $CurSet.Replace('"', '').Trim() + } # end if + + $NewSet = Read-Host "$Question [$CurSet]" + + If (-not $NewSet) { + $NewSet = $CurSet + } # end if + + If ($String) { + $Array += $Question + $Array += "$Var= `"$NewSet`"" + } Else { + $Array += $Question + $Array += "$Var= $NewSet" + } # end if/else + + $Line++ + + } Until ($Line -ge ($EndLine - 1)) + + $Array += "# End of Settings" + + $out = @() + $out = $File[0..($OriginalLine - 1)] + $out += $array + $out += $File[$Endline..($file.count - 1)] + + if ($GB) { + $out[$SetupLine] = '$SetupWizard = $False' + } # end if + + $out | Out-File $Filename + + } # end if + + } # end PROCESS block + +} # end Function Invoke-Settings + +Function Invoke-HTMLSettings { + + <# + .DESCRIPTION + Run through settings for specified file, expects question on one line, and variable/value on following line. + Outputs settings to HTML file, which accepts input, and can create a configuration file. + .NOTES + Updated: 20160830 + Updated By: David Seibel + Update Notes: + - Initial creation + #> + + [CmdletBinding(PositionalBinding = $true)] + param ( + [parameter(Position = 0)] + $Filename, + [parameter(Position = 1)] + $GB + ) + + PROCESS { + + $file = Get-Content $filename + $OriginalLine = ($file | Select-String -Pattern "# Start of Settings").LineNumber + $EndLine = ($file | Select-String -Pattern "# End of Settings").LineNumber + + if (!(($OriginalLine + 1) -eq $EndLine)) { + + $Line = $OriginalLine + $PluginInfo = Get-PluginID $Filename + $PluginName = $PluginInfo.Title + + $htmlOutput = "" + If ($PluginName.EndsWith(".ps1", 1)) { + $PluginName = ($PluginName.split("\")[-1]).split(".")[0] + } # end if + + $htmlOutput += "
" + + do { + $Question = $file[$Line] + $QuestionWithoutHash = $Question.Replace("# ", "") + $Line++ + $Split = ($file[$Line]).Split("=") + $Var = $Split[0].Trim() + if ($Split.count -gt 1) { + $CurSet = $Split[1].Trim() + # Check if the current setting is in speech marks + $String = $false + if ($CurSet -match '"') { + $String = $true + $CurSet = $CurSet.Replace('"', '').Trim() + } # end if + + $htmlOutput += "`n" + } + } Until ($Line -ge ($EndLine - 1)) + + $htmlOutput += "
$QuestionWithoutHash
" + $PluginConfig += New-Object PSObject -Property @{ + "Details" = $htmlOutput; + "Header" = $PluginName; + "PluginID" = $PluginName; + } + + return $PluginConfig + } # end if + + } # end PROCESS block + +} # end Function Invoke-HTMLSettings + +<# Replace HTML Entities in string. Used to stop
tags from being mangled in tables #> +function Format-HTMLEntities { + param ([string]$content) + + $replace = @{ + "<" = "<"; + ">" = ">"; + } + + foreach ($r in $replace.Keys.GetEnumerator()) { + $content = $content -replace $r, $replace[$r] + } + return $content +} + +<# Takes an array of content, and optional formatRules and generated HTML table #> +Function Get-HTMLTable { + param ($Content, $FormatRules) + + # Use an XML object for ease of use + $XMLTable = [xml]($content | ConvertTo-Html -Fragment) + $XMLTable.table.SetAttribute("width", "100%") + + # If only one column, fix up the table header + if (($content | Get-Member -MemberType Properties).count -eq 1) + { + $XMLTable.table.tr[0].th = (($content | Get-Member -MemberType Properties) | Select-Object -ExpandProperty Name -First 1).ToString() + } + + # If format rules are specified + if ($FormatRules) { + # Check each cell to see if there are any format rules + for ($RowN = 1; $RowN -lt $XMLTable.table.tr.count; $RowN++) { + for ($ColN = 0; $ColN -lt $XMLTable.table.tr[$RowN].td.count; $ColN++) { + if ($FormatRules.keys -contains $XMLTable.table.tr[0].th[$ColN]) { + # Current cell has a rule, test to see if they are valid + foreach ($rule in $FormatRules[$XMLTable.table.tr[0].th[$ColN]]) { + if ($XMLTable.table.tr[$RowN].td[$ColN]."#text") + { + $value = $XMLTable.table.tr[$RowN].td[$ColN]."#text" + } + else + { + $value = $XMLTable.table.tr[$RowN].td[$ColN] + } + if ($value -notmatch "^[0-9.]+$") { + $value = """$value""" + } + if (Invoke-Expression ("{0} {1}" -f $value, [string]$rule.Keys)) { + # Find what to + $RuleScope = ([string]$rule.Values).split(",")[0] + $RuleActions = ([string]$rule.Values).split(",")[1].split("|") + + switch ($RuleScope) { + "Row" { + for ($TRColN = 0; $TRColN -lt $XMLTable.table.tr[$RowN].td.count; $TRColN++) { + $XMLTable.table.tr[$RowN].selectSingleNode("td[$($TRColN + 1)]").SetAttribute($RuleActions[0], $RuleActions[1]) + } + } + "Cell" { + if ($RuleActions[0] -eq "cid") { + # Do Image - create new XML node for img and clear #text + $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]")."#text" = "" + $elem = $XMLTable.CreateElement("img") + $elem.SetAttribute("src", ("cid:{0}" -f $RuleActions[1])) + # Add img size if specified + if ($RuleActions[2] -match "(\d+)x(\d+)") { + $elem.SetAttribute("width", $Matches[1]) + $elem.SetAttribute("height", $Matches[2]) + } + + $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]").AppendChild($elem) | Out-Null + # Increment usage counter (so we don't have .bin attachments) + Set-ReportResource $RuleActions[1] + } else { + $XMLTable.table.tr[$RowN].selectSingleNode("td[$($ColN + 1)]").SetAttribute($RuleActions[0], $RuleActions[1]) + } + } + } + } + } + } + } + } + } + return (Format-HTMLEntities ([string]($XMLTable.OuterXml))) +} + +<# Takes an array of content, and returns HTML table with header column #> +Function Get-HTMLList { + param ([array]$content) + + if ($content.count -gt 0) { + # Create XML doc from HTML. Remove colgroup and header row + if ($content.count -gt 1) { + [xml]$XMLTable = $content | ConvertTo-HTML -Fragment + $XMLTable.table.RemoveChild($XMLTable.table.colgroup) | out-null + $XMLTable.table.RemoveChild($XMLTable.table.tr[0]) | out-null + $XMLTable.table.SetAttribute("width", "100%") + } else { + [xml]$XMLTable = $content | ConvertTo-HTML -Fragment -As List + } + + # Replace the first column td with th + for ($i = 0; $i -lt $XMLTable.table.tr.count; $i++) { + $node = $XMLTable.table.tr[$i].SelectSingleNode("/table/tr[$($i + 1)]/td[1]") + $elem = $XMLTable.CreateElement("th") + $elem.InnerText = $node."#text" + $trNode = $XMLTable.SelectSingleNode("/table/tr[$($i + 1)]") + $trNode.ReplaceChild($elem, $node) | Out-Null + } + + # If only one column, fix up the table header + if (($content | Get-Member -MemberType Properties).count -eq 1) + { + $XMLTable.table.tr[0].th = (($content | Get-Member -MemberType Properties) | Select-Object -ExpandProperty Name -First 1).ToString() + } + + return (Format-HTMLEntities ([string]($XMLTable.OuterXml))) + } +} + +<# Returns HTML fragment for chart. Calls Get-ChartResource to generate chart image #> +function Get-HTMLChart { + param ( + [string]$cidbase, + [Object[]]$ChartObjs + ) + $html = "" + $i = 0 + foreach ($ChartObj in $ChartObjs) { + $i++ + $base64 = Get-ChartResource $ChartObj + $cid = $cidbase + "-" + $i + Add-ReportResource -cid $cid -ResourceData $Base64 -Type "Base64" -Used $true + $html += "" + } + return $html +} + +<# Create a new Chert object, this will get fed back down the output stream as part + of plugin processing. This allows us to keep the same interface for plugins content #> +function New-Chart { + param ( + [int]$height, + [int]$width, + [Parameter(Mandatory = $true)] + [Hashtable[]]$data, + [string]$title, + [string]$titleX, + [string]$titleY, + [ValidateSet("Area", "Bar", "BoxPlot", "Bubble", "Candlestick", "Column", "Doughnut", "ErrorBar", "FastLine", + "FastPoint", "Funnel", "Kagi", "Line", "Pie", "Point", "PointAndFigure", "Polar", "Pyramid", + "Radar", "Range", "RangeBar", "RangeColumn", "Renko", "Spline", "SplineArea", "SplineRange", + "StackedArea", "StackedArea100", "StackedBar", "StackedBar100", "StackedColumn", + "StackedColumn100", "StepLine", "Stock", "ThreeLineBreak")] + $ChartType = "bar" + ) + + # If chartsize is specified in style, use it unless explicitly set + if ($ChartSize -and (-not $height -and -not $width)) { + if ($ChartSize -match "(\d+)x(\d+)") { + $height = $Matches[1] + $width = $Matches[2] + } + } + # if size not set in style or function call, default to 400x400 (maybe make this a globalVariable?) + if (-not $ChartSize -and (-not $height -and -not $width)) { + $height = 400 + $width = 400 + } + + return New-Object PSObject -Property @{ + "height" = $height; + "width" = $width; + "data" = $data; + "title" = $title; + "titleX" = $titleX; + "titleY" = $titleY; + "ChartType" = $ChartType + } +} + +<# Creates a chart Image #> +function Get-ChartResource { + param ( + $ChartDef + ) + [void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization") + + # Create a new chart object + $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart + $Chart.Width = $ChartDef.width + $Chart.Height = $ChartDef.height + $Chart.AntiAliasing = "All" + + # Create a chartarea to draw on and add to chart + $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea + $Chart.ChartAreas.Add($ChartArea) + + # Set title and axis labels + if ($ChartDef.title) { + $titleRef = $Chart.Titles.Add($ChartDef.title) + } + if ($ChartDef.titleX) { + $ChartArea.AxisX.Title = $ChartDef.titleX + } + if ($ChartDef.titleY) { + $ChartArea.AxisY.Title = $ChartDef.titleY + } + + # change chart colours + if ($ChartBackground) { + $Chart.BackColor = Get-ChartColours $ChartBackground + $ChartArea.BackColor = Get-ChartColours $ChartBackground + } else { + $Chart.BackColor = [System.Drawing.Color]::Transparent + $ChartArea.BackColor = [System.Drawing.Color]::Transparent + } + # If we have style + if ($ChartColours) { + $Chart.PaletteCustomColors = Get-ChartColours $ChartColours + $Chart.Palette = [System.Windows.Forms.DataVisualization.Charting.ChartColorPalette]::None + } + + if ($ChartFontColour) { + $Chart.ForeColor = Get-ChartColours $ChartFontColour + } + + # Add data to chart and set chart type + for ($i = 0; $i -lt $ChartDef.data.count; $i++) { + [void]$Chart.Series.Add("Data$i") + $Chart.Series["Data$i"].Points.DataBindXY($ChartDef.data[$i].Keys, $ChartDef.data[$i].Values) + $Chart.Series["Data$i"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::($ChartDef.ChartType) + } + + # Do some funky work to increase the DPI so charts look nice. Default 96 DPI looks terrible :( + [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") + + $bmp = New-Object System.Drawing.Bitmap(($ChartDef.width), ($ChartDef.height)) + $bmp.SetResolution(384, 384); + if ($ChartArea.BackColor -eq [System.Drawing.Color]::Transparent) { + $bmp.MakeTransparent() + } + $chart.DrawToBitmap($bmp, (new-object System.Drawing.Rectangle(0, 0, $ChartDef.width, $ChartDef.height))) + $ms = new-Object IO.MemoryStream + $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); + $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null + $byte = New-Object byte[] $ms.Length + $ms.read($byte, 0, $ms.length) | Out-Null + + return ("png|{0}" -f [System.Convert]::ToBase64String($byte)) +} + +<# Takes Array of HTML colour codes and returns Color object #> +function Get-ChartColours { + param ( + [string[]]$ChartColours + ) + + foreach ($colour in $ChartColours) { + [System.Drawing.Color]::FromArgb([Convert]::ToInt32($colour.Substring(0, 2), 16), + [Convert]::ToInt32($colour.Substring(2, 2), 16), + [Convert]::ToInt32($colour.Substring(4, 2), 16)); + } +} + +<# Adds a resource to the resource array, to be included in report. + At the moment, only "File" types are supported- this will be expanded to include + SystemIcons and raw byte data (so images can be packaged completely in styles if desired + #> +function Add-ReportResource { + param ( + $cid, + $ResourceData, + [ValidateSet("File", "SystemIcons", "Base64")] + $Type = "File", + $Used = $false + ) + + # If cid does not exist, add it + if ($global:ReportResources.Keys -notcontains $cid) { + $global:ReportResources.Add($cid, @{ + "Data" = ("{0}|{1}" -f $Type, $ResourceData); + "Uses" = 0 + }) + } + + # Update uses count if $Used set (Should normally be incremented with Set-ReportResource) + # Useful for things like headers where they are always required. + if ($Used) { + ($global:ReportResources[$cid].Uses)++ + } +} + +Function Set-ReportResource { + param ( + $cid + ) + + # Increment use + ($global:ReportResources[$cid].Uses)++ +} + +<# Gets a resource in the specified ReturnType (eventually support both a +base64 encoded string, and Linked Resource for email #> +function Get-ReportResource { + param ( + $cid, + [ValidateSet("embed", "linkedresource")] + $ReturnType = "embed" + ) + + $data = $global:ReportResources[$cid].Data.Split("|") + + # Process each resource type differently + switch ($data[0]) { + "File" { + # Check the path exists + if (Test-Path $data[1] -ErrorAction SilentlyContinue) { + if ($ReturnType -eq "embed") { + # return a MIME/Base64 combo for embedding in HTML + $imgData = Get-Content ($data[1]) -Encoding Byte + $type = $data[1].substring($data[1].LastIndexOf(".") + 1) + return ("data:image/{0};base64,{1}" -f $type, [System.Convert]::ToBase64String($imgData)) + } + if ($ReturnType -eq "linkedresource") { + # return a linked resource to be added to mail message + $lr = New-Object system.net.mail.LinkedResource($data[1]) + $lr.ContentId = $cid + return $lr; + } + } else { + Write-Warning ($lang.resFileWarn -f $cid) + } + } + "SystemIcons" { + # Take the SystemIcon Name - see http://msdn.microsoft.com/en-us/library/system.drawing.systemicons(v=vs.110).aspx + # Load the image into a MemoryStream in PNG format (to preserve transparency) + [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") + $bmp = ([System.Drawing.SystemIcons]::($data[1])).toBitmap() + $bmp.MakeTransparent() + $ms = new-Object IO.MemoryStream + $bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::PNG) + $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null + + if ($ReturnType -eq "embed") { + # return a MIME/Base64 combo for embedding in HTML + $byte = New-Object byte[] $ms.Length + $ms.read($byte, 0, $ms.length) | Out-Null + return ("data:image/png;base64," + [System.Convert]::ToBase64String($byte)) + } + if ($ReturnType -eq "linkedresource") { + # return a linked resource to be added to mail message + $lr = New-Object system.net.mail.LinkedResource($ms) + $lr.ContentId = $cid + return $lr; + } + } + "Base64" { + if ($ReturnType -eq "embed") { + return ("data:image/{0};base64,{1}" -f $data[1], $data[2]) + } + if ($ReturnType -eq "linkedresource") { + $w = [system.convert]::FromBase64String($data[2]) + $ms = new-Object IO.MemoryStream + $ms.Write($w, 0, $w.Length); + $ms.Seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null + $lr = New-Object system.net.mail.LinkedResource($ms) + $lr.ContentId = $cid + return $lr; + } + } + } +} + + +function Get-ConfigScripts { + return "function createCSV() { + var inputs = document.getElementsByTagName('input'); + + var strsplit = null + //var output = 'filename,question,var\n' + var output = '\n' + for (var i = 0; i < inputs.length; i += 1) { + strsplit = inputs[i].name.split('|') + output += '\t\n' + output += '\t\t' + output += strsplit[0] + output += '\n' + output += '\t\t' + output += strsplit[1] + output += '\n' + output += '\t\t' + output += strsplit[2] + output += '\n' + output += '\t\t""' + output += inputs[i].value + output += '""\n' + output += '\t\n' + } + output += '' + downloadFile('vCheckSettings.xml', output) + } + function downloadFile(filename, rows) { + var fileContent = ''; + for (var i = 0; i < rows.length; i++) { + fileContent += rows[i]; + } + + var blob = new Blob([fileContent], { type: 'text/xml;charset=utf-8;' }); + if (navigator.msSaveBlob) { // IE 10+ + navigator.msSaveBlob(blob, filename); + } else { + var link = document.createElement('a'); + if (link.download !== undefined) { // feature detection + // Browsers that support HTML5 download attribute + var url = URL.createObjectURL(blob); + link.setAttribute('href', url); + link.setAttribute('download', filename); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + } + }" +} +#endregion functions + +#region initialization +################################################################################ +# Initialization # +################################################################################ +# if we have the job parameter set, get the paths from the config file. +if ($job) { + [xml]$jobConfig = Get-Content $job + + # Use GlobalVariables path if it is valid, otherwise use default + if (Test-Path $jobConfig.vCheck.globalVariables) { + $GlobalVariables = (Get-Item $jobConfig.vCheck.globalVariables).FullName + } else { + $GlobalVariables = $ScriptPath + "\GlobalVariables.ps1" + Write-Warning ($lang.gvInvalid -f $GlobalVariables) + } + + # Get Plugin paths + $PluginPaths = @() + if ($jobConfig.vCheck.plugins.path) { + foreach ($PluginPath in ($jobConfig.vCheck.plugins.path -split ";")) { + if (Test-Path $PluginPath) { + $PluginPaths += (Get-Item $PluginPath).Fullname + $PluginPaths += Get-Childitem $PluginPath -Recurse | ?{ $_.PSIsContainer } | Select-Object -ExpandProperty FullName + } else { + $PluginPaths += $ScriptPath + "\Plugins" + Write-Warning ($lang.pluginpathInvalid -f $PluginPath, ($ScriptPath + "\Plugins")) + } + } + $PluginPaths = $PluginPaths | Sort-Object -unique + + # Get all plugins and test they are correct + $vCheckPlugins = @() + foreach ($plugin in $jobConfig.vCheck.plugins.plugin) { + $testedPaths = 0 + foreach ($PluginPath in $PluginPaths) { + $testedPaths++ + if (Test-Path ("{0}\{1}" -f $PluginPath, $plugin)) { + $vCheckPlugins += Get-Item ("{0}\{1}" -f $PluginPath, $plugin) + break; + } + # Plugin not found in any search path + elseif ($testedPaths -eq $PluginPaths.Count) { + Write-Warning ($lang.pluginInvalid -f $plugin) + } + } + } + } + # if no valid plugins specified, fall back to default + if (!$vCheckPlugins) { + $vCheckPlugins = Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Sort-Object FullName + } +} else { + $ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) } + $vCheckPlugins = @(Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Where-Object { $_.Directory -match "initialize" } | Sort-Object $ToNatural) + $PluginsSubFolder = Get-ChildItem -Path $PluginsFolder | Where-Object { ($_.PSIsContainer) -and ($_.Name -notmatch "initialize") -and ($_.Name -notmatch "finish") } + $vCheckPlugins += $PluginsSubFolder | % { Get-ChildItem -Path $_.FullName -filter "*.ps1" | Sort-Object $ToNatural } + $vCheckPlugins += Get-ChildItem -Path $PluginsFolder -filter "*.ps1" -Recurse | Where-Object { $_.Directory -match "finish" } | Sort-Object $ToNatural + $GlobalVariables = $ScriptPath + "\GlobalVariables.ps1" +} + +## Determine if the setup wizard needs to run +$file = Get-Content $GlobalVariables +$Setup = ($file | Select-String -Pattern '# Set the following to true to enable the setup wizard for first time run').LineNumber +$SetupLine = $Setup++ +$SetupSetting = Invoke-Expression (($file[$SetupLine]).Split("="))[1] + + +## Include GlobalVariables and validate settings (at the moment just check they exist) +. $GlobalVariables + +$vcvars = @("SetupWizard", "reportHeader", "SMTPSRV", "EmailFrom", "EmailTo", "EmailSubject", "DisplaytoScreen", "SendEmail", "SendAttachment", "TimeToRun", "PluginSeconds", "Style", "Date") +foreach ($vcvar in $vcvars) { + if (!($(Get-Variable -Name "$vcvar" -Erroraction 'SilentlyContinue'))) { + Write-Error ($lang.varUndefined -f $vcvar) + } +} + +# Create empty array of resources (i.e. Images) +$global:ReportResources = @{ } + +## Set the StylePath and include it +$StylePath = $ScriptPath + "\Styles\" + $Style +if (!(Test-Path ($StylePath))) { + # The path is not valid + # Use the default style + Write-Warning "Style path ($($StylePath)) is not valid" + $StylePath = $ScriptPath + "\Styles\VMware" + Write-Warning "Using $($StylePath)" +} + +# Import the Style +. ("$($StylePath)\Style.ps1") + + +if ($SetupSetting -or $config -or $GUIConfig) { + #Clear-Host + + ($lang.GetEnumerator() | Where-Object { $_.Name -match "setupMsg[0-9]*" } | Sort-Object Name) | ForEach-Object { + Write-Warning -Message "$($_.value)" + } + + if ($GUIConfig) { + $PluginResult = @() + + # Set the output filename + if (-not (Test-Path -PathType Container $Outputpath)) { New-Item $Outputpath -type directory | Out-Null } + $Filename = ("{0}\{1}_vCheck-Config_{2}.html" -f $Outputpath, $Server, (Get-Date -Format "yyyyMMdd_HHmm")) + + #$configHTML = "" + #$configHTML += Invoke-HTMLSettings -Filename $GlobalVariables + $PluginResult += Invoke-HTMLSettings -Filename $GlobalVariables + Foreach ($plugin in $vCheckPlugins) { + #$configHTML += Invoke-HTMLSettings -Filename $plugin.Fullname + $PluginResult += Invoke-HTMLSettings -Filename $plugin.Fullname + } + + # Run Style replacement + $MyConfig = Get-ReportHTML + # Always generate the report with embedded images + $embedConfig = $MyConfig + # Loop over all CIDs and replace them + Foreach ($cid in $global:ReportResources.Keys) { + $embedConfig = $embedConfig -replace ("cid:{0}" -f $cid), (Get-ReportResource $cid -ReturnType "embed") + } + + $embedConfig | Out-File $Filename + Invoke-Item $Filename + ($lang.GetEnumerator() | Where-Object { $_.Name -match "configMsg[0-9]*" } | Sort-Object Name) | ForEach-Object { + Write-Warning -Message "$($_.value)" + } + + } elseif ($config) { + Invoke-Settings -Filename $GlobalVariables -GB $true + Foreach ($plugin in $vCheckPlugins) { + Invoke-Settings -Filename $plugin.Fullname + } + } +} + +#endregion initialization +if (-not $GUIConfig) { + + #region scriptlogic + ################################################################################ + # Script logic # + ################################################################################ + # Start generating the report + $PluginResult = @() + + Write-Warning -Message $lang.pluginBegin + + # Loop over all enabled plugins + $p = 0 + $vCheckPlugins | Foreach { + $TableFormat = $null + $PluginInfo = Get-PluginID $_.Fullname + $p++ + Write-CustomOut ($lang.pluginStart -f $PluginInfo["Title"], $PluginInfo["Author"], $PluginInfo["Version"], $p, $vCheckPlugins.count) + $pluginStatus = ($lang.pluginStatus -f $p, $vCheckPlugins.count, $_.Name) + Write-Progress -ID 1 -Activity $lang.pluginActivity -Status $pluginStatus -PercentComplete (100 * $p/($vCheckPlugins.count)) + $TTR = [math]::round((Measure-Command { $Details = @(. $_.FullName)}).TotalSeconds, 2) + + Write-CustomOut ($lang.pluginEnd -f $PluginInfo["Title"], $PluginInfo["Author"], $PluginInfo["Version"], $p, $vCheckPlugins.count) + # Do a replacement for [count] for number of items returned in $header + $Header = $Header -replace "\[count\]", $Details.count + + $PluginResult += New-Object PSObject -Property @{ + "Title" = $Title; + "Author" = $PluginInfo["Author"]; + "Version" = $PluginInfo["Version"]; + "Details" = $Details; + "Display" = $Display; + "TableFormat" = $TableFormat; + "Header" = $Header; + "Comments" = $Comments; + "TimeToRun" = $TTR; + } + } + Write-Progress -ID 1 -Activity $lang.pluginActivity -Status $lang.Complete -Completed + + # Add report on plugins + if ($reportOnPlugins) { + $Comments = "Plugins in numerical order" + $Plugins = @() + foreach ($Plugin in (Get-ChildItem $PluginsFolder -Include *.ps1, *.ps1.disabled -Recurse)) { + $Plugins += New-Object PSObject -Property @{ + "Name" = (Get-PluginID $Plugin.FullName).Title; + "Enabled" = (($vCheckPlugins | Select-Object -ExpandProperty FullName) -Contains $plugin.FullName) + } + } + + if ($ListEnabledPluginsFirst) { + $Plugins = $Plugins | Sort-Object -property @{ Expression = "Enabled"; Descending = $true } + $Comments = "Plugins in numerical order, enabled plugins listed first" + } + + $PluginResult += New-Object PSObject -Property @{ + "Title" = $lang.repPRTitle; + "Author" = "vCheck"; + "Version" = $vCheckVersion; + "Details" = $Plugins; + "Display" = "Table"; + "TableFormat" = $null; + "Header" = $lang.repPRTitle; + "Comments" = $Comments; + "TimeToRun" = 0; + } + } + + # Add Time to Run detail for plugins - if specified in GlobalVariables.ps1 + if ($TimeToRun) { + $Finished = Get-Date + $PluginResult += New-Object PSObject -Property @{ + "Title" = $lang.repTTRTitle; + "Author" = "vCheck"; + "Version" = $vCheckVersion; + "Details" = ($PluginResult | Where-Object { $_.TimeToRun -gt $PluginSeconds } | Select-Object Title, TimeToRun | Sort-Object TimeToRun -Descending); + "Display" = "List"; + "TableFormat" = $null; + "Header" = ($lang.repTime -f [math]::round(($Finished - $Date).TotalMinutes, 2), ($Finished.ToLongDateString()), ($Finished.ToLongTimeString())); + "Comments" = ($lang.slowPlugins -f $PluginSeconds); + "TimeToRun" = 0; + } + } + + #endregion scriptlogic + + #region output + ################################################################################ + # Output # + ################################################################################ + # Loop over plugin results and generate HTML from style + $emptyReport = $true + $p = 1 + Foreach ($pr in $PluginResult) { + If ($pr.Details) { + $emptyReport = $false + switch ($pr.Display) { + "List" { $pr.Details = Get-HTMLList $pr.Details } + "Table" { $pr.Details = Get-HTMLTable $pr.Details $pr.TableFormat } + "Chart" { $pr.Details = Get-HTMLChart "plugin$($p)" $pr.Details } + default { $pr.Details = $null } + } + $pr | Add-Member -Type NoteProperty -Name pluginID -Value "plugin-$p" + $p++ + } + if ($pr.Details -ne $null) + { + $emptyReport = $false + } + } + + # Run Style replacement + $MyReport = Get-ReportHTML + + # Set the output filename + if (-not (Test-Path -PathType Container $Outputpath)) { New-Item $Outputpath -type directory | Out-Null } + $Filename = ("{0}\{1}_vCheck_{2}.htm" -f $Outputpath, $VIServer, (Get-Date -Format "yyyyMMdd_HHmm")) + + # Always generate the report with embedded images + $embedReport = $MyReport + # Loop over all CIDs and replace them + Foreach ($cid in $global:ReportResources.Keys) { + $embedReport = $embedReport -replace ("cid:{0}" -f $cid), (Get-ReportResource $cid -ReturnType "embed") + } + $embedReport | Out-File -encoding ASCII -filepath $Filename + + # Display to screen + if ($DisplayToScreen -and (!($emptyReport -and !$DisplayReportEvenIfEmpty))) { + Write-CustomOut $lang.HTMLdisp + Invoke-Item $Filename + } + + # Generate email + if ($SendEmail -and (!($emptyReport -and !$EmailReportEvenIfEmpty))) { + Write-CustomOut $lang.emailSend + $msg = New-Object System.Net.Mail.MailMessage ($EmailFrom, $EmailTo) + # If CC address specified, add + If ($EmailCc -ne "") { + $msg.CC.Add($EmailCc) + } + $msg.subject = $EmailSubject + + # if send attachment, just send plaintext email with HTML report attached + If ($SendAttachment) { + $msg.Body = $lang.emailAtch + $attachment = new-object System.Net.Mail.Attachment $Filename + $msg.Attachments.Add($attachment) + } + # Otherwise send the HTML email + else { + $msg.IsBodyHtml = $true; + $html = [System.Net.Mail.AlternateView]::CreateAlternateViewFromString($MyReport, $null, 'text/html') + $msg.AlternateViews.Add($html) + + # Loop over all CIDs and replace them + Foreach ($cid in $global:ReportResources.Keys) { + if ($global:ReportResources[$cid].Uses -gt 0) { + $lr = (Get-ReportResource $cid -ReturnType "linkedresource") + $html.LinkedResources.Add($lr); + } + } + } + # Send the email + $smtpClient = New-Object System.Net.Mail.SmtpClient + + # Find the VI Server and port from the global settings file + $smtpClient.Host = ($SMTPSRV -Split ":")[0] + if (($SMTPSRV -split ":")[1]) { + $smtpClient.Port = ($SMTPSRV -split ":")[1] + } + + if ($EmailSSL -eq $true) { + $smtpClient.EnableSsl = $true + } + $smtpClient.UseDefaultCredentials = $true; + $smtpClient.Send($msg) + If ($SendAttachment) { $attachment.Dispose() } + $msg.Dispose() + } + + # Run EndScript once everything else is complete + if (Test-Path ($ScriptPath + "\EndScript.ps1")) { + . ($ScriptPath + "\EndScript.ps1") + } + + #endregion output +} From 55daa6f553cc246bcb5989fadf592883b1ac02b3 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Fri, 6 Oct 2017 21:44:41 -0700 Subject: [PATCH 11/29] Updated for Clarity Theme pic Updated for clarity theme pic and removed updates section as it was out of date and is better to look at the pull requests in github --- README.md | 44 +------------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/README.md b/README.md index 11ebbcd9..fcde71e0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [Join the VMware Code and #vCheck channel on slack and ask questions here!](https://code.vmware.com/slack/) -![Alt text](https://vcheck.report/samples/vCheck-vSphere_Screenshot.PNG "vCheck Sample") +![Alt text](http://www.virtu-al.net/wp-content/uploads/2017/10/vCheck_Clarity.jpg "vCheck Sample") |Navigation| |-----------------| @@ -23,7 +23,6 @@ |[Features](#Features)| |[Installing](#Installing)| |[Enhancements](#Enhancements)| -|[Release Notes](#ReleaseNotes)| |[Contributing](#Contributing)| |[Plugins](#Plugins)| |[Styles](#Styles)| @@ -124,47 +123,6 @@ Copy the vCheck files to the desired location. Run the script 'vCheck.ps1' -Con In the meantime, don't hesitate to pop over to the [#vCheck channel on slack](https://code.vmware.com/slack/) and join in on active conversations about anything you see- or don't see- here! - - -# Release Notes -[*Back to top*](#Title) - -* 6.22 - Fixes to VMware style. Consolidating plugins. Updates to style handling. -* 6.21 - Added support for charts. New plugins. Support non-standard vCenter Ports. Bugfixes -* 6.20 - First tagged release. Bugfixes. Email resource support added. -* 6.19 - Bugfixes. -* 6.18 - Added Job parameter to allow job specifications via XML file -* 6.17 - Basic Internationalization (i18n) support -* 6.16 - Table formatting rules -* 6.15 - Added Category to all plugins and features to vCheckUtils script for Categorys. -* 6.14 - Fixed a bug where a plugin was resetting the $VM variable so later plugins were not working :( -* 6.13 - Fixed issue with plugins 63 and 65 not using the days -* 6.12 - Changed Version to PluginVersion in each Plugin as the word Version is very hard to isolate! -* 6.11 - Fixed a copy and paste mistake and plugin issues. -* 6.10 - Fixed multiple spelling mistakes and small plugin issues -* 6.9 - Fixed VMKernel logs but had to remove date/Time parser due to inconsistent VMKernel Log entries -* 6.8 - Added Creator of snapshots back in due to popular demand -* 6.7 - Added Multiple plugins from contributors - Thanks! -* 6.6 - Tech Support Mode Plugin fixed to work with 5.0 hosts -* 6.5 - HW Version plugin fixed due to string output -* 6.4 - Added a 00 plugin and VeryLastPlugin for vCenter connection info to separate the report entirely from VMware if needed. -* 6.3 - Changed the format of each Plugin so you can include a count for each header and altered plugin layout for each plugin. -* 6.2 - Added Time to Run section based on TimeToBuild by Frederic Martin -* 6.1 - Bug fixes, filter for ps1 files only in the plugins folder so other files can be kept in the plugins folder. -* 6.0 - Moved plugins into seperate scripts to make it easier to expand vCheck and fixed issues + lots lots more ! -* 5.1 - Code Fixes and ability to change colour for title text to fix issue with Outlook 2007/10 not displaying correctly -* 5.0 - Changed the order and a few titles etc, tidy up ! -* 4.9 - Added Inacessable VMs -* 4.8 - Added HA VM restarts and resets -* 4.7 - VMTools Issues -* 4.6 - Added VCB Garbage -* 4.5 - Added Host config issues -* 4.4 - Added Disk Overcommit check -* 4.3 - Added vSwitch free ports check -* 4.2 - Added General Capacity Information based on CPU and MEM ussage per cluster -* 4.1 - Added the ability to change the colours of the report. -* 4.0 - HTML Tidy up, comments added for each item and the ability to enable/disable comments. - # Contributing From 121feb8e223f313d517c560509f264a1beb8fa3e Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Fri, 6 Oct 2017 21:45:35 -0700 Subject: [PATCH 12/29] Updated default theme to Clarity Updated default theme to Clarity --- GlobalVariables.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GlobalVariables.ps1 b/GlobalVariables.ps1 index bd3a1329..9719f3f5 100644 --- a/GlobalVariables.ps1 +++ b/GlobalVariables.ps1 @@ -32,7 +32,7 @@ $EmailReportEvenIfEmpty = $true # If you would prefer the HTML file as an attachment then enable the following: $SendAttachment = $false # Set the style template to use. -$Style = "VMware" +$Style = "Clarity" # Do you want to include plugin details in the report? $reportOnPlugins = $true # List Enabled plugins first in Plugin Report? From aa70e7fccb77fef37773732b6724edaf2f26ca95 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Tue, 17 Oct 2017 14:55:12 +1100 Subject: [PATCH 13/29] Fix character encoding Looks like there was an issue with character encoding in the latest commit... --- .../00 Connection Plugin for vCenter.ps1 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 index 07f01636..0bff6e3b 100644 --- a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 +++ b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 @@ -437,10 +437,10 @@ PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred -Recurse [cmdletbinding()] param( [VMware.VimAutomation.ViCore.Types.V1.VIServer]$Server = $global:DefaultVIServer, - [parameter(Mandatory=$true,ValueFromPipelineByPropertyName,ParameterSetName=�Datastore�)] + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName,ParameterSetName="Datastore")] [Alias('Name')] [string]$Datastore, - [parameter(Mandatory=$true,ParameterSetName=�Path�)] + [parameter(Mandatory=$true,ParameterSetName="Path")] [string]$Path = '', [PSCredential]$Credential, [Switch]$Recurse = $false, @@ -545,9 +545,12 @@ PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred -Recurse $tSize = $_.Groups['Filesize'].Value if($Unit.IsPresent){ $friendly = $tSize | Get-FriendlyUnit - $obj.Add('Size',[Math]::Round($friendly.Value,0)) $obj.Add('Unit',$friendly.Unit) } + $obj.Add('Size',[Math]::Round($friendly.Value,0)) + $obj.Add('Unit',$friendly.Unit) + } else{ - $obj.Add('Size',$tSize) } + $obj.Add('Size',$tSize) + } } else{ $obj.Add('Size','') From d45056d7fa133358587de0ef68cca2401feff017 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Tue, 17 Oct 2017 15:23:34 +1100 Subject: [PATCH 14/29] blah --- Plugins/60 VM/32 VM CPU Percent RDY.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/60 VM/32 VM CPU Percent RDY.ps1 b/Plugins/60 VM/32 VM CPU Percent RDY.ps1 index 4a0b1f0f..782f84d9 100644 --- a/Plugins/60 VM/32 VM CPU Percent RDY.ps1 +++ b/Plugins/60 VM/32 VM CPU Percent RDY.ps1 @@ -10,14 +10,14 @@ $PluginCategory = "vSphere" $PercCPUReady = 10.0 # End of Settings -# Setup plugin-specific language table -$pLang = DATA { - ConvertFrom-StringData @' +# Setup plugin-specific language table +$pLang = DATA { + ConvertFrom-StringData @' pluginActivity = Checking VM CPU RDY % '@ } -# Override the default (en) if it exists in lang directory +# Override the default (en) if it exists in lang directory Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable pLang -ErrorAction SilentlyContinue # Update settings where there is an override From 82d7b27d93bcd7fe4f05cfda00ffa0ef00c2f1c5 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Tue, 17 Oct 2017 15:29:35 +1100 Subject: [PATCH 15/29] Actually use ShowTOC variable --- Styles/Clarity/Style.ps1 | 26 ++++++++++++++------------ Styles/CleanGreen/Style.ps1 | 14 ++++++++------ Styles/VMware/Style.ps1 | 36 +++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/Styles/Clarity/Style.ps1 b/Styles/Clarity/Style.ps1 index a1e89e73..07e84bb9 100644 --- a/Styles/Clarity/Style.ps1 +++ b/Styles/Clarity/Style.ps1 @@ -81,22 +81,24 @@ function Get-PluginHTML { Generate table of contents #> function Get-ReportTOC { - $TOCHTML = @" - " + " - return $TOCHTML + return $TOCHTML + } } #endregion diff --git a/Styles/CleanGreen/Style.ps1 b/Styles/CleanGreen/Style.ps1 index 1a3c86ad..5286eb65 100644 --- a/Styles/CleanGreen/Style.ps1 +++ b/Styles/CleanGreen/Style.ps1 @@ -71,13 +71,15 @@ function Get-PluginHTML { Generate table of contents #> function Get-ReportTOC { - $TOCHTML = "
    " - foreach ($pr in ($PluginResult | Where-Object {$_.Details})) { - $TOCHTML += ("
  • {1}
  • " -f $pr.PluginID, $pr.Title) + if ($ShowTOC) { + $TOCHTML = "
      " + foreach ($pr in ($PluginResult | Where-Object {$_.Details})) { + $TOCHTML += ("
    • {1}
    • " -f $pr.PluginID, $pr.Title) + } + $TOCHTML += "
    " + + return $TOCHTML } - $TOCHTML += "
" - - return $TOCHTML } #endregion diff --git a/Styles/VMware/Style.ps1 b/Styles/VMware/Style.ps1 index 32bac19e..66f35b4d 100644 --- a/Styles/VMware/Style.ps1 +++ b/Styles/VMware/Style.ps1 @@ -79,26 +79,28 @@ function Get-PluginHTML { Generate table of contents #> function Get-ReportTOC { - $TOCHTML = "
" - - $i = 0 - foreach ($pr in ($PluginResult | Where-Object {$_.Details})) { - $TOCHTML += ("" -f $pr.PluginID, $pr.Title) - - $i++ - # We have hit the end of the line - if ($i%$ToCColumns -eq 0) { - $TOCHTML +="" + if ($ShowTOC) { + $TOCHTML = "
{1}
" + + $i = 0 + foreach ($pr in ($PluginResult | Where-Object {$_.Details})) { + $TOCHTML += ("" -f $pr.PluginID, $pr.Title) + + $i++ + # We have hit the end of the line + if ($i%$ToCColumns -eq 0) { + $TOCHTML +="" + } + } + # If the row is unfinished, need to pad it out with a cell + if ($i%$ToCColumns -gt 0) { + $TOCHTML += ("" -f ($ToCColumns-($i%$ToCColumns))) } - } - # If the row is unfinished, need to pad it out with a cell - if ($i%$ToCColumns -gt 0) { - $TOCHTML += ("" -f ($ToCColumns-($i%$ToCColumns))) - } - $TOCHTML += "
{1}
  
" + $TOCHTML += "" - return $TOCHTML + return $TOCHTML + } } #endregion From eeaa62c9baa58cae7c97167d11e54856c4e99297 Mon Sep 17 00:00:00 2001 From: Doug Taliaferro Date: Fri, 17 Nov 2017 11:20:06 -0500 Subject: [PATCH 16/29] Add plugin to check host multipath policy. --- Plugins/30 Host/115 Host Multipath Policy.ps1 | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Plugins/30 Host/115 Host Multipath Policy.ps1 diff --git a/Plugins/30 Host/115 Host Multipath Policy.ps1 b/Plugins/30 Host/115 Host Multipath Policy.ps1 new file mode 100644 index 00000000..6c01b1db --- /dev/null +++ b/Plugins/30 Host/115 Host Multipath Policy.ps1 @@ -0,0 +1,29 @@ +$Title = "Host Multipath Policy" +$Comments = "See the Storage/SAN section of the VMware Compatibility Guide or your array/storage vendor's documentation for the supported/recommended policy." +$Display = "Table" +$Author = "Doug Taliaferro" +$PluginVersion = 1.0 +$PluginCategory = "vSphere" + +# Start of Settings +# The Multipath Policy (PSP Plugin) your hosts should be configured to use +$MultipathPolicy = "VMW_PSP_RR" +# End of Settings + +# Update settings where there is an override +$MultipathPolicy = Get-vCheckSetting $Title "MultipathPolicy" $MultipathPolicy + +$lunResults = @() +Foreach ($esxhost in ($HostsViews | Where-Object {$_.Runtime.ConnectionState -match "Connected|Maintenance"})) { + $esxhost | ForEach-Object {$_.config.storageDevice.multipathInfo.lun} | Where-Object {$_.path.count -gt 1 } | Where-Object {$_.policy.policy -ne $MultipathPolicy} | ForEach-Object { + $myObj = "" | Select-Object VMHost, LunID, Policy + $myObj.VMHost = $esxhost.Name + $myObj.LunID = $_.ID + $myObj.Policy = $_.Policy.policy + $lunResults += $myObj + } +} + +$lunResults + +$Header = "Hosts/LUNs not using Multipath Policy '$($MultipathPolicy)' : [count]" \ No newline at end of file From afe93b8549f6de7e47e4fd87d912174aea222cd8 Mon Sep 17 00:00:00 2001 From: Doug Taliaferro Date: Fri, 17 Nov 2017 11:45:57 -0500 Subject: [PATCH 17/29] Add plugin to check host power management policy. --- .../30 Host/120 Power Management Policy.ps1 | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Plugins/30 Host/120 Power Management Policy.ps1 diff --git a/Plugins/30 Host/120 Power Management Policy.ps1 b/Plugins/30 Host/120 Power Management Policy.ps1 new file mode 100644 index 00000000..960a7e2e --- /dev/null +++ b/Plugins/30 Host/120 Power Management Policy.ps1 @@ -0,0 +1,28 @@ +$Title = "Host Power Management Policy" +$Comments = "The following hosts are not using the specified power management policy. Power management may impact performance for latency sensitive workloads. For details see Performance Best Practices for VMware vSphere 6.5" +$Display = "Table" +$Author = "Doug Taliaferro" +$PluginVersion = 1.0 +$PluginCategory = "vSphere" + +# Start of Settings +# Which power management policy should your hosts use? For Balanced enter "dynamic" (this is the ESXi default policy), for High Performance enter "static", for Low power enter "low". +$PowerPolicy = "dynamic" +# End of Settings + +# Update settings where there is an override +$PowerPolicy = Get-vCheckSetting $Title "PowerPolicy" $PowerPolicy + +$hostResults = @() +Foreach ($esxhost in ($HostsViews | Where-Object {$_.Runtime.ConnectionState -match "Connected|Maintenance"})) { + If ($esxhost.config.PowerSystemInfo.CurrentPolicy.ShortName -ne $PowerPolicy) { + $myObj = "" | Select-Object VMHost, PowerPolicy + $myObj.VMHost = $esxhost.Name + $myObj.PowerPolicy = $esxhost.config.PowerSystemInfo.CurrentPolicy.ShortName + $hostResults += $myObj + } +} + +$hostResults + +$Header = "Hosts not using Power Mangement Policy '$($PowerPolicy)' : [count]" \ No newline at end of file From 3f7ecf59f4c110dbd3f13d52cbff8d985e7ccd57 Mon Sep 17 00:00:00 2001 From: danbarr Date: Tue, 5 Dec 2017 09:01:26 -0500 Subject: [PATCH 18/29] Fix crlf line endings in connection plugin --- .../00 Connection Plugin for vCenter.ps1 | 1154 ++++++++--------- 1 file changed, 577 insertions(+), 577 deletions(-) diff --git a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 index 9050c777..d8340353 100644 --- a/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 +++ b/Plugins/00 Initialize/00 Connection Plugin for vCenter.ps1 @@ -1,578 +1,578 @@ -$Title = "Connection settings for vCenter" -$Author = "Alan Renouf" -$PluginVersion = 1.20 -$Header = "Connection Settings" -$Comments = "Connection Plugin for connecting to vSphere" -$Display = "None" -$PluginCategory = "vSphere" - -# Start of Settings -# Please Specify the address (and optional port) of the vCenter server to connect to [servername(:port)] -$Server = "vcsa.local.lab" -# End of Settings - -# Update settings where there is an override -$Server = Get-vCheckSetting $Title "Server" $Server - -# Setup plugin-specific language table -$pLang = DATA { - ConvertFrom-StringData @' - connReuse = Re-using connection to VI Server - connOpen = Connecting to VI Server - connError = Unable to connect to vCenter, please ensure you have altered the vCenter server address correctly. To specify a username and password edit the connection string in the file $GlobalVariables - custAttr = Adding Custom properties - collectVM = Collecting VM Objects - collectHost = Collecting VM Host Objects - collectCluster = Collecting Cluster Objects - collectDatastore = Collecting Datastore Objects - collectDVM = Collecting Detailed VM Objects - collectTemplate = Collecting Template Objects - collectDVIO = Collecting Detailed VI Objects - collectAlarm = Collecting Detailed Alarm Objects - collectDHost = Collecting Detailed VMHost Objects - collectDCluster = Collecting Detailed Cluster Objects - collectDDatastore = Collecting Detailed Datastore Objects - collectDDatastoreCluster = Collecting Detailed Datastore Cluster Objects - collectAlarms = Collecting Alarm Definitions -'@ -} -# Override the default (en) if it exists in lang directory -Import-LocalizedData -BaseDirectory ($ScriptPath + "\lang") -BindingVariable pLang -ErrorAction SilentlyContinue - -# Find the VI Server and port from the global settings file -$VIServer = ($Server -Split ":")[0] -if (($server -split ":")[1]) { - $port = ($server -split ":")[1] -} -else -{ - $port = 443 -} - -# Path to credentials file which is automatically created if needed -$Credfile = $ScriptPath + "\Windowscreds.xml" - -# -# Adding PowerCLI core module/pssnapin -# -# Possibilities: -# 1) PSSnpain (-le 5.8R1) -# 2) Module + PSSnapin (-gt 5.8R1/-lt 6.5R1) -# 3) Module (-ge 6.5R1) - -function Get-CorePlatform { - [cmdletbinding()] - param() - #Thanks to @Lucd22 (Lucd.info) for this great function! - $osDetected = $false - try{ - $os = Get-CimInstance -ClassName Win32_OperatingSystem - Write-Verbose -Message 'Windows detected' - $osDetected = $true - $osFamily = 'Windows' - $osName = $os.Caption - $osVersion = $os.Version - $nodeName = $os.CSName - $architecture = $os.OSArchitecture - } - catch{ - Write-Verbose -Message 'Possibly Linux or Mac' - $uname = "$(uname)" - if($uname -match '^Darwin|^Linux'){ - $osDetected = $true - $osFamily = $uname - $osName = "$(uname -v)" - $osVersion = "$(uname -r)" - $nodeName = "$(uname -n)" - $architecture = "$(uname -p)" - } - # Other - else - { - Write-Warning -Message "Kernel $($uname) not covered" - } - } - [ordered]@{ - OSDetected = $osDetected - OSFamily = $osFamily - OS = $osName - Version = $osVersion - Hostname = $nodeName - Architecture = $architecture - } -} - -$Platform = Get-CorePlatform -switch ($platform.OSFamily) { - "Darwin" { - $templocation = "/tmp" - $Outputpath = $templocation - Get-Module -ListAvailable PowerCLI* | Import-Module - } - "Linux" { - $templocation = "/tmp" - $Outputpath = $templocation - Get-Module -ListAvailable PowerCLI* | Import-Module - } - "Windows" { - $templocation = "$ENV:Temp" - $pcliCore = 'VMware.VimAutomation.Core' - - $pssnapinPresent = $false - $psmodulePresent = $false - - if(Get-Module -Name $pcliCore -ListAvailable){ - $psmodulePresent = $true - if(!(Get-Module -Name $pcliCore)){ - Import-Module -Name $pcliCore - } - } - - if(Get-PSSnapin -Name $pcliCore -Registered -ErrorAction SilentlyContinue){ - $pssnapinPresent = $true - if(!(Get-PSSnapin -Name $pcliCore -ErrorAction SilentlyContinue)){ - Add-PSSnapin -Name $pcliCore - } - } - - if(!$pssnapinPresent -and !$psmodulePresent){ - Write-Error "Can't find PowerCLI. Is it installed?" - return - } - } -} - -$OpenConnection = $global:DefaultVIServers | Where-Object { $_.Name -eq $VIServer } -if($OpenConnection.IsConnected) { - Write-CustomOut ( "{0}: {1}" -f $pLang.connReuse, $Server ) - $VIConnection = $OpenConnection -} else { - Write-CustomOut ( "{0}: {1}" -f $pLang.connOpen, $Server ) - $VIConnection = Connect-VIServer -Server $VIServer -Port $Port -} - -if (-not $VIConnection.IsConnected) { - Write-Error $pLang.connError -} - -Write-CustomOut $pLang.custAttr - -function Get-VMLastPoweredOffDate { - param([Parameter(Mandatory=$true,ValueFromPipeline=$true)] - [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $vm) - process { - $Report = "" | Select-Object -Property Name,LastPoweredOffDate - $Report.Name = $_.Name - $Report.LastPoweredOffDate = (Get-VIEventPlus -Entity $vm | ` - Where-Object { $_.Gettype().Name -eq "VmPoweredOffEvent" } | ` - Select-Object -First 1).CreatedTime - $Report - } -} - -function Get-VMLastPoweredOnDate { - param([Parameter(Mandatory=$true,ValueFromPipeline=$true)] - [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $vm) - - process { - $Report = "" | Select-Object -Property Name,LastPoweredOnDate - $Report.Name = $_.Name - $Report.LastPoweredOnDate = (Get-VIEventPlus -Entity $vm | ` - Where-Object { $_.Gettype().Name -match "VmPoweredOnEvent" } | ` - Select-Object -First 1).CreatedTime - $Report - } -} - -New-VIProperty -Name LastPoweredOffDate -ObjectType VirtualMachine -Value {(Get-VMLastPoweredOffDate -vm $Args[0]).LastPoweredOffDate} | Out-Null -New-VIProperty -Name LastPoweredOnDate -ObjectType VirtualMachine -Value {(Get-VMLastPoweredOnDate -vm $Args[0]).LastPoweredOnDate} | Out-Null - -New-VIProperty -Name PercentFree -ObjectType Datastore -Value { - param($ds) - [math]::Round(((100 * ($ds.FreeSpaceMB)) / ($ds.CapacityMB)),2) -} -Force | Out-Null - -New-VIProperty -Name "HWVersion" -ObjectType VirtualMachine -Value { - param($vm) - - $vm.ExtensionData.Config.Version.Substring(4) -} -BasedOnExtensionProperty "Config.Version" -Force | Out-Null - -Write-CustomOut $pLang.collectVM -$VM = Get-VM | Sort-Object Name -Write-CustomOut $pLang.collectHost -$VMH = Get-VMHost | Sort-Object Name -Write-CustomOut $pLang.collectCluster -$Clusters = Get-Cluster | Sort-Object Name -Write-CustomOut $pLang.collectDatastore -$Datastores = Get-Datastore | Sort-Object Name -Write-CustomOut $pLang.collectDVM -$FullVM = Get-View -ViewType VirtualMachine | Where-Object {-not $_.Config.Template} -Write-CustomOut $pLang.collectTemplate -$VMTmpl = Get-Template -Write-CustomOut $pLang.collectDVIO -$ServiceInstance = get-view ServiceInstance -Write-CustomOut $pLang.collectAlarm -$alarmMgr = get-view $ServiceInstance.Content.alarmManager -Write-CustomOut $pLang.collectDHost -$HostsViews = Get-View -ViewType hostsystem -Write-CustomOut $pLang.collectDCluster -$clusviews = Get-View -ViewType ClusterComputeResource -Write-CustomOut $pLang.collectDDatastore -$storageviews = Get-View -ViewType Datastore -Write-CustomOut $pLang.collectAlarms -$valarms = $alarmMgr.GetAlarm($null) | Select-Object value, @{N="name";E={(Get-View -Id $_).Info.Name}} - -# Find out which version of the API we are connecting to -$VIVersion = ((Get-View ServiceInstance).Content.About.Version).Chars(0) - -# Check to see if its a VCSA or not -if ($ServiceInstance.Client.ServiceContent.About.OsType -eq "linux-x64"){ $VCSA = $true } - -# Check for vSphere -If ($VIVersion -ge 4){ - $vSphere = $true -} - -if ($VIVersion -ge 5) { - Write-CustomOut $pLang.collectDDatastoreCluster - $DatastoreClustersView = Get-View -viewtype StoragePod -} - -<# -.SYNOPSIS Returns vSphere events -.DESCRIPTION The function will return vSphere events. With - the available parameters, the execution time can be - improved, compered to the original Get-VIEvent cmdlet. -.NOTES Author: Luc Dekens -.PARAMETER Entity - When specified the function returns events for the - specific vSphere entity. By default events for all - vSphere entities are returned. -.PARAMETER EventType - This parameter limits the returned events to those - specified on this parameter. -.PARAMETER Start - The start date of the events to retrieve -.PARAMETER Finish - The end date of the events to retrieve. -.PARAMETER Recurse - A switch indicating if the events for the children of - the Entity will also be returned -.PARAMETER User - The list of usernames for which events will be returned -.PARAMETER System - A switch that allows the selection of all system events. -.PARAMETER ScheduledTask - The name of a scheduled task for which the events - will be returned -.PARAMETER FullMessage - A switch indicating if the full message shall be compiled. - This switch can improve the execution speed if the full - message is not needed. -.PARAMETER UseUTC - A switch indicating if the event shoukld remain in UTC or - local time. -.EXAMPLE - PS> Get-VIEventPlus -Entity $vm -.EXAMPLE - PS> Get-VIEventPlus -Entity $cluster -Recurse:$true -#> -function Get-VIEventPlus { - - param( - [VMware.VimAutomation.ViCore.Types.V1.Inventory.InventoryItem[]]$Entity, - [string[]]$EventType, - [DateTime]$Start, - [DateTime]$Finish = (Get-Date), - [switch]$Recurse, - [string[]]$User, - [Switch]$System, - [string]$ScheduledTask, - [switch]$FullMessage = $false, - [switch]$UseUTC = $false - ) - - process { - $eventnumber = 100 - $events = @() - $eventMgr = Get-View EventManager - $eventFilter = New-Object VMware.Vim.EventFilterSpec - $eventFilter.disableFullMessage = ! $FullMessage - $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity - $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} - $eventFilter.eventTypeId = $EventType - if($Start -or $Finish){ - $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime - if($Start){ - $eventFilter.time.beginTime = $Start - } - if($Finish){ - $eventFilter.time.endTime = $Finish - } - } - if($User -or $System){ - $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername - if($User){ - $eventFilter.UserName.userList = $User - } - if($System){ - $eventFilter.UserName.systemUser = $System - } - } - if($ScheduledTask){ - $si = Get-View ServiceInstance - $schTskMgr = Get-View $si.Content.ScheduledTaskManager - $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | - Where-Object {$_.Info.Name -match $ScheduledTask} | - Select-Object -First 1 | - Select-Object -ExpandProperty MoRef - } - if(!$Entity){ - $Entity = @(Get-Folder -NoRecursion) - } - $entity | Foreach-Object { - $eventFilter.entity.entity = $_.ExtensionData.MoRef - $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - while($eventsBuffer){ - $events += $eventsBuffer - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - } - $eventCollector.DestroyCollector() - } - if (-not $UseUTC) - { - $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } - } - - $events - } -} - -function Get-FriendlyUnit{ -<# -.SYNOPSIS Convert numbers into smaller binary multiples -.DESCRIPTION The function accepts a value and will convert it -into the biggest binary unit available. -.NOTES Author: Luc Dekens -.PARAMETER Value -The value you want to convert. -This number must be positive. -.PARAMETER IEC -A switch to indicate if the function shall return the IEC -unit names, or the more commonly used unit names. -The default is to use the commonly used unit names. -.EXAMPLE -PS> Get-FriendlyUnit -Value 123456 -.EXAMPLE -PS> 123456 | Get-FriendlyUnit -IEC -.EXAMPLE -PS> Get-FriendlyUnit -Value 123456,789123, 45678 -#> - - param( - [CmdletBinding()] - [parameter(Mandatory = $true, ValueFromPipeline = $true)] - [double[]]$Value, - [switch]$IEC - ) - - begin{ - $OldUnits = "B","KB","MB","GB","TB","PB","EB","ZB","YB" - $IecUnits = "B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB" - if($IEC){$units = $IecUnits}else{$units=$OldUnits} - } - - process{ - $Value | %{ - if($_ -lt 0){ - write-Error "Numbers must be positive." - break - } - if($value -gt 0){ - $modifier = [math]::Floor([Math]::Log($_,1KB)) - } - else{ - $modifier = 0 - } - New-Object PSObject -Property @{ - Value = $_ / [math]::Pow(1KB,$modifier) - Unit = &{if($modifier -lt $units.Count){$units[$modifier]}else{"1KB E{0}" -f $modifier}} - } - } - } -} - -function Get-HttpDatastoreItem{ -<# -.SYNOPSIS Get file and folder info from datastore -.DESCRIPTION This function will retrieve a file and folders -list from a datastore. The function uses the HTTP access to -a datastore to obtain the list. -.NOTES Author: Luc Dekens -.PARAMETER Datastore -The datastore for which to retrieve the list -.PARAMETER Path -The folder path from where to start listing files and folders. -The default is to start from the root of the datastore. -.EXAMPLE -.PARAMETER Credential -A credential for an account that has access to the datastore -.PARAMETER Recurse -A switch that defines if the files and folders list shall be recursive -.PARAMETER IncludeRoot -A switch to indicate if the root of the search path shall be included -.PARAMETER Unit -A switch that defines if the filesize shall be returned in friendly units. -Requires the Get-FriednlyUnit function -.EXAMPLE -PS> Get-HttpDatastoreItem -Datastore DS1 -Credential $cred -.EXAMPLE -PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred -.EXAMPLE -PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred -Recurse -#> - - [cmdletbinding()] - param( - [VMware.VimAutomation.ViCore.Types.V1.VIServer]$Server = $global:DefaultVIServer, - [parameter(Mandatory=$true,ValueFromPipelineByPropertyName,ParameterSetName="Datastore")] - [Alias('Name')] - [string]$Datastore, - [parameter(Mandatory=$true,ParameterSetName="Path")] - [string]$Path = '', - [PSCredential]$Credential, - [Switch]$Recurse = $false, - [Switch]$IncludeRoot = $false, - [Switch]$Unit = $false - ) - - Begin{ - $regEx = [RegEx]'(?.*?)(?.*?)(?[0-9]+|[ -]+)' - } - - Process{ - Write-Verbose -Message "$(Get-Date) $($s = Get-PSCallStack;">Entering {0}" -f $s[0].FunctionName)" - Write-Verbose -Message "$(Get-Date) Datastore:$($Datastore) Path:$($Path)" - Write-Verbose -Message "$(Get-Date) Recurse:$($Recurse.IsPresent)" - - Switch($PSCmdlet.ParameterSetName){ - 'Datastore' { - $Folder = '' - } - 'Path' { - $Datastore,$folderQualifier = $Path.Split('\[\] /',[System.StringSplitOptions]::RemoveEmptyEntries) - - if(-not $folderQualifier){ - $lastParent = '' - $lastQualifier = '' - } - else{ - if($folderQualifier.Count -eq 1){ - $lastParent = '' - $lastQualifier = "$($folderQualifier)$(if($Path -match "/$"){'/'})" - } - else{ - $lastQualifier = "$($folderQualifier[-1])$(if($Path -match "/$"){'/'})" - $lastParent = "$($folderQualifier[0..($folderQualifier.Count-2)] -join '/')/" - } - } - } - Default { - Throw "Invalid parameter combination" - } - } - $folderQualifier = $folderQualifier -join '/' - if($Path -match "/$" -and $folderQualifier -notmatch "/$"){ - $folderQualifier += '/' - } - $stack = Get-PSCallStack | Select -ExpandProperty Command - if(($stack | Group-Object -AsHashTable -AsString)[$stack[0]].Count -eq 1){ - Write-Verbose "First call" - $sDFile = @{ - Server = $Server - Credential = $Credential - Path = "[$($Datastore)]$(if($lastParent){"" $($lastParent)""})" - Recurse = $Recurse.IsPresent - IncludeRoot = $IncludeRoot.IsPresent - Unit = $Unit.IsPresent - } - $allEntry = Get-HttpDatastoreItem @sDFile - $entry = $allEntry | where{$_.Name -match "^$($lastQualifier)/*$"} - if($entry.Name -match "\/$"){ - # It's a folder - if($lastQualifier -notmatch "/$"){ - $folderQualifier += '/' - } - if($IncludeRoot.IsPresent){ - $entry - } - } - else{ - # It's a file - $entry - } - } - - if($folderQualifier -match "\/$" -or -not $folderQualifier){ - $ds = Get-Datastore -Name $Datastore -Server $Server -Verbose:$false - $dc = Get-VMHost -Datastore $ds -Verbose:$false -Server $Server | Get-Datacenter -Verbose:$false -Server $Server - $uri = "https://$($Server.Name)/folder$(if($folderQualifier){'/' + $folderQualifier})?dcPath=$($dc.Name)&dsName=$($ds.Name)" - Write-Verbose "Looking at URI: $($uri)" - Try{ - $response = Invoke-WebRequest -Uri $Uri -Method Get -Credential $Credential - } - Catch{ - $errorMsg = "`n$(Get-Date -Format 'yyyyMMdd HH:mm:ss') HTTP $($_.Exception.Response.ProtocolVersion)" + - " $($_.Exception.Response.Method) $($_.Exception.Response.StatusCode.Value__)" + - " $($_.Exception.Response.StatusDescription)`n" + - "$(Get-Date -Format 'yyyyMMdd HH:mm:ss') Uri $($_.Exception.Response.ResponseUri)`n " - Write-Error -Message $errorMsg - break - } - foreach($entry in $response){ - $regEx.Matches($entry.Content) | - Where{$_.Success -and $_.Groups['Filename'].Value -notmatch 'Parent Datacenter|Parent Directory'} | %{ - Write-Verbose "`tFound $($_.Groups['Filename'].Value)" - $fName = $_.Groups['Filename'].Value - $obj = [ordered]@{ - Name = $_.Groups['Filename'].Value - FullName = "[$($ds.Name)] $($folderQualifier)$(if($folderQualifier -notmatch '/$' -and $folderQualifier){'/'})$($_.Groups['Filename'].Value)" - Timestamp = [DateTime]$_.Groups['Timestamp'].Value - } - if($fName -notmatch "/$"){ - $tSize = $_.Groups['Filesize'].Value - if($Unit.IsPresent){ - $friendly = $tSize | Get-FriendlyUnit - $obj.Add('Size',[Math]::Round($friendly.Value,0)) - $obj.Add('Unit',$friendly.Unit) - } - else{ - $obj.Add('Size',$tSize) - } - } - else{ - $obj.Add('Size','') - if($Unit.IsPresent){ - $obj.Add('Unit','') - } - } - New-Object PSObject -Property $obj - if($_.Groups['Filename'].Value -match "/$" -and $Recurse.IsPresent){ - $sDFile = @{ - Server = $Server - Credential = $Credential - Path = "[$($ds.Name)] $($folderQualifier)$(if($folderQualifier -notmatch '/$' -and $folderQualifier){'/'})$($_.Groups['Filename'].Value)" - Recurse = $Recurse.IsPresent - IncludeRoot = $IncludeRoot - Unit = $Unit.IsPresent - } - Get-HttpDatastoreItem @sDFile - } - } - } - } - Write-Verbose -Message "$(Get-Date) $($s = Get-PSCallStack;" Get-VIEventPlus -Entity $vm +.EXAMPLE + PS> Get-VIEventPlus -Entity $cluster -Recurse:$true +#> +function Get-VIEventPlus { + + param( + [VMware.VimAutomation.ViCore.Types.V1.Inventory.InventoryItem[]]$Entity, + [string[]]$EventType, + [DateTime]$Start, + [DateTime]$Finish = (Get-Date), + [switch]$Recurse, + [string[]]$User, + [Switch]$System, + [string]$ScheduledTask, + [switch]$FullMessage = $false, + [switch]$UseUTC = $false + ) + + process { + $eventnumber = 100 + $events = @() + $eventMgr = Get-View EventManager + $eventFilter = New-Object VMware.Vim.EventFilterSpec + $eventFilter.disableFullMessage = ! $FullMessage + $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity + $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} + $eventFilter.eventTypeId = $EventType + if($Start -or $Finish){ + $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime + if($Start){ + $eventFilter.time.beginTime = $Start + } + if($Finish){ + $eventFilter.time.endTime = $Finish + } + } + if($User -or $System){ + $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername + if($User){ + $eventFilter.UserName.userList = $User + } + if($System){ + $eventFilter.UserName.systemUser = $System + } + } + if($ScheduledTask){ + $si = Get-View ServiceInstance + $schTskMgr = Get-View $si.Content.ScheduledTaskManager + $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | + Where-Object {$_.Info.Name -match $ScheduledTask} | + Select-Object -First 1 | + Select-Object -ExpandProperty MoRef + } + if(!$Entity){ + $Entity = @(Get-Folder -NoRecursion) + } + $entity | Foreach-Object { + $eventFilter.entity.entity = $_.ExtensionData.MoRef + $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + while($eventsBuffer){ + $events += $eventsBuffer + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + } + $eventCollector.DestroyCollector() + } + if (-not $UseUTC) + { + $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } + } + + $events + } +} + +function Get-FriendlyUnit{ +<# +.SYNOPSIS Convert numbers into smaller binary multiples +.DESCRIPTION The function accepts a value and will convert it +into the biggest binary unit available. +.NOTES Author: Luc Dekens +.PARAMETER Value +The value you want to convert. +This number must be positive. +.PARAMETER IEC +A switch to indicate if the function shall return the IEC +unit names, or the more commonly used unit names. +The default is to use the commonly used unit names. +.EXAMPLE +PS> Get-FriendlyUnit -Value 123456 +.EXAMPLE +PS> 123456 | Get-FriendlyUnit -IEC +.EXAMPLE +PS> Get-FriendlyUnit -Value 123456,789123, 45678 +#> + + param( + [CmdletBinding()] + [parameter(Mandatory = $true, ValueFromPipeline = $true)] + [double[]]$Value, + [switch]$IEC + ) + + begin{ + $OldUnits = "B","KB","MB","GB","TB","PB","EB","ZB","YB" + $IecUnits = "B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB" + if($IEC){$units = $IecUnits}else{$units=$OldUnits} + } + + process{ + $Value | %{ + if($_ -lt 0){ + write-Error "Numbers must be positive." + break + } + if($value -gt 0){ + $modifier = [math]::Floor([Math]::Log($_,1KB)) + } + else{ + $modifier = 0 + } + New-Object PSObject -Property @{ + Value = $_ / [math]::Pow(1KB,$modifier) + Unit = &{if($modifier -lt $units.Count){$units[$modifier]}else{"1KB E{0}" -f $modifier}} + } + } + } +} + +function Get-HttpDatastoreItem{ +<# +.SYNOPSIS Get file and folder info from datastore +.DESCRIPTION This function will retrieve a file and folders +list from a datastore. The function uses the HTTP access to +a datastore to obtain the list. +.NOTES Author: Luc Dekens +.PARAMETER Datastore +The datastore for which to retrieve the list +.PARAMETER Path +The folder path from where to start listing files and folders. +The default is to start from the root of the datastore. +.EXAMPLE +.PARAMETER Credential +A credential for an account that has access to the datastore +.PARAMETER Recurse +A switch that defines if the files and folders list shall be recursive +.PARAMETER IncludeRoot +A switch to indicate if the root of the search path shall be included +.PARAMETER Unit +A switch that defines if the filesize shall be returned in friendly units. +Requires the Get-FriednlyUnit function +.EXAMPLE +PS> Get-HttpDatastoreItem -Datastore DS1 -Credential $cred +.EXAMPLE +PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred +.EXAMPLE +PS> Get-Datastore | Get-HttpDatastoreItem -Credential $cred -Recurse +#> + + [cmdletbinding()] + param( + [VMware.VimAutomation.ViCore.Types.V1.VIServer]$Server = $global:DefaultVIServer, + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName,ParameterSetName="Datastore")] + [Alias('Name')] + [string]$Datastore, + [parameter(Mandatory=$true,ParameterSetName="Path")] + [string]$Path = '', + [PSCredential]$Credential, + [Switch]$Recurse = $false, + [Switch]$IncludeRoot = $false, + [Switch]$Unit = $false + ) + + Begin{ + $regEx = [RegEx]'(?.*?)(?.*?)(?[0-9]+|[ -]+)' + } + + Process{ + Write-Verbose -Message "$(Get-Date) $($s = Get-PSCallStack;">Entering {0}" -f $s[0].FunctionName)" + Write-Verbose -Message "$(Get-Date) Datastore:$($Datastore) Path:$($Path)" + Write-Verbose -Message "$(Get-Date) Recurse:$($Recurse.IsPresent)" + + Switch($PSCmdlet.ParameterSetName){ + 'Datastore' { + $Folder = '' + } + 'Path' { + $Datastore,$folderQualifier = $Path.Split('\[\] /',[System.StringSplitOptions]::RemoveEmptyEntries) + + if(-not $folderQualifier){ + $lastParent = '' + $lastQualifier = '' + } + else{ + if($folderQualifier.Count -eq 1){ + $lastParent = '' + $lastQualifier = "$($folderQualifier)$(if($Path -match "/$"){'/'})" + } + else{ + $lastQualifier = "$($folderQualifier[-1])$(if($Path -match "/$"){'/'})" + $lastParent = "$($folderQualifier[0..($folderQualifier.Count-2)] -join '/')/" + } + } + } + Default { + Throw "Invalid parameter combination" + } + } + $folderQualifier = $folderQualifier -join '/' + if($Path -match "/$" -and $folderQualifier -notmatch "/$"){ + $folderQualifier += '/' + } + $stack = Get-PSCallStack | Select -ExpandProperty Command + if(($stack | Group-Object -AsHashTable -AsString)[$stack[0]].Count -eq 1){ + Write-Verbose "First call" + $sDFile = @{ + Server = $Server + Credential = $Credential + Path = "[$($Datastore)]$(if($lastParent){"" $($lastParent)""})" + Recurse = $Recurse.IsPresent + IncludeRoot = $IncludeRoot.IsPresent + Unit = $Unit.IsPresent + } + $allEntry = Get-HttpDatastoreItem @sDFile + $entry = $allEntry | where{$_.Name -match "^$($lastQualifier)/*$"} + if($entry.Name -match "\/$"){ + # It's a folder + if($lastQualifier -notmatch "/$"){ + $folderQualifier += '/' + } + if($IncludeRoot.IsPresent){ + $entry + } + } + else{ + # It's a file + $entry + } + } + + if($folderQualifier -match "\/$" -or -not $folderQualifier){ + $ds = Get-Datastore -Name $Datastore -Server $Server -Verbose:$false + $dc = Get-VMHost -Datastore $ds -Verbose:$false -Server $Server | Get-Datacenter -Verbose:$false -Server $Server + $uri = "https://$($Server.Name)/folder$(if($folderQualifier){'/' + $folderQualifier})?dcPath=$($dc.Name)&dsName=$($ds.Name)" + Write-Verbose "Looking at URI: $($uri)" + Try{ + $response = Invoke-WebRequest -Uri $Uri -Method Get -Credential $Credential + } + Catch{ + $errorMsg = "`n$(Get-Date -Format 'yyyyMMdd HH:mm:ss') HTTP $($_.Exception.Response.ProtocolVersion)" + + " $($_.Exception.Response.Method) $($_.Exception.Response.StatusCode.Value__)" + + " $($_.Exception.Response.StatusDescription)`n" + + "$(Get-Date -Format 'yyyyMMdd HH:mm:ss') Uri $($_.Exception.Response.ResponseUri)`n " + Write-Error -Message $errorMsg + break + } + foreach($entry in $response){ + $regEx.Matches($entry.Content) | + Where{$_.Success -and $_.Groups['Filename'].Value -notmatch 'Parent Datacenter|Parent Directory'} | %{ + Write-Verbose "`tFound $($_.Groups['Filename'].Value)" + $fName = $_.Groups['Filename'].Value + $obj = [ordered]@{ + Name = $_.Groups['Filename'].Value + FullName = "[$($ds.Name)] $($folderQualifier)$(if($folderQualifier -notmatch '/$' -and $folderQualifier){'/'})$($_.Groups['Filename'].Value)" + Timestamp = [DateTime]$_.Groups['Timestamp'].Value + } + if($fName -notmatch "/$"){ + $tSize = $_.Groups['Filesize'].Value + if($Unit.IsPresent){ + $friendly = $tSize | Get-FriendlyUnit + $obj.Add('Size',[Math]::Round($friendly.Value,0)) + $obj.Add('Unit',$friendly.Unit) + } + else{ + $obj.Add('Size',$tSize) + } + } + else{ + $obj.Add('Size','') + if($Unit.IsPresent){ + $obj.Add('Unit','') + } + } + New-Object PSObject -Property $obj + if($_.Groups['Filename'].Value -match "/$" -and $Recurse.IsPresent){ + $sDFile = @{ + Server = $Server + Credential = $Credential + Path = "[$($ds.Name)] $($folderQualifier)$(if($folderQualifier -notmatch '/$' -and $folderQualifier){'/'})$($_.Groups['Filename'].Value)" + Recurse = $Recurse.IsPresent + IncludeRoot = $IncludeRoot + Unit = $Unit.IsPresent + } + Get-HttpDatastoreItem @sDFile + } + } + } + } + Write-Verbose -Message "$(Get-Date) $($s = Get-PSCallStack;" Date: Tue, 5 Dec 2017 09:28:51 -0500 Subject: [PATCH 19/29] Added setting for datastores to exclude --- Plugins/60 VM/30 Single Storage VMs.ps1 | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Plugins/60 VM/30 Single Storage VMs.ps1 b/Plugins/60 VM/30 Single Storage VMs.ps1 index 29824fa3..e1b03b14 100644 --- a/Plugins/60 VM/30 Single Storage VMs.ps1 +++ b/Plugins/60 VM/30 Single Storage VMs.ps1 @@ -2,21 +2,25 @@ $Title = "Single Storage VMs" $Header = "VMs stored on non shared datastores: [count]" $Comments = "The following VMs are located on storage which is only accessible by 1 host, these will not be compatible with vMotion and may be disconnected in the event of host failure" $Display = "Table" -$Author = "Alan Renouf, Frederic Martin" -$PluginVersion = 1.4 +$Author = "Alan Renouf, Frederic Martin, Dan Barr" +$PluginVersion = 1.5 $PluginCategory = "vSphere" # Start of Settings # Local Stored VMs, do not report on any VMs who are defined here $LVMDoNotInclude = "Template_*|VDI*" +# Local Datastores, do not report on any VMs within these datastores +$LDSDoNotInclude = "Local|datastore1" # End of Settings # Update settings where there is an override $LVMDoNotInclude = Get-vCheckSetting $Title "LVMDoNotInclude" $LVMDoNotInclude +$LDSDoNotInclude = Get-vCheckSetting $Title "LDSDoNotInclude" $LDSDoNotInclude -$unSharedDatastore = $storageviews | Where-Object {-Not $_.summary.multiplehostaccess} | Select-Object -Expand Name +$unSharedDatastore = $storageviews | Where-Object {$_.Name -notmatch $LDSDoNotInclude -and -not $_.summary.multiplehostaccess} | Select-Object -Expand Name $FullVM | Where-Object {$_.Name -notmatch $LVMDoNotInclude} | Where-Object {$_.Runtime.ConnectionState -notmatch "invalid|orphaned"} | Foreach-Object {$_.layoutex.file} | Where-Object {$_.type -ne "log" -and $_.name -notmatch ".vswp$" -And $unSharedDatastore -contains $_.name.Split(']')[0].Split('[')[1]} | Select-Object Name # Change Log -## 1.4 : Added Get-vCheckSetting \ No newline at end of file +## 1.4 : Added Get-vCheckSetting +## 1.5 : Added setting for datastores to exclude in addition to VM names \ No newline at end of file From 34f9be6f37da4ba191dafe6dadc4219ac8c0a42f Mon Sep 17 00:00:00 2001 From: danbarr Date: Tue, 5 Dec 2017 09:55:19 -0500 Subject: [PATCH 20/29] Add support for multiple syslog servers, only report on connected hosts --- Plugins/30 Host/51 Syslog Name.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Plugins/30 Host/51 Syslog Name.ps1 b/Plugins/30 Host/51 Syslog Name.ps1 index cf468c25..97dcfded 100644 --- a/Plugins/30 Host/51 Syslog Name.ps1 +++ b/Plugins/30 Host/51 Syslog Name.ps1 @@ -2,16 +2,19 @@ $Title = "Syslog Name" $Header = "Syslog Issues" $Comments = "The following hosts do not have the correct Syslog settings which may cause issues if ESXi hosts experience issues and logs need to be investigated" $Display = "Table" -$Author = "Jonathan Medd" -$PluginVersion = 1.1 +$Author = "Jonathan Medd, Dan Barr" +$PluginVersion = 1.2 $PluginCategory = "vSphere" # Start of Settings -# The Syslog server which should be set on your hosts -$SyslogServer = "syslogserver" +# The Syslog server(s) which should be set on your hosts (comma-separated) +$SyslogServer = "udp://syslogserver" # End of Settings # Update settings where there is an override $SyslogServer = Get-vCheckSetting $Title "SyslogServer" $SyslogServer -@($VMH | Where-Object {$_.ExtensionData.Summary.Config.Product.Name -eq 'VMware ESXi'} | Select-Object Name,@{Name='SyslogServer';Expression = {($_ | Get-VMHostSysLogServer).Host}},@{Name='SyslogSetting';Expression = {($_ | Get-AdvancedSetting -Name Syslog.Local.DatastorePath).Value| Where-Object {$_ -ne $NULL}}} | Where-Object {$_.SyslogServer -ne $SyslogServer -and $_.SyslogSetting -ne $SyslogServer}) \ No newline at end of file +@($VMH | Where-Object {($_.ExtensionData.Summary.Config.Product.Name -eq 'VMware ESXi') -and ($_.ConnectionState -eq 'Connected')} | Select-Object Name,@{Name='SyslogServer';Expression = {($_ | Get-VMHostSysLogServer).Host}},@{Name='SyslogSetting';Expression = {($_ | Get-AdvancedSetting -Name Syslog.Local.DatastorePath).Value| Where-Object {$_ -ne $NULL}}} | Where-Object {($_.SyslogServer -join ',') -ne $SyslogServer -and $_.SyslogSetting -ne $SyslogServer}) + +# Change Log +## 1.2 : Added support for multiple (comma-delimited) syslog servers; only report on connected hosts \ No newline at end of file From 8682393e420cadea3eb87699895afdbe90fa6b93 Mon Sep 17 00:00:00 2001 From: danbarr Date: Tue, 5 Dec 2017 11:31:33 -0500 Subject: [PATCH 21/29] Fix several plugins which report empty results or errors when hosts are disconnected or not responding --- Plugins/30 Host/29 AD Authentication.ps1 | 41 ++++++++++--------- Plugins/30 Host/31 NTP Name and Service.ps1 | 11 +++-- .../30 Host/35 Host Configuration Issues.ps1 | 33 ++++++++------- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/Plugins/30 Host/29 AD Authentication.ps1 b/Plugins/30 Host/29 AD Authentication.ps1 index e1257bca..218bd7be 100644 --- a/Plugins/30 Host/29 AD Authentication.ps1 +++ b/Plugins/30 Host/29 AD Authentication.ps1 @@ -1,3 +1,11 @@ +$Title = "Active Directory Authentication" +$Header = "Active Directory Authentication" +$Comments = "Active Directory configuration and status for each host. (Domain: $ADDomainName, Admin Group: $ADAdminGroup, Display all results: $ADDisplayOK)" +$Display = "Table" +$Author = "Bill Wall, Dan Barr" +$PluginVersion = 1.2 +$PluginCategory = "vSphere" + # Start of Settings # Show "OK" results? $ADDisplayOK = $false @@ -11,18 +19,18 @@ $ADAdminGroup = "ESX Admins" $ADFailedHosts = @() $ADOKHosts = @() -ForEach ($ADHost in $VMH) { - # Get authetication settings - $myADAuth = $ADHost | Get-VMHostAuthentication - # Get Admin Group settings - $myADGroup = ($ADHost | Get-AdvancedSetting -Name "Config.HostAgent.plugins.hostsvc.esxAdminsGroup").Value - # Build array item - $myADHost = $ADHost | Select-Object Name,@{Name='Domain';Expression = {$myADAuth.Domain}},@{Name='MembershipStatus';Expression = {$myADAuth.DomainMembershipStatus}},@{Name='AdminGroup';Expression = {$myADGroup}} - # Iterate tests, a single failure constitues a failure for the unit - If ($myADAuth.Domain -ne $ADDomainName) {$ADFailedHosts += $myADHost } #Configured domain does not equal expected - ElseIf ($myADGroup -ne $ADAdminGroup) {$ADFailedHosts += $myADHost} #Configured Admin Group does not equal expected - ElseIf ($myADAuth.DomainMembershipStatus -ne "OK") {$ADFailedHosts += $myADHost} #Domain Memebership is in doubt - Else {$ADOKHosts += $myADHost} #Configuration passed all tests +ForEach ($ADHost in $VMH | Where-Object {$_.Connectionstate -eq "Connected"}) { + # Get authetication settings + $myADAuth = $ADHost | Get-VMHostAuthentication + # Get Admin Group settings + $myADGroup = ($ADHost | Get-AdvancedSetting -Name "Config.HostAgent.plugins.hostsvc.esxAdminsGroup").Value + # Build array item + $myADHost = $ADHost | Select-Object Name,@{Name='Domain';Expression = {$myADAuth.Domain}},@{Name='MembershipStatus';Expression = {$myADAuth.DomainMembershipStatus}},@{Name='AdminGroup';Expression = {$myADGroup}} + # Iterate tests, a single failure constitues a failure for the unit + If ($myADAuth.Domain -ne $ADDomainName) {$ADFailedHosts += $myADHost } #Configured domain does not equal expected + ElseIf ($myADGroup -ne $ADAdminGroup) {$ADFailedHosts += $myADHost} #Configured Admin Group does not equal expected + ElseIf ($myADAuth.DomainMembershipStatus -ne "OK") {$ADFailedHosts += $myADHost} #Domain Memebership is in doubt + Else {$ADOKHosts += $myADHost} #Configuration passed all tests } # If desired, add OK hosts to display after failed hosts @@ -31,10 +39,5 @@ If ($ADDisplayOK) {$ADFailedHosts += $ADOKHosts} # Provide output $ADFailedHosts -$PluginCategory = "vSphere" -$Title = "Active Directory Authentication" -$Header = "Active Directory Authentication" -$Comments = "Active Directory configuration and status for each host. (Domain: $ADDomainName, Admin Group: $ADAdminGroup, Display all results: $ADDisplayOK)" -$Display = "Table" -$Author = "Bill Wall" -$PluginVersion = 1.1 +# Changelog +## 1.2 : Only check Connected hosts since Disconnected and Not Responding produce empty data diff --git a/Plugins/30 Host/31 NTP Name and Service.ps1 b/Plugins/30 Host/31 NTP Name and Service.ps1 index 56f7d296..1e1493cb 100644 --- a/Plugins/30 Host/31 NTP Name and Service.ps1 +++ b/Plugins/30 Host/31 NTP Name and Service.ps1 @@ -2,13 +2,16 @@ $Title = "NTP Name and Service" $Header = "NTP Issues: [count]" $Comments = "The following hosts do not have the correct NTP settings and may cause issues if the time becomes far apart from the vCenter/Domain or other hosts" $Display = "Table" -$Author = "Alan Renouf" -$PluginVersion = 1.2 +$Author = "Alan Renouf, Dan Barr" +$PluginVersion = 1.3 $PluginCategory = "vSphere" -# Start of Settings +# Start of Settings # The NTP server which should be set on your hosts (comma-separated) $ntpserver = "pool.ntp.org,pool2.ntp.org" # End of Settings -$VMH | Where-Object {$_.Connectionstate -ne "Disconnected"} | Select-Object Name, @{N="NTPServer";E={($_ | Get-VMHostNtpServer) -join ","}}, @{N="ServiceRunning";E={(Get-VmHostService -VMHost $_ | Where-Object {$_.key -eq "ntpd"}).Running}} | Where-Object {$_.ServiceRunning -eq $false -or $_.NTPServer -ne $ntpserver} \ No newline at end of file +$VMH | Where-Object {$_.Connectionstate -eq "Connected"} | Select-Object Name, @{N="NTPServer";E={($_ | Get-VMHostNtpServer) -join ","}}, @{N="ServiceRunning";E={(Get-VmHostService -VMHost $_ | Where-Object {$_.key -eq "ntpd"}).Running}} | Where-Object {$_.ServiceRunning -eq $false -or $_.NTPServer -ne $ntpserver} + +# Changelog +## 1.3 : Only check Connected hosts since Disconnected and Not Responding produce empty data diff --git a/Plugins/30 Host/35 Host Configuration Issues.ps1 b/Plugins/30 Host/35 Host Configuration Issues.ps1 index 5d96bf47..fa7b4e97 100644 --- a/Plugins/30 Host/35 Host Configuration Issues.ps1 +++ b/Plugins/30 Host/35 Host Configuration Issues.ps1 @@ -2,24 +2,27 @@ $Title = "Host Configuration Issues" $Header = "Host(s) Config Issue(s): [count]" $Comments = "The following configuration issues have been registered against Hosts in vCenter" $Display = "Table" -$Author = "Alan Renouf" -$PluginVersion = 1.1 +$Author = "Alan Renouf, Dan Barr" +$PluginVersion = 1.2 $PluginCategory = "vSphere" -# Start of Settings -# End of Settings +# Start of Settings +# End of Settings $hostcialarms = @() -foreach ($HostsView in $HostsViews) { - if ($HostsView.ConfigIssue) { - $HostConfigIssues = $HostsView.ConfigIssue - Foreach ($HostConfigIssue in $HostConfigIssues) { - $Details = "" | Select-Object Name, Message - $Details.Name = $HostsView.name - $Details.Message = $HostConfigIssue.FullFormattedMessage - $hostcialarms += $Details - } - } +foreach ($HostsView in $HostsViews | Where-Object {$_.Summary.Runtime.ConnectionState -eq 'connected'}) { + if ($HostsView.ConfigIssue) { + $HostConfigIssues = $HostsView.ConfigIssue + Foreach ($HostConfigIssue in $HostConfigIssues) { + $Details = "" | Select-Object Name, Message + $Details.Name = $HostsView.name + $Details.Message = $HostConfigIssue.FullFormattedMessage + $hostcialarms += $Details + } + } } -$hostcialarms | Sort-Object name \ No newline at end of file +$hostcialarms | Sort-Object name + +# Changelog +## 1.2 : Only check Connected hosts since Disconnected and Not Responding hosts produce an error From 561712f19dc92b012be022512c94af3f5363201d Mon Sep 17 00:00:00 2001 From: Sneddo Date: Wed, 6 Dec 2017 14:40:40 +1100 Subject: [PATCH 22/29] Changes from PR #538 I'm not a smart man and working out Git was too hard :P --- Plugins/10 vCenter/82 License Report.ps1 | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Plugins/10 vCenter/82 License Report.ps1 b/Plugins/10 vCenter/82 License Report.ps1 index 7d6ac6ac..826162e1 100644 --- a/Plugins/10 vCenter/82 License Report.ps1 +++ b/Plugins/10 vCenter/82 License Report.ps1 @@ -1,23 +1,26 @@ -$Title = "vCenter License Report" -$Header = "License Report" -$Comments = "The following displays licenses registered with this server and usage." -$Display = "Table" -$Author = "Justin Mercier" -$PluginVersion = 1.1 -$PluginCategory = "vSphere" - # Start of Settings +# Display Eval licenses? +$licenseEvals = $true # End of Settings $vSphereLicInfo = @() Foreach ($LicenseMan in Get-View ($ServiceInstance | Select-Object -First 1).Content.LicenseManager) { - ($LicenseMan | Select-Object -ExpandProperty Licenses) | Select-Object @{Name="VC";e={([Uri]$LicenseMan.Client.ServiceUrl).Host}}, ` + ($LicenseMan | Select-Object -ExpandProperty Licenses) | Where {$licenseEvals -or $_.Name -notmatch "Evaluation" } | Select-Object @{Name="VC";e={([Uri]$LicenseMan.Client.ServiceUrl).Host}}, ` Name, LicenseKey, Total, Used, @{Name="Information";e={$_.Labels | Select-Object -ExpandProperty Value}}, ` - @{"Name"="ExpirationDate";e={$_.Properties | Where-Object { $_.key -eq "expirationDate" } | Select-Object -ExpandProperty Value}} + @{"Name"="ExpirationDate";e={$_.Properties | Where-Object { $_.key -eq "expirationDate" } | Select-Object -ExpandProperty Value}} } +$Title = "vCenter License Report" +$Header = "License Report" +$Comments = "The following displays licenses registered with this server and usage. Include Evals: $licenseEvals" +$Display = "Table" +$Author = "Justin Mercier, Bill Wall" +$PluginVersion = 1.2 +$PluginCategory = "vSphere" + # Changelog ## 1.0 : Initial Release -## 1.1 : Code refactor \ No newline at end of file +## 1.1 : Code refactor +## 1.2 : Added the ability to exclude evaluation licenses, clean up whitespace (@thebillness) From ecb2874e2e42f73fc53c69528fbc806a74b97ee4 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Fri, 8 Dec 2017 08:14:44 +1100 Subject: [PATCH 23/29] Fix variable in if statement As discussed on Slack, looks like there is a bug in the logic... --- Plugins/60 VM/32 VM CPU Percent RDY.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/60 VM/32 VM CPU Percent RDY.ps1 b/Plugins/60 VM/32 VM CPU Percent RDY.ps1 index 782f84d9..fe8f5fdb 100644 --- a/Plugins/60 VM/32 VM CPU Percent RDY.ps1 +++ b/Plugins/60 VM/32 VM CPU Percent RDY.ps1 @@ -29,7 +29,7 @@ ForEach ($v in ($VM | Where-Object {$_.PowerState -eq "PoweredOn"})){ For ($cpunum = 0; $cpunum -lt $v.NumCpu; $cpunum++){ $PercReady = [Math]::Round((($v | Get-Stat -ErrorAction SilentlyContinue -Stat Cpu.Ready.Summation -Realtime | Where-Object {$_.Instance -eq $cpunum} | Measure-Object -Property Value -Average).Average)/200,1) - if ($_.PercReady -gt $PercCPUReady) + if ($PercReady -gt $PercCPUReady) { New-Object -TypeName PSObject -Property @{ VM = $v.Name @@ -46,4 +46,4 @@ Write-Progress -ID 2 -Parent 1 -Activity $plang.pluginActivity -Status $lang.Com $Header = ("VM CPU % RDY over {0}: [count]" -f $PercCPUReady) # Change Log -## 1.2 : Added Get-vCheckSetting, code refactor \ No newline at end of file +## 1.2 : Added Get-vCheckSetting, code refactor From 4f106eb2b5d1443ee05033cd8bfbe6972a78fc13 Mon Sep 17 00:00:00 2001 From: Doug Taliaferro Date: Tue, 19 Dec 2017 12:31:19 -0500 Subject: [PATCH 24/29] Add host plugin to detect evaluation licenses --- Plugins/30 Host/130 Eval License Key.ps1 | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Plugins/30 Host/130 Eval License Key.ps1 diff --git a/Plugins/30 Host/130 Eval License Key.ps1 b/Plugins/30 Host/130 Eval License Key.ps1 new file mode 100644 index 00000000..f1f456f0 --- /dev/null +++ b/Plugins/30 Host/130 Eval License Key.ps1 @@ -0,0 +1,22 @@ +$Title = "Eval License Keys" +$Comments = "The following hosts are using an evaluation key that will expire, causing them to disconnect from vCenter." +$Display = "Table" +$Author = "Doug Taliaferro" +$PluginVersion = 1.0 +$PluginCategory = "vSphere" + +# Start of Settings +# End of Settings + +$hostResults = @() +foreach ($esxhost in ($VMH | where {$_.ConnectionState -match "Connected|Maintenance"})) { + if ($esxhost.LicenseKey -eq "00000-00000-00000-00000-00000") { + $myObj = "" | Select VMHost, LicenseKey + $myObj.VMHost = $esxhost.Name + $myObj.LicenseKey = $esxhost.LicenseKey + $hostResults += $myObj + } +} +$hostResults + +$Header = "Hosts using an Evaluation Key : [count]" \ No newline at end of file From 79578a36e357e0c350e8c782a3494b47ceea750b Mon Sep 17 00:00:00 2001 From: Doug Taliaferro Date: Tue, 19 Dec 2017 12:45:23 -0500 Subject: [PATCH 25/29] Add host plugin for NFS storage settings --- .../125 Host NFS Settings.ps1.disabled | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Plugins/30 Host/125 Host NFS Settings.ps1.disabled diff --git a/Plugins/30 Host/125 Host NFS Settings.ps1.disabled b/Plugins/30 Host/125 Host NFS Settings.ps1.disabled new file mode 100644 index 00000000..4f3201ba --- /dev/null +++ b/Plugins/30 Host/125 Host NFS Settings.ps1.disabled @@ -0,0 +1,74 @@ +$Title = "Host NFS Settings" +$Comments = "The following hosts may not have the recommended settings for NFS storage. See VMware KB 2239 and your storage vendor's documentation for the recommended settings." +$Display = "Table" +$Author = "Doug Taliaferro" +$PluginVersion = 1.0 +$PluginCategory = "vSphere" + +# Create an object to store the settings - the property names match the ESXi advanced setting name so we +# can use them to get each setting from the host and compare them +$myObj = "" | Select-Object Misc.APDTimeout, NFS.HeartbeatTimeout, NFS.SendBufferSize, NFS.ReceiveBufferSize, NFS.MaxVolumes, ` + Net.TcpipHeapMax, Net.TcpipHeapSize, NFS.HeartbeatFrequency, NFS.HeartbeatDelta, NFS.HeartbeatMaxFailures +# Start of Settings +# APD Timeout - default of 140 is usually recommended (consult with your storage vendor) +$myObj.'Misc.APDTimeout' = "140" +# NFS Heartbeat Timeout - default of 5 is usually recommended (consult with your storage vendor) +$myObj.'NFS.HeartbeatTimeout' = "5" +# NFS Send Buffer Size - default of 264 is usually recommended (consult with your storage vendor) +$myObj.'NFS.SendBufferSize' = "264" +# NFS Receive Buffer Size - default of 256 is usually recommended (consult with your storage vendor) +$myObj.'NFS.ReceiveBufferSize' = "256" +# NFS Max Volumes - ESXi host default is 8, but 256 is often recommended (see VMware KB 2239) +$myObj.'NFS.MaxVolumes' = "256" +# Net TCPIP Heap Max - for ESXi 6.x this can be increased to '1536' (see VMware KB 2239) +$myObj.'Net.TcpipHeapMax' = "512" +# Net TCPIP Heap Size - ESXi host default is 0, but 32 is often recommended (see VMware KB 2239) +$myObj.'Net.TcpipHeapSize' = "32" +# NFS Heartbeat Frequency - default of 12 is usually recommended (consult with your storage vendor) +$myObj.'NFS.HeartbeatFrequency' = "12" +# NFS Heartbeat Delta - default of 5 is usually recommended (consult with your storage vendor) +$myObj.'NFS.HeartbeatDelta' = "5" +# NFS Heartbeat Max Failures - default of 10 is usually recommended (consult with your storage vendor) +$myObj.'NFS.HeartbeatMaxFailures' = "10" +# End of Settings + +$hostResults = @() +Foreach ($esxhost in ($HostsViews | Where-Object {$_.Runtime.ConnectionState -match "Connected|Maintenance"})) { + + # Create an object to store the host settings + $hostObj = New-Object Object + # Iterate through each property in the custom object containing the settings + $myObj.psobject.Properties | % { + $PropertyName = $_.Name + $hostObj | Add-Member -MemberType NoteProperty -Name $PropertyName -Value ($esxhost.Config.Option | Where-Object { $_.Key -eq $PropertyName }).Value + } + + # Compare the two objects for differences + If ((Compare-Object ($hostObj | Out-String -Stream) ($myObj | Out-String -Stream)).count -gt 0) { + # If they don't match, create a final object that includes the host name + $finalObj = New-Object Object + $finalObj | Add-Member -MemberType NoteProperty -Name VMHost -Value $esxhost.Name + Foreach ( $Property in $hostObj.psobject.Properties){ + $finalObj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value + } + $hostResults += $finalObj + } +} + +# If there are results, create a header row to show the plugin settings in the report +if ($hostResults.Count -gt 0) { + $headObj = New-Object Object + $headObj | Add-Member -MemberType NoteProperty -Name VMHost -Value "Plugin Settings" + foreach ( $Property in $myObj.psobject.Properties){ + $headObj | Add-Member -MemberType NoteProperty -Name $Property.Name -Value $Property.value + } + $hostResults = ,$headObj + $hostResults +} +$hostResults + +$Header = "Host NFS settings that don't match : [count]" +# Table formatting - highlight cells without matching settings +$TableFormat = @{} +foreach ($Property in $myObj.psObject.Properties) { + $TableFormat += @{"$($Property.Name)" = @(@{"-ne $($Property.Value)" = "Cell,class|critical"; })} +} From eb666cd7194fb0411b073352bb10ef54dc8b7187 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Thu, 21 Dec 2017 07:19:48 +1100 Subject: [PATCH 26/29] Fix Issue #609 --- Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 b/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 index 53d724c1..925ced78 100644 --- a/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 +++ b/Plugins/60 VM/79 Find VMs in Uncontrolled Snapshot Mode.ps1 @@ -24,7 +24,7 @@ $PluginCategory = "vSphere" $i=0; foreach ($eachDS in ($Datastores | Where-Object {$_.State -eq "Available"})) { Write-Progress -ID 2 -Parent 1 -Activity $pLang.pluginActivity -Status ($pLang.pluginStatus -f $i, $Datastores.count, $eachDS.Name) -PercentComplete ($i*100/$Datastores.count) - $eachDS.Name + $FilePath = $eachDS.DatastoreBrowserPath + '\*\*delta.vmdk*' $fileList = @(Get-ChildItem -Path "$FilePath" | Select-Object Name, FolderPath, FullName) $FilePath = $eachDS.DatastoreBrowserPath + '\*\-*-flat.vmdk' @@ -48,4 +48,4 @@ foreach ($eachDS in ($Datastores | Where-Object {$_.State -eq "Available"})) { } } } -Write-Progress -ID 1 -Activity $pLang.pluginActivity -Status $pLang.Complete -Completed \ No newline at end of file +Write-Progress -ID 1 -Activity $pLang.pluginActivity -Status $pLang.Complete -Completed From 7a270f2e5aaf1d7812cba15f698df05de7effad0 Mon Sep 17 00:00:00 2001 From: Greg Harms Date: Wed, 20 Dec 2017 15:14:21 -0600 Subject: [PATCH 27/29] Select-Object now returns Folder as a string; Added IgnoredVMpath --- Plugins/60 VM/60 Powered Off VMs.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Plugins/60 VM/60 Powered Off VMs.ps1 b/Plugins/60 VM/60 Powered Off VMs.ps1 index 66922448..5f7283d7 100644 --- a/Plugins/60 VM/60 Powered Off VMs.ps1 +++ b/Plugins/60 VM/60 Powered Off VMs.ps1 @@ -2,28 +2,33 @@ $Title = "Powered Off VMs" $Header = "VMs Powered Off - Number of Days" $Display = "Table" $Author = "Adam Schwartzberg" -$PluginVersion = 1.4 +$PluginVersion = 1.5 $PluginCategory = "vSphere" # Start of Settings # VMs not to report on $IgnoredVMs = "Windows7*" +#VmPathName not to report on +$IgnoredVMpath = "-backup-" # Report VMs powered off over this many days $PoweredOffDays = 7 # End of Settings # Update settings where there is an override $IgnoredVMs = Get-vCheckSetting $Title "IgnoredVMs" $IgnoredVMs +$IgnoredVMpath = Get-vCheckSetting $Title "IgnoredVMpath" $IgnoredVMpath $PoweredOffDays = Get-vCheckSetting $Title "PoweredOffDays" $PoweredOffDays $VM | Where-Object {$_.ExtensionData.Config.ManagedBy.ExtensionKey -ne 'com.vmware.vcDr' -and $_.PowerState -eq "PoweredOff" -and $_.LastPoweredOffDate -lt $date.AddDays(-$PoweredOffDays) -and - $_.Name -notmatch $IgnoredVMs} | - Select-Object -Property Name, LastPoweredOffDate, Folder, Notes | + $_.Name -notmatch $IgnoredVMs -and + $_.ExtensionData.Config.Files.VmPathName -notmatch $IgnoredVMpath} | + Select-Object -Property Name, LastPoweredOffDate, @{l = 'Folder'; e = {$_.Folder.Name}}, Notes | Sort-Object -Property LastPoweredOffDate $Comments = ("May want to consider deleting VMs that have been powered off for more than {0} days" -f $PoweredOffDays) # Change Log -## 1.4 : Added Get-vCheckSetting, $PoweredOffDays \ No newline at end of file +## 1.4 : Added Get-vCheckSetting, $PoweredOffDays +## 1.5 : Select-Object now returns Folder as a string; Added IgnoredVMpath \ No newline at end of file From 5b67b2245fce0481c33f248034c06e511090c335 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Fri, 22 Dec 2017 10:24:42 +1100 Subject: [PATCH 28/29] Simplify code --- Plugins/30 Host/130 Eval License Key.ps1 | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Plugins/30 Host/130 Eval License Key.ps1 b/Plugins/30 Host/130 Eval License Key.ps1 index f1f456f0..f1b8d4e0 100644 --- a/Plugins/30 Host/130 Eval License Key.ps1 +++ b/Plugins/30 Host/130 Eval License Key.ps1 @@ -1,4 +1,5 @@ $Title = "Eval License Keys" +$Header = "Hosts using an Evaluation Key : [count]" $Comments = "The following hosts are using an evaluation key that will expire, causing them to disconnect from vCenter." $Display = "Table" $Author = "Doug Taliaferro" @@ -8,15 +9,4 @@ $PluginCategory = "vSphere" # Start of Settings # End of Settings -$hostResults = @() -foreach ($esxhost in ($VMH | where {$_.ConnectionState -match "Connected|Maintenance"})) { - if ($esxhost.LicenseKey -eq "00000-00000-00000-00000-00000") { - $myObj = "" | Select VMHost, LicenseKey - $myObj.VMHost = $esxhost.Name - $myObj.LicenseKey = $esxhost.LicenseKey - $hostResults += $myObj - } -} -$hostResults - -$Header = "Hosts using an Evaluation Key : [count]" \ No newline at end of file +$VMH | where-object {($_.ConnectionState -match "Connected|Maintenance") -and ($_.LicenseKey -eq "00000-00000-00000-00000-00000")} | Select Name, LicenseKey From e8e1ca3d14176e41b86fdca5e7b67559398795c4 Mon Sep 17 00:00:00 2001 From: Sneddo Date: Wed, 27 Dec 2017 07:49:56 +1100 Subject: [PATCH 29/29] Version Bump --- vCheck.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vCheck.ps1 b/vCheck.ps1 index 1fb6ed84..02f6a47a 100644 --- a/vCheck.ps1 +++ b/vCheck.ps1 @@ -30,7 +30,7 @@ .NOTES File Name : vCheck.ps1 Author : Alan Renouf - @alanrenouf - Version : 6.24 + Version : 6.25 Thanks to all who have commented on my blog to help improve this project all beta testers and previous contributors to this script. @@ -68,7 +68,7 @@ param ( [string]$job ) -$vCheckVersion = "6.24" +$vCheckVersion = "6.25" $Date = Get-Date # Setup all paths required for script to run