# vCheck - Daily Error Report # # Changes: # # Hypervisor.fr additions : # # Only NTP service check (not NTP server list) # Hosts Ballooning # Datastore OverAllocation # Snapshot Oversize # VCB garbage # VMkernel warning check for ESX/ESXi (with deduplication) # VM inaccessible or invalid # Snapshot add/remove summary # Time build # # Version 3.0 - Added VMs in mis-matched Folder names # Version 2.9 - Added counts to each titlebar and output to screen whilst running for interactive mode # Version 2.8 - Changed VC Services to show only unexpected status # Version 2.7 - Added VMs with outdated Hardware - vSphere Only # Version 2.6 - Added Slot size check - vSphere Only # version 2.5 - Added report on Hosts in a HA cluster where the swapfile location is set, check the hosts - Thanks Raphaël SCHITZ (http://www.hypervisor.fr/) # Version 2.4 - Added VM/Host/Cluster Alerts - Thanks Raphaël SCHITZ (http://www.hypervisor.fr/) # Version 2.3 - Added VMs with over x amount of vCPUs # Version 2.2 - Added Dead SCSILuns - Thanks Raphaël SCHITZ (http://www.hypervisor.fr/) # Version 2.1 - Now checks for VMs stored on storage available to only one host rather than local storage # Version 2.0 - CPU Ready # Version 1.17 - vmkernel host log file check for warnings # Version 1.16 - NTP Server and service check # Version 1.15 - DRSMigrations & Local Stored VMs # Version 1.14 - Active/Inactive VMs # Version 1.13 - Bug Fixes # Version 1.12 - Added Hosts in Maintenance Mode and not responding + Bug Fixes # Version 1.11 - Simplified mail function. # Version 1.10 - Added How many days old the snapshots are # Version 1.9 - Added ability to change user account which makes the WMI calls # Version 1.8 - Added Real name resolution via AD and sorted disk space by PerfFree # Version 1.7 - Added Event Logs for VMware warnings and errors for past day # Version 1.6 - Add details to service state to see if it is expected or not # Version 1.5 - Check for objects to see if they exist before sending the email + add VMs with No VMTools param( [string] $VISRV) $starttime = Get-Date # You can change the following defaults by altering the below settings: # # Set the SMTP Server address $SMTPSRV = "" # Set the Email address to recieve from $EmailFrom = "" # Set the Email address to send the email to $EmailTo = "" #### Detail Settings #### # Set the username of the account with permissions to access the VI Server # for event logs and service details - you will be asked for the same username and password # only the first time this runs after setting the below username. # If it is left blank it will use the credentials of the user who runs the script $SetUsername = "" # Set the location to store the credentials in a secure manner $CredFile = ".\mycred.crd" # Set the warning threshold for Datastore % Free Space $DatastoreSpace = "5" # Set the warning threshold for snapshots in days old $SnapshotAge = 14 # Set the number of days to show VMs created & removed for (also used for made/revomed snapshot) $VMsNewRemovedAge = 2 # Set the number of days of VC Events to check for errors $VCEventAge = 1 # Set the number of days of VC Event Logs to check for warnings and errors $VCEvntlgAge = 1 # Set the number of days of DRS Migrations to report and count on $DRSMigrateAge = 1 # Local Stored VMs, do not report on any VMs who are defined below $LVMDoNotInclude = "Template_*|Local_*" # The NTP server to check $ntpserver = "ntp.MyDomain.com" # vmkernel log file checks - set the number of days to check before today $vmkernelchk = 1 # CPU ready on VMs - To learn more read here: http://communities.vmware.com/docs/DOC-7390 $PercCPUReady = 10.0 # Change the next line to the maximum amount of vCPUs your VMs are allowed $vCpu = 2 # Number of slots available in a cluster $numslots = 10 # Ballooning days $BalloonHostsAge = 1 # Datastore OverAllocation % $OverAllocation = 100 # Full vmkernel log file checks - set the number of days to check before today # You should not exceed 1 day since ESXi logs are rotated very often $FullVMKernelchk = 1 # Check inaccessible or invalid VM $ShowBlindedVM = 1 # This section can be used to turn off certain areas of the report which may not be relevent to your installation # Set them to $False if you do not want them in your output. # # General Summary Info $ShowGenSum = $true # Snapshot Information $ShowSnap = $false # Datastore Information $Showdata = $true # Hosts in Maintenance mode $ShowMaint = $true # Hosts not responding or Disconnected $ShowResDis = $true # Dead LunPath $ShowLunPath = $true # VMs Created or cloned $ShowCreated = $true # VMs vCPU $Showvcpu = $false # VMs Removed $ShowRemoved = $true # Host Swapfile datastores $ShowSwapFile = $true # DRS Migrations $ShowDRSMig = $true # Cluster Slot Sizes $ShowSlot = $true # VM Hardware Version $ShowHWVer = $false # VI Events $ShowVIevents = $true # VMs in inconsistent folders $ShowFolders = $true # VM Tools $Showtools = $true # Connected CDRoms $ShowCDRom = $true # ConnectedFloppy Drives $ShowFloppy = $true # NTP Issues $ShowNTP = $true # Single storage VMs $ShowSingle = $false # VM CPU Ready $ShowCPURDY = $true # Host Alarms $ShowHostAlarm = $true # VM Alarms $ShowVMAlarm = $true # Cluster Alarms $ShowCLUAlarm = $true # VC Service Details $ShowVCDetails = $true # VC Event Log Errors $ShowVCError = $true # VC Event Log Warnings $ShowVCWarn = $true # VMKernel Warning entries $ShowVMKernel = $false # Hypervisor.fr additions : # Host Ballooning $ShowHostBallooning = $true # Datastore OverAllocation $ShowOverAllocation = $true # VCB garbage $ShowVCBgarbage = $true # Snapshot Oversize $ShowOversize = $true # VMKernel Warnings for ESX/ESXi $ShowFullVMKernel = $true # Snapshot add/remove summary $ShowSnapSum = $true # NTP service running only check $showNTPservieOnly = $true ####################################### # Start of script if ($VISRV -eq ""){ Write-Host Write-Host "Please specify a VI Server name eg...." Write-Host " powershell.exe DailyReport.ps1 MYVISERVER" Write-Host Write-Host exit } function Write-CustomOut ($Details){ $LogDate = Get-Date Write-Host "$($LogDate.TimeofDay.Hours):$($LogDate.TimeofDay.Minutes) $Details" } function Send-SMTPmail($to, $from, $subject, $smtpserver, $body) { $mailer = new-object Net.Mail.SMTPclient($smtpserver) $msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body) $msg.IsBodyHTML = $true $mailer.send($msg) } Function Get-CustomHTML ($Header){ $Report = @" $($Header) $($Header)
Generated on $($ENV:Computername) - Visit http://virtu-al.net for more great scripts !
Report created on $(Get-Date)
"@ Return $Report } Function Get-CustomHeader0 ($Title){ $Report = @"

$($Title)

"@ Return $Report } Function Get-CustomHeader ($Num, $Title){ $Report = @"

$($Title)

"@ Return $Report } Function Get-CustomHeaderClose{ $Report = @"
"@ Return $Report } Function Get-CustomHeader0Close{ $Report = @"
"@ Return $Report } Function Get-CustomHTMLClose{ $Report = @" "@ Return $Report } Function Get-HTMLTable { param([array]$Content) $HTMLTable = $Content | ConvertTo-Html $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace 'HTML TABLE', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" $HTMLTable = $HTMLTable -replace '', "" Return $HTMLTable } Function Get-HTMLDetail ($Heading, $Detail){ $Report = @"
$Heading $($Detail)
"@ Return $Report } function Find-Username ($username){ if ($username -ne $null) { $root = [ADSI]"" $filter = ("(&(objectCategory=user)(samAccountName=$Username))") $ds = new-object system.DirectoryServices.DirectorySearcher($root,$filter) $ds.PageSize = 1000 $ds.FindOne() } } function Get-VIServices { If ($SetUsername -ne ""){ $Services = get-wmiobject win32_service -Credential $creds -ComputerName $VISRV | Where {$_.DisplayName -like "VMware*" } } Else { $Services = get-wmiobject win32_service -ComputerName $VISRV | Where {$_.DisplayName -like "VMware*" } } $myCol = @() Foreach ($service in $Services){ $MyDetails = "" | select-Object Name, State, StartMode, Health If ($service.StartMode -eq "Auto") { if ($service.State -eq "Stopped") { $MyDetails.Name = $service.Displayname $MyDetails.State = $service.State $MyDetails.StartMode = $service.StartMode $MyDetails.Health = "Unexpected State" } } If ($service.StartMode -eq "Auto") { if ($service.State -eq "Running") { $MyDetails.Name = $service.Displayname $MyDetails.State = $service.State $MyDetails.StartMode = $service.StartMode $MyDetails.Health = "OK" } } If ($service.StartMode -eq "Disabled") { If ($service.State -eq "Running") { $MyDetails.Name = $service.Displayname $MyDetails.State = $service.State $MyDetails.StartMode = $service.StartMode $MyDetails.Health = "Unexpected State" } } If ($service.StartMode -eq "Disabled") { if ($service.State -eq "Stopped") { $MyDetails.Name = $service.Displayname $MyDetails.State = $service.State $MyDetails.StartMode = $service.StartMode $MyDetails.Health = "OK" } } $myCol += $MyDetails } Write-Output $myCol } function Get-DatastoreSummary { param( $InputObject = $null ) process { if ($InputObject -and $_) { throw 'The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.' return } $processObject = $(if ($InputObject) {$InputObject} else {$_}) if ($processObject) { $myCol = @() foreach ($ds in $_) { $MyDetails = "" | select-Object Name, Type, CapacityMB, FreeSpaceMB, PercFreeSpace $MyDetails.Name = $ds.Name $MyDetails.Type = $ds.Type $MyDetails.CapacityMB = $ds.CapacityMB $MyDetails.FreeSpaceMB = $ds.FreeSpaceMB $MyDetails.PercFreeSpace = [math]::Round(((100 * ($ds.FreeSpaceMB)) / ($ds.CapacityMB)),0) $myCol += $MyDetails } $myCol | Where { $_.PercFreeSpace -lt $DatastoreSpace } } } end { } } function Get-SnapshotSummary { param( $InputObject = $null ) PROCESS { if ($InputObject -and $_) { throw 'ParameterBinderStrings\AmbiguousParameterSet' break } elseif ($InputObject) { $InputObject } elseif ($_) { $mySnaps = @() foreach ($snap in $_){ $SnapshotInfo = Get-SnapshotExtra $snap $mySnaps += $SnapshotInfo } $mySnaps | Select VM, Name, @{N="DaysOld";E={((Get-Date) - $_.Created).Days}}, @{N="Creator";E={(Find-Username (($_.Creator.split("\"))[1])).Properties.displayname}}, Created, Description -ErrorAction SilentlyContinue | Sort DaysOld } else { throw 'ParameterBinderStrings\InputObjectNotBound' } } } function Get-SnapshotTree{ param($tree, $target) $found = $null foreach($elem in $tree){ if($elem.Snapshot.Value -eq $target.Value){ $found = $elem continue } } if($found -eq $null -and $elem.ChildSnapshotList -ne $null){ $found = Get-SnapshotTree $elem.ChildSnapshotList $target } return $found } function Get-SnapshotExtra ($snap){ $guestName = $snap.VM # The name of the guest $tasknumber = 999 # Windowsize of the Task collector $taskMgr = Get-View TaskManager # Create hash table. Each entry is a create snapshot task $report = @{} $filter = New-Object VMware.Vim.TaskFilterSpec $filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime $filter.Time.beginTime = (($snap.Created).AddDays(-5)) $filter.Time.timeType = "startedTime" $collectionImpl = Get-View ($taskMgr.CreateCollectorForTasks($filter)) $dummy = $collectionImpl.RewindCollector $collection = $collectionImpl.ReadNextTasks($tasknumber) while($collection -ne $null){ $collection | where {$_.DescriptionId -eq "VirtualMachine.createSnapshot" -and $_.State -eq "success" -and $_.EntityName -eq $guestName} | %{ $row = New-Object PsObject $row | Add-Member -MemberType NoteProperty -Name User -Value $_.Reason.UserName $vm = Get-View $_.Entity if($vm -ne $null){ $snapshot = Get-SnapshotTree $vm.Snapshot.RootSnapshotList $_.Result if($snapshot -ne $null){ $key = $_.EntityName + "&" + ($snapshot.CreateTime.ToString()) $report[$key] = $row } } } $collection = $collectionImpl.ReadNextTasks($tasknumber) } $collectionImpl.DestroyCollector() # Get the guest's snapshots and add the user $snapshotsExtra = $snap | % { $key = $_.vm.Name + "&" + ($_.Created.ToString()) if($report.ContainsKey($key)){ $_ | Add-Member -MemberType NoteProperty -Name Creator -Value $report[$key].User } $_ } $snapshotsExtra } Function Set-Cred ($File) { $Credential = Get-Credential $credential.Password | ConvertFrom-SecureString | Set-Content $File } Function Get-Cred ($User,$File) { $password = Get-Content $File | ConvertTo-SecureString $credential = New-Object System.Management.Automation.PsCredential($user,$password) $credential } function Get-UnShareableDatastore { $Report = @() Foreach ($datastore in (Get-Datastore)){ If (($datastore | get-view).summary.multiplehostaccess -eq $false){ ForEach ($VM in (get-vm -datastore $Datastore )){ $SAHost = "" | Select VM, Datastore $SAHost.VM = $VM.Name $SAHost.Datastore = $Datastore.Name $Report += $SAHost } } } $Report } function Get-VmSize($vmname) # modded version from Arnim van Lieshout http://www.van-lieshout.com/2009/07/how-big-is-my-vm/ { #Initialize variables $VmDirs =@() $VmSize = 0 $fileQueryFlags = New-Object VMware.Vim.FileQueryFlags $fileQueryFlags.FileSize = $true #$fileQueryFlags.FileType = $true #$fileQueryFlags.Modification = $true $searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec $searchSpec.details = $fileQueryFlags Get-View -ViewType VirtualMachine -Filter @{"Name" = $vmname } | % { #Create an array with the vm's directories #$VmDirs += $_.Config.Files.VmPathName.split("/")[0] $VmDirs += $_.Config.Files.SnapshotDirectory.split("/")[0] #$VmDirs += $_.Config.Files.SuspendDirectory.split("/")[0] #$VmDirs += $_.Config.Files.LogDirectory.split("/")[0] #Add directories of the vm's virtual disk files foreach ($disk in $_.Layout.Disk) { foreach ($diskfile in $disk.diskfile){ $VmDirs += $diskfile.split("/")[0] } } #Only take unique array items $VmDirs = $VmDirs | Sort | Get-Unique foreach ($dir in $VmDirs){ $ds = Get-Datastore ($dir.split("[")[1]).split("]")[0] $dsb = Get-View (($ds | get-view).Browser) $searchSpec = [VMware.Vim.VIConvert]::ToVim4($searchSpec) $searchSpec.details.fileOwnerSpecified = $true $dsBrowserMoRef = [VMware.Vim.VIConvert]::ToVim4($dsb.MoRef); $taskMoRef = $dsb.Client.VimService.SearchDatastoreSubFolders_Task($dsBrowserMoRef, $dir, $searchSpec) $task = [VMware.Vim.VIConvert]::ToVim($dsb.WaitForTask([VMware.Vim.VIConvert]::ToVim($taskMoRef))) foreach ($result in $task){ foreach ($file in $result.File){ $VmSize += $file.FileSize } } } } return $VmSize } function Get-hypersnapshot($VMViews) { $snapp = @() ForEach ($VMView in $VMViews) { if ($VMView.Snapshot) { $vmg = Get-VM -Name $VMView.name $vmname = $VMView.name $hddsize = 0 $snapcount = 0 ForEach ($DISK in $vmg.HardDisks) # Loop through VM's harddisks { $hddsize = $hddsize+[math]::round($DISK.CapacityKB/1048576, 0) } $snaps = $vmg | Get-Snapshot foreach ($snap in $snaps) { $snapcount++ } $totalsize = Get-VmSize($vmname) $totalsize = [math]::round($totalsize/1073741824,0) $overab = $snappObj = "" | Select VM,vmdkSize,RealSize,SnapCount,OverSize $snappObj.VM = $vmname $snappObj.vmdkSize = "$hddsize GB" $snappObj.RealSize = "$totalsize GB" $snappObj.SnapCount = $snapcount if ($hddsize -eq 0) {$snappObj.OverSize = "Linked Clone"} else { $oversize = [math]::round((($totalsize*100)/$hddsize), 0) $snappObj.OverSize = "$oversize%" } $snapp += $snappObj } } return $snapp } If ($SetUsername -ne ""){ if ((Test-Path -Path $CredFile) -eq $false) { Set-Cred $CredFile } $creds = Get-Cred $SetUsername $CredFile } Write-CustomOut "Connecting to VI Server" $VIServer = Connect-VIServer $VISRV If ($VIServer.IsConnected -ne $true){ # Fix for scheduled tasks not running. $USER = $env:username $APPPATH = "C:\Documents and Settings\" + $USER + "\Application Data" #SET THE APPDATA ENVIRONMENT WHEN NEEDED if ($env:appdata -eq $null -or $env:appdata -eq 0) { $env:appdata = $APPPATH } $VIServer = Connect-VIServer $VISRV If ($VIServer.IsConnected -ne $true){ send-SMTPmail -to $EmailTo -from $EmailFrom -subject "ERROR: $VISRV Daily Report" -smtpserver $SMTPSRV -body "The Connect-VISERVER Cmdlet did not work, please check you VI Server." exit } } # Find out which version of the VI we are connecting to If ((Get-View ServiceInstance).Content.About.Version -ge "4.0.0"){ $VIVersion = 4 } Else{ $VIVersion = 3 } Write-CustomOut "Collecting VM Objects" $VM = Get-VM | Sort Name Write-CustomOut "Collecting VM Host Objects" $VMH = Get-VMHost | Sort Name Write-CustomOut "Collecting Cluster Objects" $Clusters = Get-Cluster | Sort Name Write-CustomOut "Collecting Datastore Objects" $Datastores = Get-Datastore | Sort Name Write-CustomOut "Collecting Detailed VM Objects" $FullVM = Get-View -ViewType VirtualMachine | Where {-not $_.Config.Template} $VMViews = $FullVM Write-CustomOut "Collecting Template Objects" $VMTmpl = Get-Template Write-CustomOut "Collecting Detailed VI Objects" $serviceInstance = get-view ServiceInstance Write-CustomOut "Collecting Detailed Alarm Objects" $alarmMgr = get-view $serviceInstance.Content.alarmManager Write-CustomOut "Collecting Detailed VMHost Objects" $HostsViews = Get-View -ViewType hostsystem # Check for vSphere If ($serviceInstance.Client.ServiceContent.About.Version -ge 4){ $vSphere = $true } $MyReport = Get-CustomHTML "$VIServer vCheck" $MyReport += Get-CustomHeader0 ($VIServer.Name) # ---- General Summary Info ---- If ($ShowGenSum){ Write-CustomOut "..Adding General Summary Info to the report" $MyReport += Get-CustomHeader "1" "General Details" $MyReport += Get-HTMLDetail "Number of Hosts:" (@($VMH).Count) $MyReport += Get-HTMLDetail "Number of VMs:" (@($VM).Count) $MyReport += Get-HTMLDetail "Number of Templates:" (@($VMTmpl).Count) $MyReport += Get-HTMLDetail "Number of Clusters:" (@($Clusters).Count) $MyReport += Get-HTMLDetail "Number of Datastores:" (@($Datastores).Count) $MyReport += Get-HTMLDetail "Active VMs:" (@($FullVM | Where { $_.Runtime.PowerState -eq "poweredOn" }).Count) $MyReport += Get-HTMLDetail "In-active VMs:" (@($FullVM | Where { $_.Runtime.PowerState -eq "poweredOff" }).Count) $MyReport += Get-HTMLDetail "DRS Migrations for last $($DRSMigrateAge) Days:" @(Get-VIEvent -maxsamples 10000 -Start (Get-Date).AddDays(-$DRSMigrateAge ) | where {$_.Gettype().Name -eq "DrsVmMigratedEvent"}).Count $MyReport += Get-CustomHeaderClose } # ---- Snapshot Information ---- If ($ShowSnap){ Write-CustomOut "..Checking Snapshots" $Snapshots = @($VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-$SnapshotAge))} | Get-SnapshotSummary) If (($Snapshots | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Snapshots (Over $SnapshotAge Days Old) : $($snapshots.count)" $MyReport += Get-HTMLTable $Snapshots $MyReport += Get-CustomHeaderClose } } # ---- Datastore Information ---- If ($Showdata){ Write-CustomOut "..Checking Datastores" $OutputDatastores = @($Datastores | Get-DatastoreSummary | Sort PercFreeSpace) If (($OutputDatastores | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Datastores (Less than $DatastoreSpace% Free) : $($OutputDatastores.count)" $MyReport += Get-HTMLTable $OutputDatastores $MyReport += Get-CustomHeaderClose } } # ---- Hosts in Maintenance Mode ---- If ($ShowMaint){ Write-CustomOut "..Checking Hosts in Maintenance Mode" $MaintHosts = @($VMH | where {$_.State -match "Maintenance"} | Select name) If (($MaintHosts | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Hosts in Maintenance Mode : $($MaintHosts.count)" $MyReport += Get-HTMLTable $MaintHosts $MyReport += Get-CustomHeaderClose } } # ---- Hosts Not responding or Disconnected ---- If ($ShowResDis){ Write-CustomOut "..Checking Hosts Not responding or Disconnected" $RespondHosts = @($VMH | where {$_.State -match "NotResponding" -or $_.State -match "Disconnected"} | get-view | Select name, @{N="Connection State";E={$_.Runtime.ConnectionState}}, @{N="Power State";E={$_.Runtime.PowerState}}) If (($RespondHosts | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Hosts not responding or disconnected : $($RespondHosts.count)" $MyReport += Get-HTMLTable $RespondHosts $MyReport += Get-CustomHeaderClose } } # ---- Dead LunPath ---- If ($ShowLunPath){ Write-CustomOut "..Checking Hosts Dead Lun Path" $deadluns = @() foreach ($esxhost in $VMH) { if ($esxhost.state -eq "Connected") { $esxluns = Get-ScsiLun -vmhost $esxhost |Get-ScsiLunPath foreach ($esxlun in $esxluns){ if ($esxlun.state -eq "Dead") { $myObj = "" | Select VMHost, Lunpath, State $myObj.VMHost = $esxhost $myObj.Lunpath = $esxlun.Lunpath $myObj.State = $esxlun.state $deadluns += $myObj } } } } If (($deadluns | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Dead LunPath : $($deadluns.count)" $MyReport += Get-HTMLTable $deadluns $MyReport += Get-CustomHeaderClose } } # ---- Get VIEvent ---- Write-CustomOut "..Checking VIEvent" $VIEvent = Get-VIEvent -maxsamples 100000 -Start (Get-Date).AddDays(-$VMsNewRemovedAge) # ---- VMs created or Cloned ---- If ($ShowCreated){ Write-CustomOut "..Checking for created or cloned VMs" $OutputCreatedVMs = @($VIEvent | where {$_.Gettype().Name -eq "VmCreatedEvent" -or $_.Gettype().Name -eq "VmBeingClonedEvent" -or $_.Gettype().Name -eq "VmBeingDeployedEvent"} | Select createdTime, @{N="User";E={(Find-Username (($_.userName.split("\"))[1])).Properties.displayname}}, fullFormattedMessage) If (($OutputCreatedVMs | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs Created or Cloned (Last $VMsNewRemovedAge Day(s)) : $($OutputCreatedVMs.count)" $MyReport += Get-HTMLTable $OutputCreatedVMs $MyReport += Get-CustomHeaderClose } } # ---- VMs Removed ---- If ($ShowRemoved){ Write-CustomOut "..Checking for removed VMs" $OutputRemovedVMs = @($VIEvent | where {$_.Gettype().Name -eq "VmRemovedEvent"}| Select createdTime, @{N="User";E={(Find-Username (($_.userName.split("\"))[1])).Properties.displayname}}, fullFormattedMessage) If (($OutputRemovedVMs | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs Removed (Last $VMsNewRemovedAge Day(s)) : $($OutputRemovedVMs.count)" $MyReport += Get-HTMLTable $OutputRemovedVMs $MyReport += Get-CustomHeaderClose } } # ---- VMs vCPU ---- If ($Showvcpu){ Write-CustomOut "..Checking for VMs with over $vCPU vCPUs" $OverCPU = @($VM | Where {$_.NumCPU -gt $vCPU} | Select Name, PowerState, NumCPU) If (($OverCPU | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs with over $vCPU vCPUs : $($OverCPU.count)" $MyReport += Get-HTMLTable $OverCPU $MyReport += Get-CustomHeaderClose } } # ---- VMSwapfileDatastore not set---- If ($Showswapfile){ Write-CustomOut "..Checking Host Swapfile datastores" $cluswap = @() foreach ($clusview in $clusviews) { if ($clusview.ConfigurationEx.VmSwapPlacement -eq "hostLocal") { $CluNodesViews = Get-VMHost -Location $clusview.name |Get-View foreach ($CluNodesView in $CluNodesViews) { if ($CluNodesView.Config.LocalSwapDatastore.Value -eq $null) { $Details = "" | Select-Object Cluster, Host, Message $Details.cluster = $clusview.name $Details.host = $CluNodesView.name $Details.Message = "Swapfile location NOT SET" $cluswap += $Details } } } } If (($cluswap | Measure-Object).count -gt 0) { $cluswap = $cluswap | sort name $MyReport += Get-CustomHeader "1" "VMSwapfileDatastore(s) not set : $($cluswap.count)" $MyReport += Get-HTMLTable $cluswap $MyReport += Get-CustomHeaderClose } } # ---- DRS Migrations ---- If ($ShowDRSMig){ Write-CustomOut "..Checking DRS Migrations" $DRSMigrations = @(Get-VIEvent -maxsamples 100000 -Start (Get-Date).AddDays(-$DRSMigrateAge ) | where {$_.Gettype().Name -eq "DrsVmMigratedEvent"} | select createdTime, fullFormattedMessage) If (($DRSMigrations | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "DRS Migrations (Last $DRSMigrateAge Day(s)) : $($DRSMigrations.count)" $MyReport += Get-HTMLTable $DRSMigrations $MyReport += Get-CustomHeaderClose } } # --- Cluster Slot Sizes --- If ($Showslot){ If ($vSphere -eq $true){ Write-CustomOut "..Checking Cluster Slot Sizes" $SlotInfo = @() Foreach ($Cluster in ($Clusters| Get-View)){ If ($Cluster.Configuration.DasConfig.Enabled -eq $true){ $SlotDetails = $Cluster.RetrieveDasAdvancedRuntimeInfo() $Details = "" | Select Cluster, TotalSlots, UsedSlots, AvailableSlots $Details.Cluster = $Cluster.Name $Details.TotalSlots = $SlotDetails.TotalSlots $Details.UsedSlots = $SlotDetails.UsedSlots $Details.AvailableSlots = $SlotDetails.TotalSlots - $SlotDetails.UsedSlots $SlotInfo += $Details } } $SlotCHK = @($SlotInfo | Where { $_.AvailableSlots -lt $numslots}) If (($SlotCHK | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Clusters with less than $numslots Slot Sizes : $($SlotCHK.count)" $MyReport += Get-HTMLTable $SlotCHK $MyReport += Get-CustomHeaderClose } } } # ---- VM Hardware Version ---- If ($ShowHWVer){ If ($vSphere -eq $true){ Write-CustomOut "..Checking VM Hardware Version" $HV = @($FullVM | Select Name, @{N="HardwareVersion";E={"Version $($_.Config.Version[5])"}} | Where {$_.HardwareVersion -ne "Version 7"}) If (($HV | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs with old hardware : $($HV.count)" $MyReport += Get-HTMLTable $HV $MyReport += Get-CustomHeaderClose } } } # ---- VC Errors ---- If ($ShowVIEvents){ Write-CustomOut "..Checking VI Events" $OutputErrors = @(Get-VIEvent -maxsamples 10000 -Start (Get-Date).AddDays(-$VCEventAge ) -Type Error | Select @{N="Host";E={$_.host.name}}, createdTime, @{N="User";E={(Find-Username (($_.userName.split("\"))[1])).Properties.displayname}}, fullFormattedMessage) If (($OutputErrors | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Error Events (Last $VCEventAge Day(s)) : $($OutputErrors.count)" $MyReport += Get-HTMLTable $OutputErrors $MyReport += Get-CustomHeaderClose } } # ---- VMs in inconsistent folders ---- If ($Showfolders){ Write-CustomOut "..Checking VMs in Inconsistent folders" $VMFolder = @() Foreach ($CHKVM in $FullVM){ $Details = "" |Select-Object VM,Path $Folder = ((($CHKVM.Summary.Config.VmPathName).Split(']')[1]).Split('/'))[0].TrimStart(' ') $Path = ($CHKVM.Summary.Config.VmPathName).Split('/')[0] If ($CHKVM.Name-ne $Folder){ $Details.VM= $CHKVM.Name $Details.Path= $Path $VMFolder += $Details} } If (($VMFolder | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs in Inconsistent folders : $($VMFolder.count)" $MyReport += Get-HTMLTable $VMFolder $MyReport += Get-CustomHeaderClose } } # ---- No VM Tools ---- If ($Showtools){ Write-CustomOut "..Checking VM Tools" $NoTools = @($FullVM | Where {$_.Runtime.Powerstate -eq "poweredOn" -And ($_.Guest.toolsStatus -eq "toolsNotInstalled" -Or $_.Guest.ToolsStatus -eq "toolsNotRunning")} | Select Name, @{N="Status";E={$_.Guest.ToolsStatus}}) If (($NoTools | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "No VMTools : $($NoTools.count)" $MyReport += Get-HTMLTable $NoTools $MyReport += Get-CustomHeaderClose } } # ---- CD-Roms Connected ---- If ($ShowCDROM){ Write-CustomOut "..Checking for connected CDRoms" $CDConn = @($VM | Where { $_ | Get-CDDrive | Where { $_.ConnectionState.Connected -eq $true } } | Select Name, Host) If (($CDConn | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VM: CD-ROM Connected - VMotion Violation : $($CDConn.count)" $MyReport += Get-HTMLTable $CDConn $MyReport += Get-CustomHeaderClose } } # ---- Floppys Connected ---- If ($ShowFloppy){ Write-CustomOut "..Checking for connected floppy drives" $Floppy = @($VM | Where { $_ | Get-FloppyDrive | Where { $_.ConnectionState.Connected -eq $true } } | Select Name, Host) If (($Floppy | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VM:Floppy Drive Connected - VMotion Violation : $($Floppy.count)" $MyReport += Get-HTMLTable $Floppy $MyReport += Get-CustomHeaderClose } } # ---- Single Storage VMs ---- If ($ShowSingle){ Write-CustomOut "..Checking Datastores assigned to single hosts for VMs" $LocalVMs = @($LocalOnly | Get-UnShareableDatastore | Where { $_.VM -notmatch $LVMDoNotInclude }) If (($LocalVMs | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VMs stored on non shared datastores : $($LocalVMs.count)" $MyReport += Get-HTMLTable $LocalVMs $MyReport += Get-CustomHeaderClose } } # ---- NTP Check ---- If ($ShowNTP){ Write-CustomOut "..Checking NTP Name and Service" if ($showNTPservieOnly) { $NTPCheck = @($VMH | Where {$_.state -ne "Disconnected"} | Select Name, @{N="NTPServer";E={$_ | Get-VMHostNtpServer}}, @{N="ServiceRunning";E={(Get-VmHostService -VMHost $_ | Where-Object {$_.key -eq "ntpd"}).Running}} | Where {$_.ServiceRunning -eq $false}) } else { $NTPCheck = @($VMH | Where {$_.state -ne "Disconnected"} | Select Name, @{N="NTPServer";E={$_ | Get-VMHostNtpServer}}, @{N="ServiceRunning";E={(Get-VmHostService -VMHost $_ | Where-Object {$_.key -eq "ntpd"}).Running}} | Where {$_.ServiceRunning -eq $false -or $_.NTPServer -ne $ntpserver}) } If (($NTPCheck | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "NTP Issues : $($NTPCheck.count)" $MyReport += Get-HTMLTable $NTPCheck $MyReport += Get-CustomHeaderClose } } # ---- CPU %Ready Check ---- If ($ShowCPURDY){ Write-CustomOut "..Checking VM CPU %RDY" $myCol = @() ForEach ($v in ($VM | Where {$_.PowerState -eq "PoweredOn"})){ For ($cpunum = 0; $cpunum -lt $v.NumCpu; $cpunum++){ $myObj = "" | Select VM, VMHost, CPU, PercReady $myObj.VM = $v.Name $myObj.VMHost = $v.Host $myObj.CPU = $cpunum $myObj.PercReady = [Math]::Round((($v | Get-Stat -Stat Cpu.Ready.Summation -RealTime | Where {$_.Instance -eq $cpunum} | Measure-Object -Property Value -Average).Average)/200,1) $myCol += $myObj } } $rdycheck = @($myCol | Where {$_.PercReady -gt $PercCPUReady} | Sort PercReady -Descending) If (($rdycheck | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VM CPU % RDY over $PercCPUReady : $($rdycheck.count)" $MyReport += Get-HTMLTable $rdycheck $MyReport += Get-CustomHeaderClose } } # ---- Host Alarm ---- If ($ShowHostAlarm){ Write-CustomOut "..Checking Host Alarms" $alarms = $alarmMgr.GetAlarm($null) $valarms = $alarms | select value, @{N="name";E={(Get-View -Id $_).Info.Name}} $hostsalarms = @() foreach ($HostsView in $HostsViews){ if ($HostsView.TriggeredAlarmState){ $hostsTriggeredAlarms = $HostsView.TriggeredAlarmState Foreach ($hostsTriggeredAlarm in $hostsTriggeredAlarms){ $Details = "" | Select-Object Object, Alarm, Status, Time $Details.Object = $HostsView.name $Details.Alarm = ($valarms |?{$_.value -eq ($hostsTriggeredAlarm.alarm.value)}).name $Details.Status = $hostsTriggeredAlarm.OverallStatus $Details.Time = $hostsTriggeredAlarm.time $hostsalarms += $Details } } } If (($hostsalarms | Measure-Object).count -gt 0) { $hostsalarms = $hostsalarms |sort Time $MyReport += Get-CustomHeader "1" "Host(s) Alarm(s) : $($hostalarms.count)" $MyReport += Get-HTMLTable $hostsalarms $MyReport += Get-CustomHeaderClose } } # ---- VM Alarm ---- If ($ShowVMAlarm){ Write-CustomOut "..Checking VM Alarms" $vmsalarms = @() foreach ($VMView in $FullVM){ if ($VMView.TriggeredAlarmState){ $VMsTriggeredAlarms = $VMView.TriggeredAlarmState Foreach ($VMsTriggeredAlarm in $VMsTriggeredAlarms){ $Details = "" | Select-Object Object, Alarm, Status, Time $Details.Object = $VMView.name $Details.Alarm = ($valarms |?{$_.value -eq ($VMsTriggeredAlarm.alarm.value)}).name $Details.Status = $VMsTriggeredAlarm.OverallStatus $Details.Time = $VMsTriggeredAlarm.time $vmsalarms += $Details } } } If (($vmsalarms | Measure-Object).count -gt 0) { $vmsalarms = $vmsalarms | sort Time $MyReport += Get-CustomHeader "1" "VM(s) Alarm(s) : $($vmsalarms.count)" $MyReport += Get-HTMLTable $vmsalarms $MyReport += Get-CustomHeaderClose } } # ---- Cluster ConfigIssue ---- If ($ShowCLUAlarm){ Write-CustomOut "..Checking Cluster Configuration Issues" $clualarms = @() $clusviews = Get-View -ViewType ClusterComputeResource foreach ($clusview in $clusviews) { if ($clusview.ConfigIssue) { $CluConfigIssues = $clusview.ConfigIssue Foreach ($CluConfigIssue in $CluConfigIssues) { $Details = "" | Select-Object Name, Message $Details.name = $clusview.name $Details.Message = $CluConfigIssue.FullFormattedMessage $clualarms += $Details } } } If (($clualarms | Measure-Object).count -gt 0) { $clualarms = $clualarms | sort name $MyReport += Get-CustomHeader "1" "Cluster(s) Config Issue(s) : $($Clualarms.count)" $MyReport += Get-HTMLTable $clualarms $MyReport += Get-CustomHeaderClose } } # ---- Virtual Center Details ---- If ($ShowVCDetails){ Write-CustomOut "..Checking VC Services" $Services = @(Get-VIServices | Where {$_.Name -ne $null -and $_.Health -ne "OK"}) If (($Services | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "$VIServer Service Details : $($Services.count)" $MyReport += Get-HTMLTable ($Services) $MyReport += Get-CustomHeaderClose } } # ---- Virtual Center Event Logs - Error ---- If ($Showvcerror){ Write-CustomOut "..Checking VC Error Event Logs" $ConvDate = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::Now.AddDays(-$VCEvntlgAge)) If ($SetUsername -ne ""){ $ErrLogs = @(Get-WmiObject -Credential $creds -computer $VIServer -query ("Select * from Win32_NTLogEvent Where Type='Error' and TimeWritten >='" + $ConvDate + "'") | Where {$_.Message -like "*VMware*"} | Select @{N="TimeGenerated";E={$_.ConvertToDateTime($_.TimeGenerated)}}, Message) } Else { $ErrLogs = @(Get-WmiObject -computer $VIServer -query ("Select * from Win32_NTLogEvent Where Type='Error' and TimeWritten >='" + $ConvDate + "'") | Where {$_.Message -like "*VMware*"} | Select @{N="TimeGenerated";E={$_.ConvertToDateTime($_.TimeGenerated)}}, Message) } If (($ErrLogs | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "$VIServer Event Logs ($VCEvntlgAge day(s)): Error : $($ErrLogs.count)" $MyReport += Get-HTMLTable ($ErrLogs) $MyReport += Get-CustomHeaderClose } } # ---- Virtual Center Event Logs - Warning ---- If ($Showvcwarn){ Write-CustomOut "..Checking VC Warning Event Logs" $ConvDate = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::Now.AddDays(-$VCEvntlgAge)) If ($SetUsername -ne ""){ $WarnLogs = @(Get-WmiObject -Credential $creds -computer $VIServer -query ("Select * from Win32_NTLogEvent Where Type='Warning' and TimeWritten >='" + $ConvDate + "'") | Where {$_.Message -like "*VMware*"} | Select @{N="TimeGenerated";E={$_.ConvertToDateTime($_.TimeGenerated)}}, Message) } Else { $WarnLogs = @(Get-WmiObject -computer $VIServer -query ("Select * from Win32_NTLogEvent Where Type='Warning' and TimeWritten >='" + $ConvDate + "'") | Where {$_.Message -like "*VMware*"} | Select @{N="TimeGenerated";E={$_.ConvertToDateTime($_.TimeGenerated)}}, Message ) } If (($WarnLogs | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "$VIServer Event Logs ($VCEvntlgAge day(s)): Warning : $($WarnLogs.count)" $MyReport += Get-HTMLTable ($WarnLogs) $MyReport += Get-CustomHeaderClose } } # ---- vmkernel log file check ---- If ($Showvmkernel){ Write-CustomOut "..Checking vmkernel logs for warnings" $days = ([string](-$vmkernelchk..0 | Foreach-Object {Get-Date -date (Get-Date).AddDays($_) -uFormat "%b %e|"})).TrimEnd("|").Replace("| ","|") $VMKernelWarning = @() $rexp = [regex]("(?:" + $days + ")" + ".*" + "WARNING") foreach ($VMHost in ($VMH)){ $Warnings = (Get-Log –VMHost ($VMHost) -Key vmkernel -ErrorAction SilentlyContinue).Entries | where {$_ -match $rexp} if ($Warnings -ne $null) { $Warnings | % { $Details = "" | Select-Object VMHost, Message $Details.VMHost = $VMHost.Name $Details.Message = $_ $VMKernelWarning += $Details } } } If (($VMKernelWarning | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Warning messages: vmkernel : $($VMKernelWarning.count)" $MyReport += Get-HTMLTable $VMKernelWarning $MyReport += Get-CustomHeaderClose } } # ---- Hosts Ballooning ---- if ($ShowHostBallooning) { Write-CustomOut "..Checking Hosts Ballooning" $BalloonHosts = $VMH | select name, @{N="vmmemctl (GB)";E={[math]::round((Get-Stat -Entity $_ -stat mem.vmmemctl.average -Start (Get-Date).adddays(-$BalloonHostsAge) -Finish (Get-Date) -Realtime |Measure-Object -Property value -Average |select Average).average/1048576,0)}} |where {$_."vmmemctl (GB)"-gt "0"} If (($BalloonHosts | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Hosts Ballooning (Last $BalloonHostsAge Day(s)) : $($BalloonHosts.count)" $MyReport += Get-HTMLTable $BalloonHosts $MyReport += Get-CustomHeaderClose } } # ---- Datastore OverAllocation ---- if ($ShowOverAllocation) { Write-CustomOut "..Checking Datastore OverAllocation" $storages = $Datastores |Get-View $voverallocation = @() foreach ($storage in $storages) { if ($storage.Summary.Uncommitted -gt "0") { $Details = "" | Select-Object Datastore, Overallocation $Details.Datastore = $storage.name $Details.overallocation = [math]::round(((($storage.Summary.Capacity - $storage.Summary.FreeSpace) + $storage.Summary.Uncommitted)*100)/$storage.Summary.Capacity,0) if ($Details.overallocation -gt $OverAllocation) { $voverallocation += $Details } } } If (($voverallocation | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Datastore OverAllocation % : $($voverallocation.count)" $MyReport += Get-HTMLTable $voverallocation $MyReport += Get-CustomHeaderClose } } # invalid or inaccessible VM if ($ShowBlindedVM) { Write-CustomOut "..Checking invalid or inaccessible VM" $BlindedVM = $FullVM|?{$_.Runtime.ConnectionState -eq "invalid" -or $_.Runtime.ConnectionState -eq "inaccessible"}|sort name |select name If (($BlindedVM | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VM invalid or inaccessible : $($BlindedVM.count)" $MyReport += Get-HTMLTable $BlindedVM $MyReport += Get-CustomHeaderClose } } # VCB Garbage if ($ShowVCBgarbage) { Write-CustomOut "..Checking VCB Garbage" $VCBGarbage = $VM |where { (Get-Snapshot -VM $_).name -eq "_VCB-BACKUP_" -or (Get-Snapshot -VM $_).name -eq "Consolidate Helper- 0"} |sort name |select name If (($VCBGarbage | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "VCB Garbage : $($VCBGarbage.count)" $MyReport += Get-HTMLTable $VCBGarbage $MyReport += Get-CustomHeaderClose } } # ---- Snapshot Oversize Information ---- if ($ShowOversize) { If ($vSphere -eq $true){ Write-CustomOut "..Checking Snapshot Oversize Information" $Snapshots = Get-hypersnapshot($VMViews)|sort OverSize -Descending If (($Snapshots | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Snapshots Oversize : $($Snapshots.count)" $MyReport += Get-HTMLTable $Snapshots $MyReport += Get-CustomHeaderClose } } } # Full VMKernel Warnings check if ($ShowFullVMKernel) { Write-CustomOut "..Checking Full VMKernel Warnings" $SysGlobalization = New-Object System.Globalization.CultureInfo("en-US") $VMHV = Get-View -ViewType HostSystem $VMKernelWarnings = @() foreach ($VMHost in ($VMHV)){ $product = $VMHost.config.product.ProductLineId if ($product -eq "embeddedEsx"){ $Warnings = (Get-Log -vmhost ($VMHost.name) -Key messages -ErrorAction SilentlyContinue).entries |where {$_ -match "warning" -and $_ -match "vmkernel"} if ($Warnings -ne $null) { $VMKernelWarning = @() $Warnings | % { $Details = "" | Select-Object VMHost, Time, Message, Length $Details.VMHost = $VMHost.Name if (($_.split()[1]) -eq "") {$Details.Time = ([datetime]::ParseExact(($_.split()[0] + " " + $_.split()[2] + " " + $_.split()[3]), "MMM d HH:mm:ss", $SysGlobalization))} else {$Details.Time = ([datetime]::ParseExact(($_.split()[0] + " " + $_.split()[1] + " " + $_.split()[2]), "MMM dd HH:mm:ss", $SysGlobalization))} $Details.Message = ([regex]::split($_, "WARNING: "))[1] $Details.Length = ($Details.Message).Length if ($Details.Length -gt 0) { if ($Details.Time -gt $starttime.AddDays(-$FullVMKernelchk)) { $VMKernelWarning += $Details } } } $VMKernelWarnings += $VMKernelWarning | Sort-Object -Property Length -Unique |select VMHost, Message, Time } } else { $Warnings = (Get-Log –VMHost ($VMHost.Name) -Key vmkernel -ErrorAction SilentlyContinue).Entries | where {$_ -match "warning" -and $_ -match "vmkernel"} if ($Warnings -ne $null) { $VMKernelWarning = @() $Warnings | % { $Details = "" | Select-Object VMHost, Time, Message, Length $Details.VMHost = $VMHost.Name if (($_.split()[1]) -eq "") {$Details.Time = ([datetime]::ParseExact(($_.split()[0] + " " + $_.split()[2] + " " + $_.split()[3]), "MMM d HH:mm:ss", $SysGlobalization))} else {$Details.Time = ([datetime]::ParseExact(($_.split()[0] + " " + $_.split()[1] + " " + $_.split()[2]), "MMM dd HH:mm:ss", $SysGlobalization))} $Details.Message = ([regex]::split($_, "WARNING: "))[1] $Details.Length = ($Details.Message).Length if ($Details.Length -gt 0) { if ($Details.Time -gt $starttime.AddDays(-$FullVMKernelchk)) { $VMKernelWarning += $Details } } } $VMKernelWarnings += $VMKernelWarning | Sort-Object -Property Length -Unique |select VMHost, Message, Time } } } If (($VMKernelWarnings | Measure-Object).count -gt 0) { $VMKernelWarnings = $VMKernelWarnings |sort time -Descending $MyReport += Get-CustomHeader "1" "ESX/ESXi VMKernel Warnings" $MyReport += Get-HTMLTable $VMKernelWarnings $MyReport += Get-CustomHeaderClose } } if ($ShowSnapSum) { Write-CustomOut "..Checking Snapshot add/remove summary" # ---- Snapshots created ---- $Outputaddsnaps = $VIEvent | where {$_.fullFormattedMessage -match "Create virtual machine snapshot"}| Select createdTime, @{N="User";E={(Find-Username (($_.userName.split("\"))[1])).Properties.displayname}}, @{N="VM Name";E={$_.vm.name}} If (($Outputaddsnaps | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Snapshot created (Last $VMsNewRemovedAge Day(s)) : $($Outputaddsnaps.count)" $MyReport += Get-HTMLTable $Outputaddsnaps $MyReport += Get-CustomHeaderClose } # ---- Snapshots deleted ---- $Outputremsnaps = $VIEvent | where {$_.fullFormattedMessage -match "Remove snapshot"}| Select createdTime, @{N="User";E={(Find-Username (($_.userName.split("\"))[1])).Properties.displayname}}, @{N="VM Name";E={$_.vm.name}} If (($Outputremsnaps | Measure-Object).count -gt 0) { $MyReport += Get-CustomHeader "1" "Snapshot removed (Last $VMsNewRemovedAge Day(s)) : $($Outputremsnaps.count)" $MyReport += Get-HTMLTable $Outputremsnaps $MyReport += Get-CustomHeaderClose } } # ---- Time to build ---- $MyReport += Get-CustomHeader "1" "Time to build" $finishtime=Get-Date $executiontime = $finishtime - $starttime $MyReport += Get-HTMLDetail "Time in minutes:" (@([math]::round($executiontime.TotalMinutes,0))) $MyReport += Get-CustomHeaderClose $MyReport += Get-CustomHeader0Close $MyReport += Get-CustomHTMLClose #Uncomment the following lines to save the htm file in a central location #$Date = Get-Date #$Filename = "C:\Temp\" + $VIServer + "DailyReport" + "_" + $Date.Day + "-" + $Date.Month + "-" + $Date.Year + ".htm" #$MyReport | out-file -encoding ASCII -filepath $Filename #Invoke-Item $Filename Write-CustomOut "..Sending Email" send-SMTPmail $EmailTo $EmailFrom "$VISRV Daily Report" $SMTPSRV $MyReport $VIServer | Disconnect-VIServer -Confirm:$false