Skip to content

Instantly share code, notes, and snippets.

@hansdg1
Created May 16, 2019 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hansdg1/ea4f1e9248f5dfdbcc5cbb0ecd9ebb4b to your computer and use it in GitHub Desktop.
Save hansdg1/ea4f1e9248f5dfdbcc5cbb0ecd9ebb4b to your computer and use it in GitHub Desktop.
#Requires -Version 5.0 -Modules VMware.VimAutomation.Core
<#
.SYNOPSIS
Check vCenter(s) for any VMs with disks needing consolidation, attempting to consolidate any it finds.
.DESCRIPTION
Kicks off ConsolidateVMDisks_Tasks for any VirtualMachine objects it finds needing consolidation. Emails a report detailing any it successfully consolidates, has errors with, or excluded due.
.NOTES
Since we don't specify a credential pair to connect with, it will attempt to connect as the current script environment's user.
Due to the current architecture of the code, if the -WhatIf switch is used, the Disconnect-VIServer command will not execute. To workaround this, I added "-WhatIf:$false" to that line.
#>
[CmdletBinding(SupportsShouldProcess = $true)]
Param(
# List of VMs to exclude from disk consolidation
[string[]]
$ExcludedVMs,
# Email address the report will be sent to.
[ValidateNotNullOrEmpty()]
[string[]]
$ToAddress,
# Email address the report will show as from.
[string]
$FromAddress
)
function Write-Log {
<#
.SYNOPSIS
Logs messages to a file
.DESCRIPTION
Receives messages and logs them to an output file.
.EXAMPLE
Write-Log -Message "Write this to the log" -Path "C:\Temp\LogFile.log"
.NOTES
Uses the variable $LogPath as the default value for Path
#>
[CmdletBinding()]
param (
# The message to be written
[Parameter(Mandatory = $true)]
[string]
$Message,
# File path of the log file. Default value is $LogPath, if set.
[ValidateNotNullOrEmpty()]
[string]
$Path = $LogPath
)
begin {
$ConfirmPreference = 'None'
}
process {
Write-Verbose $Message
Add-Content -Path $Path -Value "[$(Get-Date -Format 'yy:MM:dd-hh:mm:ss')] $Message"
}
}
function Add-EmailBodyContent {
<#
.SYNOPSIS
Generates a formatted text block using a heading and list of child items.
.DESCRIPTION
The Add-EmailBodyContent cmdlet accepts a SectionHeader and array of ChildItems and uses them to generate a formatted block of text.
The SectionHeader is listed on the first line, with each of the ChildItems indented on subsequent lines.
.EXAMPLE
PS> Add-EmailBodyContent -SectionHeader 'Cars' -ChildItems $('car1','car2','car3')
Cars:
car1
car2
car3
#>
[CmdletBinding()]
param (
# The header for the text block.
[Parameter(Mandatory)]
[string]
$SectionHeader,
# List of items to appear indented under the SectionHeader
[Parameter(Mandatory)]
[string[]]
$ChildItems
)
process {
Write-Output "$($SectionHeader):`n"
foreach ($item in $ChildItems) {
Write-Output "`t$($item)`n"
}
Write-Output "`n"
}
}
function Invoke-DiskConsolidation {
<#
.SYNOPSIS
Start disk consolidation tasks for each VirtualMachine in View
.DESCRIPTION
Start disk consolidation tasks for each of the VirtualMachine managed objects in View. These tasks are non-blocking and will execute in parallel.
.EXAMPLE
PS> $TaskList = Invoke-DiskConsolidation -View $view -ExcludedVMs $ExcludedVMs
Returns a Hashtable ($TaskList) containing the task objects and VM names
.NOTES
The tasks created by the ConsolidateVMDisks_Task method are non-blocking, meaning it will not wait for completion before continuing.
There is another VirtualMachine method, ConsolidateVMDisks, which waits for the consolidation to complete before continuing.
.OUTPUTS
[System.Collections.Hashtable] Containing tasks and the VM names
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param (
# List of vSphere View VirtualMachine objects representing the VMs that will have their disks consolidated.
[VMware.Vim.ViewBase[]]
$View,
# List of VMs to exclude from consolidation
[string[]]
$ExcludedVMs
)
begin {
$View = $View | Where-Object {$_.Name -notin $ExcludedVMs}
$TaskList = @{}
}
process {
foreach ($VirtualMachineObj in $View) {
Write-Log "Kicking off consolidation for $($VirtualMachineObj.Name)"
if ($PSCmdlet.ShouldProcess($VirtualMachineObj.Name)) {
$TaskList[(Get-Task -Id $VirtualMachineObj.ConsolidateVMDisks_Task()).Id] = $VirtualMachineObj.Name
}
}
}
end {
Write-Output $TaskList
}
}
##### Variables #####
$vCentersList = @('vcenter1.example.com', 'vcenter2.example.com')
$LogPath = "$env:SystemDrive\Windows\Temp\Disk_Consolidation.log"
$ExcludedVMs = @('exclude-this-vm')
# Used when determining if consolidation succeeded/failed
$TaskList = @{}
$SuccessfulVMs = @()
$ErrorVMs = @()
# Used in report generation
$EmailBody = ''
#####################
foreach ($vCenter in $vCentersList) {
try {
Connect-VIServer $vCenter -ErrorAction Stop | Out-Null
Write-Log "Connected to $($vCenter)"
} catch {
Write-Log "Error connecting to $($vCenter)"
}
}
if ($Global:DefaultVIServers.Count -gt 0) {
$VirtualMachineView = Get-View -ViewType VirtualMachine -Property Name, Runtime.ConsolidationNeeded -Filter @{'Runtime.ConsolidationNeeded' = 'True' }
$TaskList = Invoke-DiskConsolidation -View $VirtualMachineView -ExcludedVMs $ExcludedVMs
do {
foreach ($Task in @($TaskList.Keys)) {
switch (Get-Task -Id $Task) {
{$_.State -eq 'Success'} {
Write-Log "Successfully consolidated $($TaskList.$Task)."
$SuccessfulVMs += $TaskList.$Task
$TaskList.Remove($Task)
}
{$_.State -eq 'Error'} {
Write-Log "Error consolidating $($TaskList.$Task)."
$ErrorVMs += $TaskList.$Task
$TaskList.Remove($Task)
}
{$_.State -eq 'Running'} {
Write-Log "Waiting for $($Task) ($($TaskList.$Task)) to complete."
Start-Sleep -Seconds 5
}
Default { Start-Sleep -Seconds 5 }
}
}
} while ($TaskList.Count -gt 0)
if ($SuccessfulVMs -gt 0) {
$EmailBody += Add-EmailBodyContent -SectionHeader "Successfully Consolidated" -ChildItems $SuccessfulVMs
}
if ($ErrorVMs -gt 0) {
$EmailBody += Add-EmailBodyContent -SectionHeader "Error Consolidating" -ChildItems $ErrorVMs
}
if ($ExcludedVMs -gt 0) {
$EmailBody += Add-EmailBodyContent -SectionHeader "Excluded From Consolidation" -ChildItems $ExcludedVMs
}
# If $EmailBody is still empty, include a warning
if ($EmailBody -eq '') {
$EmailBody += Add-EmailBodyContent -SectionHeader "Warning, no VMs processed. Check the logs." -ChildItems "$($LogPath)"
}
Write-Log -Message "Sending email report. From:$($FromAddress) To:$($ToAddress)"
Send-MailMessage -From $FromAddress -To $ToAddress -SmtpServer 'mailserver.example.com' -Subject 'Automated Disk Consolidation Results' -Body $EmailBody
Disconnect-VIServer * -Confirm:$false -WhatIf:$false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment