From 7a38462f1a7bb90591825fd0bcf25df6d2f52ecb Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:34:15 +0200 Subject: [PATCH 01/21] Create VMFSIncrease.psd1 --- Modules/VMFSIncrease/VMFSIncrease.psd1 | 1 + 1 file changed, 1 insertion(+) create mode 100644 Modules/VMFSIncrease/VMFSIncrease.psd1 diff --git a/Modules/VMFSIncrease/VMFSIncrease.psd1 b/Modules/VMFSIncrease/VMFSIncrease.psd1 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Modules/VMFSIncrease/VMFSIncrease.psd1 @@ -0,0 +1 @@ + From 1489e86b669e9ab790a1dfa265d52464db0a3ab8 Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:37:11 +0200 Subject: [PATCH 02/21] Add files via upload --- Modules/VMFSIncrease/LICENSE.txt.URL | 6 + Modules/VMFSIncrease/VMFSIncrease.psd1 | 19 +- Modules/VMFSIncrease/VMFSIncrease.psm1 | 247 +++++++++++++++++++++++++ 3 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 Modules/VMFSIncrease/LICENSE.txt.URL create mode 100644 Modules/VMFSIncrease/VMFSIncrease.psm1 diff --git a/Modules/VMFSIncrease/LICENSE.txt.URL b/Modules/VMFSIncrease/LICENSE.txt.URL new file mode 100644 index 0000000..5ac2446 --- /dev/null +++ b/Modules/VMFSIncrease/LICENSE.txt.URL @@ -0,0 +1,6 @@ +[InternetShortcut] +URL=https://github.com/lucdekens/LogInsight/blob/v1.0/LICENSE.txt +IDList= +HotKey=0 +IconFile=C:\Users\ldekens\AppData\Local\Mozilla\Firefox\Profiles\2ahnnh1i.default\shortcutCache\ec4nFcIEAQBPFmSiPtTJ2w==.ico +IconIndex=0 diff --git a/Modules/VMFSIncrease/VMFSIncrease.psd1 b/Modules/VMFSIncrease/VMFSIncrease.psd1 index 8b13789..d87ad53 100644 --- a/Modules/VMFSIncrease/VMFSIncrease.psd1 +++ b/Modules/VMFSIncrease/VMFSIncrease.psd1 @@ -1 +1,18 @@ - +@{ + ModuleToProcess = 'VMFSIncrease.psm1' + ModuleVersion = '1.0.0.0' + GUID = '9f167385-c5c6-4a65-ac14-949c67519001' + Author = 'Luc Dekens ' + CompanyName = 'Community' + Copyright = '(c) 2016. All rights reserved.' + Description = 'Expand and Extend VMFS DatastoresModule description' + PowerShellVersion = '3.0' + FunctionsToExport = 'Get-VmfsDatastoreInfo','Get-VmfsDatastoreIncrease','New-VmfsDatastoreIncrease' + PrivateData = @{ + PSData = @{ + Tags = @('VMFS','Expand','Extend','vSphere') + LicenseUri = 'https://www.tldrlegal.com/l/mit' + ProjectUri = 'https://github.com/lucdekens/VMFSIncrease' + } + } +} diff --git a/Modules/VMFSIncrease/VMFSIncrease.psm1 b/Modules/VMFSIncrease/VMFSIncrease.psm1 new file mode 100644 index 0000000..ec0fa2b --- /dev/null +++ b/Modules/VMFSIncrease/VMFSIncrease.psm1 @@ -0,0 +1,247 @@ +function Get-VmfsDatastoreInfo +{ + [CmdletBinding(SupportsShouldProcess = $True)] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True)] + [PSObject]$Datastore + ) + + Process + { + if ($Datastore -is [String]) + { + $Datastore = Get-Datastore -Name $Datastore -ErrorAction SilentlyContinue + } + if ($Datastore -isnot [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]) + { + Write-Error 'Invalid value for Datastore.' + return + } + if ($Datastore.Type -ne 'VMFS') + { + Write-Error "$($Datastore.Name) is not a VMFS datastore" + return + } + + # Get the Datastore System Manager from an ESXi that has the Datastore + $esx = Get-View -Id ($Datastore.ExtensionData.Host | Get-Random | Select -ExpandProperty Key) + $hsSys = Get-View -Id $esx.ConfigManager.StorageSystem + + foreach ($extent in $Datastore.ExtensionData.Info.Vmfs.Extent) + { + $lun = $esx.Config.StorageDevice.ScsiLun | where{ $_.CanonicalName -eq $extent.DiskName } + + $hdPartInfo = $hsSys.RetrieveDiskPartitionInfo($lun.DeviceName) + $hdPartInfo[0].Layout.Partition | %{ + New-Object PSObject -Property ([ordered]@{ + Datastore = $Datastore.Name + CanonicalName = $lun.CanonicalName + Model = "$($lun.Vendor.TrimEnd(' ')).$($lun.Model.TrimEnd(' ')).$($lun.Revision.TrimEnd(' '))" + DiskSizeGB = $hdPartInfo[0].Layout.Total.BlockSize * $hdPartInfo[0].Layout.Total.Block / 1GB + DiskBlocks = $hdPartInfo[0].Layout.Total.Block + DiskBlockMB = $hdPartInfo[0].Layout.Total.BlockSize/1MB + PartitionFormat = $hdPartInfo[0].Spec.PartitionFormat + Partition = if ($_.Partition -eq '') { '' }else{ $_.Partition } + Used = $extent.Partition -eq $_.Partition + Type = $_.Type + PartitionSizeGB = [math]::Round(($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize / 1GB, 1) + PartitionBlocks = $_.End.Block - $_.Start.Block + 1 + PartitionBlockMB = $_.Start.BlockSize/1MB + Start = $_.Start.Block + End = $_.End.Block + }) + } + } + } +} + +function Get-VmfsDatastoreIncrease +{ + [CmdletBinding(SupportsShouldProcess = $True)] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True)] + [PSObject]$Datastore + ) + + Process + { + if ($Datastore -is [String]) + { + $Datastore = Get-Datastore -Name $Datastore -ErrorAction SilentlyContinue + } + if ($Datastore -isnot [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]) + { + Write-Error 'Invalid value for Datastore.' + return + } + + if ($Datastore.Type -ne 'VMFS') + { + Write-Error "$($Datastore.Name) is not a VMFS datastore" + return + } + + # Get the Datastore System Manager from an ESXi that has the Datastore + $esx = Get-View -Id ($Datastore.ExtensionData.Host | Get-Random | Select -ExpandProperty Key) + $hsSys = Get-View -Id $esx.ConfigManager.StorageSystem + $hdSys = Get-View -Id $esx.ConfigManager.DatastoreSystem + + $extents = $Datastore.ExtensionData.Info.Vmfs.Extent | Select -ExpandProperty DiskName + + $hScsiDisk = $hdSys.QueryAvailableDisksForVmfs($Datastore.ExtensionData.MoRef) + foreach ($disk in $hScsiDisk) + { + $partInfo = $hsSys.RetrieveDiskPartitionInfo($disk.DeviceName) + $partUsed = ($partInfo[0].Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + if ($extents -contains $disk.CanonicalName) + { + $incType = 'Expand' + $vmfsExpOpt = $hdSys.QueryVmfsDatastoreExpandOptions($Datastore.ExtensionData.MoRef) + $PartMax = ($vmfsExpOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + } + else + { + $incType = 'Extend' + $vmfsExtOpt = $hdSys.QueryVmfsDatastoreExtendOptions($Datastore.ExtensionData.MoRef, $disk.DevicePath, $null) + $partMax = ($vmfsExpOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + } + New-Object PSObject -Property ([ordered]@{ + Datastore = $Datastore.Name + CanonicalName = $disk.CanonicalName + Model = "$($disk.Vendor.TrimEnd(' ')).$($disk.Model.TrimEnd(' ')).$($disk.Revision.TrimEnd(' '))" + DiskSizeGB = $partInfo[0].Layout.Total.BlockSize * $hdPartInfo[0].Layout.Total.Block / 1GB + DiskBlocks = $partInfo[0].Layout.Total.Block + DiskBlockMB = $partInfo[0].Layout.Total.BlockSize/1MB + AvailableGB = [math]::Round($partMax - $partUsed, 2) + Type = $incType + }) + } + } +} + +function New-VmfsDatastoreIncrease +{ + [CmdletBinding(SupportsShouldProcess = $True)] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $True)] + [PSObject]$Datastore, + [int]$IncreaseSizeGB, + [Parameter(Position = 1)] + [string]$CanonicalName, + [Parameter(Mandatory = $true, ParameterSetName = 'Expand')] + [switch]$Expand, + [Parameter(Mandatory = $true, ParameterSetName = 'ExTend')] + [switch]$Extend + ) + + Process + { + if ($Datastore -is [String]) + { + $Datastore = Get-Datastore -Name $Datastore -ErrorAction SilentlyContinue + } + if ($Datastore -isnot [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]) + { + Write-Error 'Invalid value for Datastore.' + return + } + + if ($Datastore.Type -ne 'VMFS') + { + Write-Error "$($Datastore.Name) is not a VMFS datastore" + return + } + + # Get the Datastore System Manager from an ESXi that has the Datastore + $esx = Get-View -Id ($Datastore.ExtensionData.Host | Get-Random | Select -ExpandProperty Key) + $hsSys = Get-View -Id $esx.ConfigManager.StorageSystem + $hdSys = Get-View -Id $esx.ConfigManager.DatastoreSystem + + $extents = $Datastore.ExtensionData.Info.Vmfs.Extent | Select -ExpandProperty DiskName + + $hScsiDisk = $hdSys.QueryAvailableDisksForVmfs($Datastore.ExtensionData.MoRef) + + # Expand or Extend + switch ($PSCmdlet.ParameterSetName) + { + 'Expand' { + $expOpt = $hdSys.QueryVmfsDatastoreExpandOptions($Datastore.ExtensionData.MoRef) + if ($CanonicalName) + { + $dsOpt = $expOpt | where{ $_.Spec.Extent.DiskName -eq $CanonicalName } + } + else + { + $dsOpt = $expOpt | Sort-Object -Property { $_.Spec.Extent.Diskname } | select -first 1 + } + if ($IncreaseSizeGB -ne 0) + { + $lun = $hScsiDisk | where{ $_.CanonicalName -eq $dsOpt.Spec.Extent.DiskName } + $partInfo = $hsSys.RetrieveDiskPartitionInfo($lun.DeviceName) + $partMax = ($vmfsExpOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + $partUsed = ($partInfo[0].Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + if (($partMax - $partUsed) -ge $IncreaseSizeGB) + { + $spec = $dsOpt.Spec + $spec.Partition.Partition[0].EndSector -= ([math]::Floor(($partMax - $partUsed - $IncreaseSizeGB) * 1GB/512)) + } + else + { + Write-Error "Requested expand size $($IncreaseSizeGB)GB not available on $($lun.CanonicalName)" + return + } + } + else + { + $spec = $dsOpt.Spec + } + $hdSys.ExpandVmfsDatastore($Datastore.ExtensionData.MoRef, $spec) + } + 'Extend' { + if ($CanonicalName) + { + $lun = $hScsiDisk | where{ $extents -notcontains $_.CanonicalName -and $_.CanonicalName -eq $CanonicalName } + } + else + { + $lun = $hScsiDisk | where{ $extents -notcontains $_.CanonicalName } | Sort-Object -Property CanonicalName | select -First 1 + } + if (!$lun) + { + Write-Error "No valid LUN provided or found for extent" + return + } + $vmfsExtOpt = $hdSys.QueryVmfsDatastoreExtendOptions($Datastore.ExtensionData.MoRef, $lun.DevicePath, $null) + if ($IncreaseSizeGB -ne 0) + { + $partInfo = $hsSys.RetrieveDiskPartitionInfo($lun.DeviceName) + $partMax = ($vmfsExpOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + Measure-Object -Sum | select -ExpandProperty Sum)/1GB + if ($partMax -ge $IncreaseSizeGB) + { + $spec = $vmfsExtOpt[0].Spec + $spec.Partition.Partition[0].EndSector = $spec.Partition.Partition[0].StartSector + [math]::Floor($IncreaseSizeGB * 1GB / 512) + } + else + { + Write-Error "No valid LUN for extent with $($IncreaseSizeGB)GB space found" + return + } + } + else + { + $spec = $vmfsExtOpt.Spec + } + + $hdSys.ExtendVmfsDatastore($Datastore.ExtensionData.MoRef, $spec) + } + } + } +} + +Export-ModuleMember -Function Get-VmfsDatastoreInfo,Get-VmfsDatastoreIncrease,New-VmfsDatastoreIncrease From 7392e451c5d528fd2a44d9ca243441ff375f983e Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:37:35 +0200 Subject: [PATCH 03/21] Create en-US --- Modules/VMFSIncrease/en-US | 1 + 1 file changed, 1 insertion(+) create mode 100644 Modules/VMFSIncrease/en-US diff --git a/Modules/VMFSIncrease/en-US b/Modules/VMFSIncrease/en-US new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Modules/VMFSIncrease/en-US @@ -0,0 +1 @@ + From b2ba87db865ead8f404af051ab7526cd4c8d828c Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:38:13 +0200 Subject: [PATCH 04/21] Delete en-US --- Modules/VMFSIncrease/en-US | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Modules/VMFSIncrease/en-US diff --git a/Modules/VMFSIncrease/en-US b/Modules/VMFSIncrease/en-US deleted file mode 100644 index 8b13789..0000000 --- a/Modules/VMFSIncrease/en-US +++ /dev/null @@ -1 +0,0 @@ - From 7bbec511cbaf3d969450ce05f7e0e6ec64f11bbd Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:38:56 +0200 Subject: [PATCH 05/21] Create about_VMFSIncrease.help.txt --- .../VMFSIncrease/en-US/about_VMFSIncrease.help.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Modules/VMFSIncrease/en-US/about_VMFSIncrease.help.txt diff --git a/Modules/VMFSIncrease/en-US/about_VMFSIncrease.help.txt b/Modules/VMFSIncrease/en-US/about_VMFSIncrease.help.txt new file mode 100644 index 0000000..55f3c0a --- /dev/null +++ b/Modules/VMFSIncrease/en-US/about_VMFSIncrease.help.txt @@ -0,0 +1,13 @@ +TOPIC + VMFSIncrease + +SYNOPSIS + The VMFSIncrease module offers the same functionality that is available +through the Increase button in the vSphere Web Client. + +DESCRIPTION + The VMFSIncrease offers functionality that allows to Expand or Extend + VMFS Datastores. The module uses the vSphere API to implement this functionality. + + SEE ALSO +http://www.lucd.info/2016/07/29/vmfs-datastores-expand-and-extend From 99250732fb33773e67cc909c42ba45188200b03c Mon Sep 17 00:00:00 2001 From: lucdekens Date: Fri, 29 Jul 2016 15:39:10 +0200 Subject: [PATCH 06/21] Add files via upload --- .../en-US/VMFSIncrease.psm1-Help.xml | 473 ++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 Modules/VMFSIncrease/en-US/VMFSIncrease.psm1-Help.xml diff --git a/Modules/VMFSIncrease/en-US/VMFSIncrease.psm1-Help.xml b/Modules/VMFSIncrease/en-US/VMFSIncrease.psm1-Help.xml new file mode 100644 index 0000000..2ff4f26 --- /dev/null +++ b/Modules/VMFSIncrease/en-US/VMFSIncrease.psm1-Help.xml @@ -0,0 +1,473 @@ + + + + + + + + + + Get-VmfsDatastoreInfo + + Provides partition information for all the extents in the datastore. + + + + + Get + VmfsDatastoreInfo + + + + The function will display partition information for all the extents used by the datastore. + + + + + Get-VmfsDatastoreInfo + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + + + + + + + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + PSObject + + + + + + + + + + + System.Management.Automation.PSObject + + + + + + + + + + + + + System.Object + + + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS C:\> + + Get-VmfsDatastoreInfo -Datastore MyDS + + Will return partition information for the Datastore named MyDS + + + + -------------------------- EXAMPLE 2 -------------------------- + + PS C:\> + + Get-Datastore -Name My* | Get-VmfsDatastoreInfo + + This example will return partition information for all the Datastore objects that are returned by the Get-Datastore PowerCLI cmdlet + + + + + + + + Get-VmfsDatastoreIncrease + + Displays the increase options for a datastore + + + + + Get + VmfsDatastoreIncrease + + + + The function will provide all the Expand and Extend options for a specific datastore + + + + + Get-VmfsDatastoreIncrease + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + + + + + + + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + PSObject + + + + + + + + + + + System.Management.Automation.PSObject + + + + + + + + + + + + + System.Object + + + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS C:\> + + Get-VmfsDatastoreIncrease -Datastore MyDS + + The exmaple will list all Expand and Extend options available for the Datastore, named MyDS + + + + -------------------------- EXAMPLE 2 -------------------------- + + PS C:\> + + Get-Datastore -Name MyDS* | Get-VmfsDatastoreIncrease + + The Expand and Extend options for all Datastore retruned by the PowerCLI Get-Datastore will be returned. + + + + + + + + New-VmfsDatastoreIncrease + + Increase the capacity of a Datastore + + + + + New + VmfsDatastoreIncrease + + + + The capacity of the Datastore in increased through an Expand or an Extend. +To allow successful completion there shall be free capacity on one of the Extents, or there shall be free LUNs available. +With the Expand or Extend switches the caller selects which type of capacity increase is used. + + + + + New-VmfsDatastoreIncrease + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + + + + CanonicalName + + The Canonical name of the LUN on which to create a new Extent, or the name of the LUN on which to apply the Expansion. +If this parameter is not provided, the function will sort the available LUN alphanumerically on the Canonical names and slect the first one. + + String + + + + + IncreaseSizeGB + + The amount of GB by which to increase the size of the Datastore. +If this parameter is not used, all of the available Expand or Extend diskspace will be used. + + Int32 + + + + + Expand + + A switch to indicate if the Datastore shall be Expanded + + SwitchParameter + + + + + + New-VmfsDatastoreIncrease + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + + + + CanonicalName + + The Canonical name of the LUN on which to create a new Extent, or the name of the LUN on which to apply the Expansion. +If this parameter is not provided, the function will sort the available LUN alphanumerically on the Canonical names and slect the first one. + + String + + + + + IncreaseSizeGB + + The amount of GB by which to increase the size of the Datastore. +If this parameter is not used, all of the available Expand or Extend diskspace will be used. + + Int32 + + + + + Extend + + A switch to indicate if the Datastore shall be Extended + + SwitchParameter + + + + + + + + + Datastore + + The name of the Datastore or a PowerCLI Datastore object + + PSObject + + PSObject + + + + + + + IncreaseSizeGB + + The amount of GB by which to increase the size of the Datastore. +If this parameter is not used, all of the available Expand or Extend diskspace will be used. + + Int32 + + Int32 + + + + + + + CanonicalName + + The Canonical name of the LUN on which to create a new Extent, or the name of the LUN on which to apply the Expansion. +If this parameter is not provided, the function will sort the available LUN alphanumerically on the Canonical names and slect the first one. + + String + + String + + + + + + + Expand + + A switch to indicate if the Datastore shall be Expanded + + SwitchParameter + + SwitchParameter + + + + + + + Extend + + A switch to indicate if the Datastore shall be Extended + + SwitchParameter + + SwitchParameter + + + + + + + + + + + System.Management.Automation.PSObject + + + + + + + + + + + + + System.Object + + + + + + + + + + + -------------------------- EXAMPLE 1 -------------------------- + + PS C:\> + + New-VmfsDatastoreIncrease -Datastore MyDS -Expand + + The capacity of the Datastore, named MyDS, will be Expanded with all available free space on the first extent of the Datastore. + + + + -------------------------- EXAMPLE 2 -------------------------- + + PS C:\> + + New-VmfsDatastoreIncrease -Name MyDS -Expand -IncreaseSizeGB 25 + + The capacity of the Datastore, named MyDS, will be Expanded with 25GB on the first extent of the Datastore. +Provided if course, this amount of free space is available. + + + + -------------------------- EXAMPLE 3 -------------------------- + + PS C:\> + + New-VmfsDatastoreIncrease -Datastore 'TestDS' -Expand -IncreaseSizeGB 15 -CanonicalName 'naa.600507680180732f1800000000000011' + + The capacity of the Datastore MyDS will be increased with 15GB on the extent with the Canonicalname naa.600507680180732f1800000000000011 + + + + -------------------------- EXAMPLE 4 -------------------------- + + PS C:\> + + New-VmfsDatastoreIncrease -Datastore MyDS -Expand -CanonicalName 'naa.600507680180732f1800000000000012' + + The capacity of the Datastore MyDS will be increased with all available free space on the extent with the Canonicalname naa.600507680180732f1800000000000012 + + + + -------------------------- EXAMPLE 5 -------------------------- + + PS C:\> + + New-VmfsDatastoreIncrease -Datastore MyDS -Extend + + A new Extent will be added to Datastore MyDS. +All available free space of the LUN will be allocated. +The available LUNs are ordered alphanumerically by their Canonicalname, and the first LUN is used. + + + + -------------------------- EXAMPLE 6 -------------------------- + + PS C:\> + + Get-Datastore -Name MyDS | New-VmfsDatastoreIncrease -Extend -IncreaseSizeGB 50 + + The capacity of the Datastore returned by the PowerCLI Get-Datastore cmdlet will be increased by 50GB. +This is done by adding a new Extent to the Datastore. +The available LUNs are ordered alphanumerically by their Canonicalname, and the first LUN is used. + + + + + + \ No newline at end of file From 48c4839be314f9c138d2e369b08c850afbafe736 Mon Sep 17 00:00:00 2001 From: aaronwsmith Date: Wed, 10 Aug 2016 14:43:20 -0500 Subject: [PATCH 07/21] VM-Snapshot-Report.ps1, Version 1.0 Ready for merge if accepted. PowerActions Report Script that reports on VMs with snapshots along with their description, size of the snapshot in GB, the VM's provisioned vs. used space in GB, if the snapshot is the current one being used, its parent snapshot (if there is one), and the Power state of the VM itself. VM object is key (as it's the first managed object in the output), enabling you the ability to right-click an entry in the report to edit the target VM. --- PowerActions/VM-Snapshot-Report.ps1 | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 PowerActions/VM-Snapshot-Report.ps1 diff --git a/PowerActions/VM-Snapshot-Report.ps1 b/PowerActions/VM-Snapshot-Report.ps1 new file mode 100644 index 0000000..89f5453 --- /dev/null +++ b/PowerActions/VM-Snapshot-Report.ps1 @@ -0,0 +1,46 @@ +<# +.MYNGC_REPORT +KEY\(VM\) +.LABEL +VM Snapshot Report +.DESCRIPTION +PowerActions Report Script that reports on VMs with snapshots along with their description, size of the snapshot in GB, the VM's provisioned vs. used space in GB, +if the snapshot is the current one being used, its parent snapshot (if there is one), and the Power state of the VM itself. VM object is key (as it's the first +managed object in the output), enabling you the ability to right-click an entry in the report to edit the target VM. Version 1.0, written by Aaron Smith (@awsmith99), +published 08/10/2016. +#> + +param +( + [Parameter(Mandatory=$true)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster] + $vParam +); + +[Array] $vmList = @( Get-VM -Location $vParam | Sort Name ); + +foreach ( $vmItem in $vmList ) +{ + [Array] $vmSnapshotList = @( Get-Snapshot -VM $vmItem ); + + foreach ( $snapshotItem in $vmSnapshotList ) + { + $vmProvisionedSpaceGB = [Math]::Round( $vmItem.ProvisionedSpaceGB, 2 ); + $vmUsedSpaceGB = [Math]::Round( $vmItem.UsedSpaceGB, 2 ); + $snapshotSizeGB = [Math]::Round( $snapshotItem.SizeGB, 2 ); + + $output = New-Object -TypeName PSObject; + + $output | Add-Member -MemberType NoteProperty -Name "VM" -Value $vmItem; + $output | Add-Member -MemberType NoteProperty -Name "Name" -Value $snapshotItem.Name; + $output | Add-Member -MemberType NoteProperty -Name "Description" -Value $snapshotItem.Description; + $output | Add-Member -MemberType NoteProperty -Name "ParentSnapshot" -Value $snapshotItem.ParentSnapshot.Name; + $output | Add-Member -MemberType NoteProperty -Name "IsCurrentSnapshot" -Value $snapshotItem.IsCurrent; + $output | Add-Member -MemberType NoteProperty -Name "SnapshotSizeGB" -Value $snapshotSizeGB; + $output | Add-Member -MemberType NoteProperty -Name "ProvisionedSpaceGB" -Value $vmProvisionedSpaceGB; + $output | Add-Member -MemberType NoteProperty -Name "UsedSpaceGB" -Value $vmUsedSpaceGB; + $output | Add-Member -MemberType NoteProperty -Name "PowerState" -Value $snapshotItem.PowerState; + + $output; + } +} \ No newline at end of file From 067de29b3054eda53eeab61ae73ba00897ee5263 Mon Sep 17 00:00:00 2001 From: aaronwsmith Date: Wed, 10 Aug 2016 15:06:27 -0500 Subject: [PATCH 08/21] VM-Snapshot-Report.ps1 Ready for merge if approved ... added date created and calculated age in days for the snapshot as additional columns in the report. --- PowerActions/VM-Snapshot-Report.ps1 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/PowerActions/VM-Snapshot-Report.ps1 b/PowerActions/VM-Snapshot-Report.ps1 index 89f5453..cad9312 100644 --- a/PowerActions/VM-Snapshot-Report.ps1 +++ b/PowerActions/VM-Snapshot-Report.ps1 @@ -4,10 +4,10 @@ KEY\(VM\) .LABEL VM Snapshot Report .DESCRIPTION -PowerActions Report Script that reports on VMs with snapshots along with their description, size of the snapshot in GB, the VM's provisioned vs. used space in GB, -if the snapshot is the current one being used, its parent snapshot (if there is one), and the Power state of the VM itself. VM object is key (as it's the first -managed object in the output), enabling you the ability to right-click an entry in the report to edit the target VM. Version 1.0, written by Aaron Smith (@awsmith99), -published 08/10/2016. +PowerActions Report Script that reports on VMs with snapshots along with their description, date of the snapshot, age in days of the snapshot, size of the snapshot in GB, +the VM's provisioned vs. used space in GB, if the snapshot is the current one being used, its parent snapshot (if there is one), and the Power state of the VM itself. VM +object is key (as it's the first managed object in the output), enabling you the ability to right-click an entry in the report to edit the target VM. Version 1.0, written +by Aaron Smith (@awsmith99), published 08/10/2016. #> param @@ -28,12 +28,15 @@ foreach ( $vmItem in $vmList ) $vmProvisionedSpaceGB = [Math]::Round( $vmItem.ProvisionedSpaceGB, 2 ); $vmUsedSpaceGB = [Math]::Round( $vmItem.UsedSpaceGB, 2 ); $snapshotSizeGB = [Math]::Round( $snapshotItem.SizeGB, 2 ); + $snapshotAgeDays = ((Get-Date) - $snapshotItem.Created).Days; $output = New-Object -TypeName PSObject; $output | Add-Member -MemberType NoteProperty -Name "VM" -Value $vmItem; $output | Add-Member -MemberType NoteProperty -Name "Name" -Value $snapshotItem.Name; $output | Add-Member -MemberType NoteProperty -Name "Description" -Value $snapshotItem.Description; + $output | Add-Member -MemberType NoteProperty -Name "Created" -Value $snapshotItem.Created; + $output | Add-Member -MemberType NoteProperty -Name "AgeDays" -Value $snapshotAgeDays; $output | Add-Member -MemberType NoteProperty -Name "ParentSnapshot" -Value $snapshotItem.ParentSnapshot.Name; $output | Add-Member -MemberType NoteProperty -Name "IsCurrentSnapshot" -Value $snapshotItem.IsCurrent; $output | Add-Member -MemberType NoteProperty -Name "SnapshotSizeGB" -Value $snapshotSizeGB; From 19ff0ea26ac8513b1582dcb5b7fd4ff0fd5ca877 Mon Sep 17 00:00:00 2001 From: equelin Date: Tue, 11 Oct 2016 16:50:15 +0200 Subject: [PATCH 09/21] Add enhancements to module VMCPFunctions --- Modules/VMCPFunctions.psm1 | 400 +++++++++++++++++++++---------------- 1 file changed, 233 insertions(+), 167 deletions(-) diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 index ada8b23..e90174f 100644 --- a/Modules/VMCPFunctions.psm1 +++ b/Modules/VMCPFunctions.psm1 @@ -7,58 +7,90 @@ Twitter: @vBrianGraf VMware Blog: blogs.vmware.com/powercli Personal Blog: www.vtagion.com + + Modified on: 10/11/2016 + Modified by: Erwan Quélin + Twitter: @erwanquelin + Github: https://github.com/equelin =========================================================================== .DESCRIPTION - This function will allow users to view the VMCP settings for their clusters + This function will allow users to view the VMCP settings for their clusters - .Example - # This will show you the VMCP settings of your cluster + .PARAMETER Cluster + Cluster Name or Object + + .PARAMETER Server + vCenter server object + + .EXAMPLE + Get-VMCPSettings + + This will show you the VMCP settings for all the clusters + + .EXAMPLE Get-VMCPSettings -cluster LAB-CL - .Example - # This will show you the VMCP settings of your cluster - Get-VMCPSettings -cluster (Get-Cluster Lab-CL) + This will show you the VMCP settings of your cluster + + .EXAMPLE + Get-VMCPSettings -cluster (Get-Cluster Lab-CL) + + This will show you the VMCP settings of your cluster + + .EXAMPLE + Get-Cluster | Get-VMCPSettings + + This will show you the VMCP settings for all the clusters #> -[CmdletBinding()] - param - ( - [Parameter(Mandatory=$True, - ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True, - HelpMessage='What is the Cluster Name?')] - $cluster - ) - Begin { - # Determine input and convert to ClusterImpl object - Switch ($cluster.GetType().Name) - { - "string" {$CL = Get-Cluster $cluster} - "ClusterImpl" {$CL = $cluster} + [CmdletBinding()] + param + ( + [Parameter(Mandatory=$False, + ValueFromPipeline=$True, + ValueFromPipelineByPropertyName=$True, + HelpMessage='What is the Cluster Name?')] + $cluster = (Get-Cluster -Server $Server), + + [Parameter(Mandatory=$False)] + [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers + ) + + Process { + + Foreach ($Clus in $Cluster) { + + Write-Verbose "Processing Cluster $($Clus.Name)" + + # Determine input and convert to ClusterImpl object + Switch ($Clus.GetType().Name) + { + "string" {$CL = Get-Cluster $Clus -Server $Server -ErrorAction SilentlyContinue} + "ClusterImpl" {$CL = $Clus} + } + + If ($CL) { + # Work with the Cluster View + $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server + + # Create Hashtable with desired properties to return + $properties = [ordered]@{ + 'Cluster' = $ClusterMod.Name; + 'VMCP Status' = $clustermod.Configuration.DasConfig.VmComponentProtecting; + 'Protection For APD' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD; + 'APD Timeout Enabled' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.EnableAPDTimeoutForHosts; + 'APD Timeout (Seconds)' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmTerminateDelayForAPDSec; + 'Reaction on APD Cleared' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared; + 'Protection For PDL' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL + } + + # Create PSObject with the Hashtable + $object = New-Object -TypeName PSObject -Prop $properties + + # Show object + $object + } } - } - Process { - # Work with the Cluster View - $ClusterMod = Get-View -Id "ClusterComputeResource-$($cl.ExtensionData.MoRef.Value)" - - # Create Hashtable with desired properties to return - $properties = [ordered]@{ - 'Cluster' = $ClusterMod.Name; - 'VMCP Status' = $clustermod.Configuration.DasConfig.VmComponentProtecting; - 'Protection For APD' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD; - 'APD Timeout Enabled' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.EnableAPDTimeoutForHosts; - 'APD Timeout (Seconds)' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmTerminateDelayForAPDSec; - 'Reaction on APD Cleared' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared; - 'Protection For PDL' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL - } - - # Create PSObject with the Hashtable - $object = New-Object -TypeName PSObject -Prop $properties - - # Show object - return $object - } - End {} - + } } function Set-VMCPSettings { @@ -70,151 +102,185 @@ function Set-VMCPSettings { Twitter: @vBrianGraf VMware Blog: blogs.vmware.com/powercli Personal Blog: www.vtagion.com + + Modified on: 10/11/2016 + Modified by: Erwan Quélin + Twitter: @erwanquelin + Github: https://github.com/equelin =========================================================================== .DESCRIPTION - This function will allow users to enable/disable VMCP and also allow + This function will allow users to enable/disable VMCP and also allow them to configure the additional VMCP settings For each parameter, users should use the 'Tab' button to auto-fill the possible values. - .Example - # This will enable VMCP and configure the Settings + .PARAMETER Cluster + Cluster Name or Object + + .PARAMETER enableVMCP + Enable or disable VMCP + + .PARAMETER VmStorageProtectionForPDL + VM Storage Protection for PDL settings. Might be: + - disabled + - warning + - restartAggressive + + .PARAMETER VmStorageProtectionForAPD + VM Storage Protection for APD settings. Might be: + - disabled + - restartConservative + - restartAggressive + - warning + + .PARAMETER VmTerminateDelayForAPDSec + VM Terminate Delay for APD (seconds). + + .PARAMETER VmReactionOnAPDCleared + VM reaction on APD Cleared. Might be: + - reset + - none + + .PARAMETER Server + vCenter server object + + .EXAMPLE Set-VMCPSettings -cluster LAB-CL -enableVMCP:$True -VmStorageProtectionForPDL ` restartAggressive -VmStorageProtectionForAPD restartAggressive ` -VmTerminateDelayForAPDSec 2000 -VmReactionOnAPDCleared reset - .Example - # This will disable VMCP and configure the Settings + This will enable VMCP and configure the Settings on cluster LAB-CL + + .EXAMPLE Set-VMCPSettings -cluster LAB-CL -enableVMCP:$False -VmStorageProtectionForPDL ` disabled -VmStorageProtectionForAPD disabled ` -VmTerminateDelayForAPDSec 600 -VmReactionOnAPDCleared none + + This will disable VMCP and configure the Settings on cluster LAB-CL + + .EXAMPLE + Set-VMCPSettings -enableVMCP:$False -VmStorageProtectionForPDL ` + disabled -VmStorageProtectionForAPD disabled ` + -VmTerminateDelayForAPDSec 600 -VmReactionOnAPDCleared none + + This will disable VMCP and configure the Settings on all clusters available #> - [CmdletBinding()] - param - ( - [Parameter(Mandatory=$True, - ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True, - HelpMessage='What is the Cluster Name?')] - $cluster, - - [Parameter(Mandatory=$True, - ValueFromPipeline=$False, - HelpMessage='True=Enabled False=Disabled')] - [switch]$enableVMCP, + [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")] + param + ( + [Parameter(Mandatory=$True, + ValueFromPipeline=$True, + ValueFromPipelineByPropertyName=$True, + HelpMessage='What is the Cluster Name?')] + $cluster= (Get-Cluster -Server $Server), + + [Parameter(Mandatory=$True, + ValueFromPipeline=$False, + HelpMessage='True=Enabled False=Disabled')] + [switch]$enableVMCP, - [Parameter(Mandatory=$True, - ValueFromPipeline=$False, - HelpMessage='Actions that can be taken in response to a PDL event')] - [ValidateSet("disabled","warning","restartAggressive")] - [string]$VmStorageProtectionForPDL, - - [Parameter(Mandatory=$True, - ValueFromPipeline=$False, - HelpMessage='Options available for an APD response')] - [ValidateSet("disabled","restartConservative","restartAggressive","warning")] - [string]$VmStorageProtectionForAPD, - - [Parameter(Mandatory=$True, - ValueFromPipeline=$False, - HelpMessage='Value in seconds')] - [Int]$VmTerminateDelayForAPDSec, - - [Parameter(Mandatory=$True, - ValueFromPipeline=$False, - HelpMessage='This setting will instruct vSphere HA to take a certain action if an APD event is cleared')] - [ValidateSet("reset","none")] - [string]$VmReactionOnAPDCleared + [Parameter(Mandatory=$True, + ValueFromPipeline=$False, + HelpMessage='Actions that can be taken in response to a PDL event')] + [ValidateSet("disabled","warning","restartAggressive")] + [string]$VmStorageProtectionForPDL, + + [Parameter(Mandatory=$True, + ValueFromPipeline=$False, + HelpMessage='Options available for an APD response')] + [ValidateSet("disabled","restartConservative","restartAggressive","warning")] + [string]$VmStorageProtectionForAPD, + + [Parameter(Mandatory=$True, + ValueFromPipeline=$False, + HelpMessage='Value in seconds')] + [Int]$VmTerminateDelayForAPDSec, + + [Parameter(Mandatory=$True, + ValueFromPipeline=$False, + HelpMessage='This setting will instruct vSphere HA to take a certain action if an APD event is cleared')] + [ValidateSet("reset","none")] + [string]$VmReactionOnAPDCleared, + + [Parameter(Mandatory=$False)] + [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers + ) - ) -Begin{ + Process { - # Determine input and convert to ClusterImpl object - Switch ($cluster.GetType().Name) - { - "string" {$CL = Get-Cluster $cluster} - "ClusterImpl" {$CL = $cluster} - } -} -Process{ - # Create the object we will configure - $settings = New-Object VMware.Vim.ClusterConfigSpecEx - $settings.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo - - # Based on $enableVMCP switch - if ($enableVMCP -eq $false) { - $settings.dasConfig.vmComponentProtecting = "disabled" - } - elseif ($enableVMCP -eq $true) { - $settings.dasConfig.vmComponentProtecting = "enabled" - } + Foreach ($Clus in $Cluster) { - #Create the VMCP object to work with - $settings.dasConfig.defaultVmSettings = New-Object VMware.Vim.ClusterDasVmSettings - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings = New-Object VMware.Vim.ClusterVmComponentProtectionSettings - - #Storage Protection For PDL - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = "$VmStorageProtectionForPDL" - - #Storage Protection for APD - switch ($VmStorageProtectionForAPD) { - "disabled" { - # If Disabled, there is no need to set the Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'disabled' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } - - "restartConservative" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartConservative' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec - } - - "restartAggressive" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartAggressive' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec - } - - "warning" { - # If Warning, there is no need to set the Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'warning' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } + Write-Verbose "Processing Cluster $Clus" + # Determine input and convert to ClusterImpl object + Switch ($Clus.GetType().Name) + { + "string" {$CL = Get-Cluster $Clus -Server $Server -ErrorAction SilentlyContinue} + "ClusterImpl" {$CL = $Clus} } - - # Reaction On APD Cleared - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" - # Execute API Call - $modify = $true - $ClusterMod = Get-View -Id "ClusterComputeResource-$($cl.ExtensionData.MoRef.Value)" - $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) | out-null - + If ($CL) { + # Create the object we will configure + $settings = New-Object VMware.Vim.ClusterConfigSpecEx + $settings.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo + + # Based on $enableVMCP switch + if ($enableVMCP -eq $false) { + $settings.dasConfig.vmComponentProtecting = "disabled" + } + elseif ($enableVMCP -eq $true) { + $settings.dasConfig.vmComponentProtecting = "enabled" + } + #Create the VMCP object to work with + $settings.dasConfig.defaultVmSettings = New-Object VMware.Vim.ClusterDasVmSettings + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings = New-Object VMware.Vim.ClusterVmComponentProtectionSettings -} -End{ - # Update variable data after API call - $ClusterMod.updateViewData() + #Storage Protection For PDL + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = "$VmStorageProtectionForPDL" - # Create Hashtable with desired properties to return - $properties = [ordered]@{ - 'Cluster' = $ClusterMod.Name; - 'VMCP Status' = $clustermod.Configuration.DasConfig.VmComponentProtecting; - 'Protection For APD' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD; - 'APD Timeout Enabled' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.EnableAPDTimeoutForHosts; - 'APD Timeout (Seconds)' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmTerminateDelayForAPDSec; - 'Reaction on APD Cleared' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared; - 'Protection For PDL' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL + #Storage Protection for APD + switch ($VmStorageProtectionForAPD) { + "disabled" { + # If Disabled, there is no need to set the Timeout Value + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'disabled' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false + } + + "restartConservative" { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartConservative' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec + } + + "restartAggressive" { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartAggressive' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec + } + + "warning" { + # If Warning, there is no need to set the Timeout Value + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'warning' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false + } + + } + + # Reaction On APD Cleared + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" + + # Execute API Call + $modify = $true + $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server + + If ($pscmdlet.ShouldProcess($CL.Name,"Modify VMCP configuration")) { + $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) | out-null + } + + # Show result + Get-VMCPSettings -Cluster $CL -Server $Server + } + } } - - # Create PSObject with the Hashtable - $object = New-Object -TypeName PSObject -Prop $properties - - # Show object - return $object - -} } From 4b8e5dc46ff6c1171ed761a898cce14392cf42c3 Mon Sep 17 00:00:00 2001 From: equelin Date: Tue, 11 Oct 2016 16:56:31 +0200 Subject: [PATCH 10/21] Fix bug in Set-VMCPSettings --- Modules/VMCPFunctions.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 index e90174f..34382e2 100644 --- a/Modules/VMCPFunctions.psm1 +++ b/Modules/VMCPFunctions.psm1 @@ -168,7 +168,7 @@ function Set-VMCPSettings { [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")] param ( - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$false, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage='What is the Cluster Name?')] From 2a96855d030efdb3ce5ddd12e5133305af776fe2 Mon Sep 17 00:00:00 2001 From: equelin Date: Mon, 14 Nov 2016 17:22:13 +0100 Subject: [PATCH 11/21] Cluster need to be provided by the user + No need anymore to provide all parameters + Add timer to wait the end of the execution of the reconfiguration task before showing the result --- Modules/VMCPFunctions.psm1 | 120 ++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 42 deletions(-) diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 index 34382e2..921e8e4 100644 --- a/Modules/VMCPFunctions.psm1 +++ b/Modules/VMCPFunctions.psm1 @@ -168,35 +168,35 @@ function Set-VMCPSettings { [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")] param ( - [Parameter(Mandatory=$false, + [Parameter(Mandatory=$true, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, HelpMessage='What is the Cluster Name?')] - $cluster= (Get-Cluster -Server $Server), + $cluster, - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, - HelpMessage='True=Enabled False=Disabled')] - [switch]$enableVMCP, + HelpMessage='$True=Enabled $False=Disabled')] + [bool]$enableVMCP, - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage='Actions that can be taken in response to a PDL event')] [ValidateSet("disabled","warning","restartAggressive")] [string]$VmStorageProtectionForPDL, - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage='Options available for an APD response')] [ValidateSet("disabled","restartConservative","restartAggressive","warning")] [string]$VmStorageProtectionForAPD, - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage='Value in seconds')] [Int]$VmTerminateDelayForAPDSec, - [Parameter(Mandatory=$True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage='This setting will instruct vSphere HA to take a certain action if an APD event is cleared')] [ValidateSet("reset","none")] @@ -217,9 +217,18 @@ function Set-VMCPSettings { { "string" {$CL = Get-Cluster $Clus -Server $Server -ErrorAction SilentlyContinue} "ClusterImpl" {$CL = $Clus} + default {Throw 'Please provide a cluster name or object'} } If ($CL) { + + # Get the actual configuration of the Cluster + $ActualSettings = Get-VMCPSettings -Cluster $CL -Server $Server + + # Show actual settings in the verbose mode + Write-Verbose "[$($CL.Name)] Actual VMCP settings " + Write-Verbose $ActualSettings + # Create the object we will configure $settings = New-Object VMware.Vim.ClusterConfigSpecEx $settings.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo @@ -237,49 +246,76 @@ function Set-VMCPSettings { $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings = New-Object VMware.Vim.ClusterVmComponentProtectionSettings #Storage Protection For PDL - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = "$VmStorageProtectionForPDL" + If ($PSBoundParameters.ContainsKey('VmStorageProtectionForPDL')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $VmStorageProtectionForPDL + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $ActualSettings.'Protection For PDL' + } #Storage Protection for APD - switch ($VmStorageProtectionForAPD) { - "disabled" { - # If Disabled, there is no need to set the Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'disabled' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } - - "restartConservative" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartConservative' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec - } - - "restartAggressive" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartAggressive' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec - } - - "warning" { - # If Warning, there is no need to set the Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'warning' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } - + If ($PSBoundParameters.ContainsKey('VmStorageProtectionForAPD')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $VmStorageProtectionForAPD + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $ActualSettings.'Protection For APD' } + #Storage Protection for APD + If ($PSBoundParameters.ContainsKey('VmStorageProtectionForAPD')) { + switch ($VmStorageProtectionForAPD) { + "disabled" { + # If Disabled, there is no need to set enable Timeout Value + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'disabled' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false + } + + "restartConservative" { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartConservative' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true + } + + "restartAggressive" { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartAggressive' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true + } + + "warning" { + # If Warning, there is no need to enable the Timeout Value + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'warning' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false + } + } + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $ActualSettings.'Protection For APD' + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $ActualSettings.'APD Timeout Enabled' + } + + #APD Timeout Enabled + If ($PSBoundParameters.ContainsKey('VmTerminateDelayForAPDSec')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $ActualSettings.'APD Timeout (Seconds)' + } + # Reaction On APD Cleared - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" + If ($PSBoundParameters.ContainsKey('VmReactionOnAPDCleared')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = $ActualSettings.'Reaction on APD Cleared' + } # Execute API Call - $modify = $true - $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server - If ($pscmdlet.ShouldProcess($CL.Name,"Modify VMCP configuration")) { - $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) | out-null + $modify = $true + $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server + $Task = $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) } - # Show result - Get-VMCPSettings -Cluster $CL -Server $Server + # Wait for the reconfiguration task to finish to show the result + If ($Task) { + $TaskID = "Task-" + $($Task.Value) + Get-Task -Id $TaskID | Wait-Task | Out-Null + Get-VMCPSettings -Cluster $CL -Server $Server + } } } } From 758b80f6b8f3eaefe2522b4775d1e07782ded7b2 Mon Sep 17 00:00:00 2001 From: equelin Date: Mon, 14 Nov 2016 17:39:32 +0100 Subject: [PATCH 12/21] -server parameter was missing for the wait-task function --- Modules/VMCPFunctions.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 index 921e8e4..4f9b16e 100644 --- a/Modules/VMCPFunctions.psm1 +++ b/Modules/VMCPFunctions.psm1 @@ -313,7 +313,7 @@ function Set-VMCPSettings { # Wait for the reconfiguration task to finish to show the result If ($Task) { $TaskID = "Task-" + $($Task.Value) - Get-Task -Id $TaskID | Wait-Task | Out-Null + Get-Task -Id $TaskID -Server $Server | Wait-Task | Out-Null Get-VMCPSettings -Cluster $CL -Server $Server } } From 5d475bbd1be456bd26ff9e67b4cf40b330602cae Mon Sep 17 00:00:00 2001 From: Brian Graf Date: Fri, 18 Nov 2016 08:33:35 -0700 Subject: [PATCH 13/21] Initial Commit Backup-VCSA 6.5 --- Modules/Backup-VCSA.psm1 | 201 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 Modules/Backup-VCSA.psm1 diff --git a/Modules/Backup-VCSA.psm1 b/Modules/Backup-VCSA.psm1 new file mode 100644 index 0000000..21e036d --- /dev/null +++ b/Modules/Backup-VCSA.psm1 @@ -0,0 +1,201 @@ +Function Backup-VCSAToFile { +<# + .NOTES + =========================================================================== + Created by: Brian Graf + Date: October 30, 2016 + Organization: VMware + Blog: www.vtagion.com + Twitter: @vBrianGraf + =========================================================================== + + .SYNOPSIS + This function will allow you to create a full or partial backup of your + VCSA appliance. (vSphere 6.5 and higher) + + .DESCRIPTION + Use this function to backup your VCSA to a remote location + + .EXAMPLE + [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$BackupPassword = "VMw@re123" + $Comment = "First API Backup" + $LocationType = "FTP" + $location = "10.144.99.5/vcsabackup-$((Get-Date).ToString('yyyy-MM-dd-hh-mm'))" + $LocationUser = "admin" + [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$locationPassword = "VMw@re123" + PS C:\> Backup-VCSAToFile -BackupPassword $BackupPassword -LocationType $LocationType -Location $location -LocationUser $LocationUser -LocationPassword $locationPassword -Comment "This is a demo" -ShowProgress -FullBackup + + + .NOTES + You must be connected to the CisService for this to work, if you are not connected, the function will prompt you for your credentials + If a -LocationType is not chosen, the function will default to FTP. + The destination location for a backup must be an empty folder (easiest to use the get-date cmdlet in the location) + -ShowProgress will give you a progressbar as well as updates in the console + -SeatBackup will only backup the config whereas -Fullbackup grabs the historical data as well +#> + param ( + [Parameter(ParameterSetName=’FullBackup’)] + [switch]$FullBackup, + [Parameter(ParameterSetName=’SeatBackup’)] + [switch]$SeatBackup, + [ValidateSet('FTPS', 'HTTP', 'SCP', 'HTTPS', 'FTP')] + $LocationType = "FTP", + $Location, + $LocationUser, + [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$LocationPassword, + [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$BackupPassword, + $Comment = "Backup job", + [switch]$ShowProgress + ) + Begin { + if (!($global:DefaultCisServers)){ + [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null + $Connection = Connect-CisServer $global:DefaultVIServer + } else { + $Connection = $global:DefaultCisServers + } + if ($FullBackup) {$parts = @("common","seat")} + if ($SeatBackup) {$parts = @("seat")} + } + Process{ + $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job + $CreateSpec = $BackupAPI.Help.create.piece.CreateExample() + $CreateSpec.parts = $parts + $CreateSpec.backup_password = $BackupPassword + $CreateSpec.location_type = $LocationType + $CreateSpec.location = $Location + $CreateSpec.location_user = $LocationUser + $CreateSpec.location_password = $LocationPassword + $CreateSpec.comment = $Comment + try { + $BackupJob = $BackupAPI.create($CreateSpec) + } + catch { + Write-Error $Error[0].exception.Message + } + + + If ($ShowProgress){ + do { + $BackupAPI.get("$($BackupJob.ID)") | select id, progress, state + $progress = ($BackupAPI.get("$($BackupJob.ID)").progress) + Write-Progress -Activity "Backing up VCSA" -Status $BackupAPI.get("$($BackupJob.ID)").state -PercentComplete ($BackupAPI.get("$($BackupJob.ID)").progress) -CurrentOperation "$progress% Complete" + start-sleep -seconds 5 + } until ($BackupAPI.get("$($BackupJob.ID)").progress -eq 100 -or $BackupAPI.get("$($BackupJob.ID)").state -ne "INPROGRESS") + + $BackupAPI.get("$($BackupJob.ID)") | select id, progress, state + } + Else { + $BackupJob | select id, progress, state + } + } + End {} +} + +Function Get-VCSABackupJobs { +<# + .NOTES + =========================================================================== + Created by: Brian Graf + Date: October 30, 2016 + Organization: VMware + Blog: www.vtagion.com + Twitter: @vBrianGraf + =========================================================================== + + .SYNOPSIS + Get-VCSABackupJobs returns a list of all backup jobs VCSA has ever performed (vSphere 6.5 and higher) + + .DESCRIPTION + Get-VCSABackupJobs returns a list of all backup jobs VCSA has ever performed + + .EXAMPLE + PS C:\> Get-VCSABackupJobs + + .NOTES + The values returned are read as follows: + YYYYMMDD-hhmmss-vcsabuildnumber + You can pipe the results of this function into the Get-VCSABackupStatus function + Get-VCSABackupJobs | select -First 1 | Get-VCSABackupStatus <- Most recent backup +#> + param ( + [switch]$ShowNewest + ) + Begin { + if (!($global:DefaultCisServers)){ + [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null + $Connection = Connect-CisServer $global:DefaultVIServer + } else { + $Connection = $global:DefaultCisServers + } + } + Process{ + + $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job + + try { + if ($ShowNewest) { + $results = $BackupAPI.list() + $results[0] + } else { + $BackupAPI.list() + } + } + catch { + Write-Error $Error[0].exception.Message + } + + } + + End {} +} + +Function Get-VCSABackupStatus { +<# + .NOTES + =========================================================================== + Created by: Brian Graf + Date: October 30, 2016 + Organization: VMware + Blog: www.vtagion.com + Twitter: @vBrianGraf + =========================================================================== + + .SYNOPSIS + Returns the ID, Progress, and State of a VCSA backup (vSphere 6.5 and higher) + + .DESCRIPTION + Returns the ID, Progress, and State of a VCSA backup + + .EXAMPLE + PS C:\> $backups = Get-VCSABackupJobs + $backups[0] | Get-VCSABackupStatus + + .NOTES + The BackupID can be piped in from the Get-VCSABackupJobs function and can return multiple job statuses +#> + Param ( + [parameter(ValueFromPipeline=$True)] + [string[]]$BackupID + ) + Begin { + if (!($global:DefaultCisServers)){ + [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null + $Connection = Connect-CisServer $global:DefaultVIServer + } else { + $Connection = $global:DefaultCisServers + } + + $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job + } + Process{ + + foreach ($id in $BackupID) { + $BackupAPI.get("$id") | select id, progress, state + } + + + } + + End {} +} From 181add5bfd96db238a945642407bbbecdeba37e5 Mon Sep 17 00:00:00 2001 From: Brian Graf Date: Fri, 18 Nov 2016 09:15:43 -0700 Subject: [PATCH 14/21] Added Credit to Alan --- Modules/Backup-VCSA.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Backup-VCSA.psm1 b/Modules/Backup-VCSA.psm1 index 21e036d..271a7ff 100644 --- a/Modules/Backup-VCSA.psm1 +++ b/Modules/Backup-VCSA.psm1 @@ -27,6 +27,7 @@ .NOTES + Credit goes to @AlanRenouf for sharing the base of this function with me which I was able to take and make more robust as well as add in progress indicators You must be connected to the CisService for this to work, if you are not connected, the function will prompt you for your credentials If a -LocationType is not chosen, the function will default to FTP. The destination location for a backup must be an empty folder (easiest to use the get-date cmdlet in the location) From 2d594cb60d328ed9144d4071ea37b1c3bb40ddba Mon Sep 17 00:00:00 2001 From: William Lam Date: Sun, 20 Nov 2016 09:26:52 -0800 Subject: [PATCH 15/21] Adding several vSphere 6.5 API examples --- Scripts/AutomaticVMFSUnmap.ps1 | 68 ++++++++++++++++++++++++++++++++++ Scripts/ESXInstallDate.ps1 | 30 +++++++++++++++ Scripts/ESXInstalledVIBs.ps1 | 41 ++++++++++++++++++++ Scripts/SecureBoot.ps1 | 65 ++++++++++++++++++++++++++++++++ Scripts/vSphereLogins.ps1 | 29 +++++++++++++++ 5 files changed, 233 insertions(+) create mode 100755 Scripts/AutomaticVMFSUnmap.ps1 create mode 100755 Scripts/ESXInstallDate.ps1 create mode 100755 Scripts/ESXInstalledVIBs.ps1 create mode 100755 Scripts/SecureBoot.ps1 create mode 100755 Scripts/vSphereLogins.ps1 diff --git a/Scripts/AutomaticVMFSUnmap.ps1 b/Scripts/AutomaticVMFSUnmap.ps1 new file mode 100755 index 0000000..fa240d9 --- /dev/null +++ b/Scripts/AutomaticVMFSUnmap.ps1 @@ -0,0 +1,68 @@ +<# +.SYNOPSIS Retrieve the current VMFS Unmap priority for VMFS 6 datastore +.NOTES Author: William Lam +.NOTES Site: www.virtuallyghetto.com +.NOTES Reference: http://www.virtuallyghetto.com/2016/10/configure-new-automatic-space-reclamation-vmfs-unmap-using-vsphere-6-5-apis.html +.PARAMETER Datastore + VMFS 6 Datastore to enable or disable VMFS Unamp +.EXAMPLE + Get-Datastore "mini-local-datastore-hdd" | Get-VMFSUnmap +#> + +Function Get-VMFSUnmap { + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl]$Datastore + ) + + if($datastoreInfo -is [VMware.Vim.VmfsDatastoreInfo] -and $datastoreInfo.Vmfs.MajorVersion -eq 6) { + $datastoreInfo.Vmfs | select Name, UnmapPriority, UnmapGranularity + } else { + Write-Host "Not a VMFS Datastore and/or VMFS version is not 6.0" + } +} + +<# +.SYNOPSIS Configure the VMFS Unmap priority for VMFS 6 datastore +.NOTES Author: William Lam +.NOTES Site: www.virtuallyghetto.com +.NOTES Reference: http://www.virtuallyghetto.com/2016/10/configure-new-automatic-space-reclamation-vmfs-unmap-using-vsphere-6-5-apis.html +.PARAMETER Datastore + VMFS 6 Datastore to enable or disable VMFS Unamp +.EXAMPLE + Get-Datastore "mini-local-datastore-hdd" | Set-VMFSUnmap -Enabled $true +.EXAMPLE + Get-Datastore "mini-local-datastore-hdd" | Set-VMFSUnmap -Enabled $false +#> + +Function Set-VMFSUnmap { + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl]$Datastore, + [String]$Enabled + ) + + $vmhostView = ($Datastore | Get-VMHost).ExtensionData + $storageSystem = Get-View $vmhostView.ConfigManager.StorageSystem + + if($Enabled -eq $true) { + $enableUNMAP = "low" + $reconfigMessage = "Enabling Automatic VMFS Unmap for $Datastore" + } else { + $enableUNMAP = "none" + $reconfigMessage = "Disabling Automatic VMFS Unmap for $Datastore" + } + + $uuid = $datastore.ExtensionData.Info.Vmfs.Uuid + + Write-Host "$reconfigMessage ..." + $storageSystem.UpdateVmfsUnmapPriority($uuid,$enableUNMAP) +} \ No newline at end of file diff --git a/Scripts/ESXInstallDate.ps1 b/Scripts/ESXInstallDate.ps1 new file mode 100755 index 0000000..26a3edf --- /dev/null +++ b/Scripts/ESXInstallDate.ps1 @@ -0,0 +1,30 @@ +<# +.SYNOPSIS Retrieve the installation date of an ESXi host +.NOTES Author: William Lam +.NOTES Site: www.virtuallyghetto.com +.NOTES Reference: http://www.virtuallyghetto.com/2016/10/super-easy-way-of-getting-esxi-installation-date-in-vsphere-6-5.html +.PARAMETER Vmhost + ESXi host to query installation date +.EXAMPLE + Get-Vmhost "mini" | Get-ESXInstallDate +#> + +Function Get-ESXInstallDate { + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Vmhost + ) + + if($Vmhost.Version -eq "6.5.0") { + $imageManager = Get-View ($Vmhost.ExtensionData.ConfigManager.ImageConfigManager) + $installDate = $imageManager.installDate() + + Write-Host "$Vmhost was installed on $installDate" + } else { + Write-Host "ESXi must be running 6.5" + } +} diff --git a/Scripts/ESXInstalledVIBs.ps1 b/Scripts/ESXInstalledVIBs.ps1 new file mode 100755 index 0000000..c759f73 --- /dev/null +++ b/Scripts/ESXInstalledVIBs.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS Retrieve the installation date of an ESXi host +.NOTES Author: William Lam +.NOTES Site: www.virtuallyghetto.com +.PARAMETER Vmhost + ESXi host to query installed ESXi VIBs +.EXAMPLE + Get-ESXInstalledVibs -Vmhost (Get-Vmhost "mini") +.EXAMPLE + Get-ESXInstalledVibs -Vmhost (Get-Vmhost "mini") -vibname vsan +#> + +Function Get-ESXInstalledVibs { + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Vmhost, + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$vibname="" + ) + + $imageManager = Get-View ($Vmhost.ExtensionData.ConfigManager.ImageConfigManager) + $vibs = $imageManager.fetchSoftwarePackages() + + foreach ($vib in $vibs) { + if($vibname -ne "") { + if($vib.name -eq $vibname) { + return $vib | select Name, Version, Vendor, CreationDate, Summary + } + } else { + $vib | select Name, Version, Vendor, CreationDate, Summary + } + } +} diff --git a/Scripts/SecureBoot.ps1 b/Scripts/SecureBoot.ps1 new file mode 100755 index 0000000..dac71b8 --- /dev/null +++ b/Scripts/SecureBoot.ps1 @@ -0,0 +1,65 @@ +Function Get-SecureBoot { + <# + .SYNOPSIS Query Seure Boot setting for a VM in vSphere 6.5 + .NOTES Author: William Lam + .NOTES Site: www.virtuallyghetto.com + .PARAMETER Vm + VM to query Secure Boot setting + .EXAMPLE + Get-VM -Name Windows10 | Get-SecureBoot + #> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Vm + ) + + $secureBootSetting = if ($vm.ExtensionData.Config.BootOptions.EfiSecureBootEnabled) { "enabled" } else { "disabled" } + Write-Host "Secure Boot is" $secureBootSetting +} + +Function Set-SecureBoot { + <# + .SYNOPSIS Enable/Disable Seure Boot setting for a VM in vSphere 6.5 + .NOTES Author: William Lam + .NOTES Site: www.virtuallyghetto.com + .PARAMETER Vm + VM to enable/disable Secure Boot + .EXAMPLE + Get-VM -Name Windows10 | Set-SecureBoot -Enabled + .EXAMPLE + Get-VM -Name Windows10 | Set-SecureBoot -Disabled + #> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Vm, + [Switch]$Enabled, + [Switch]$Disabled + ) + + if($Enabled) { + $secureBootSetting = $true + $reconfigMessage = "Enabling Secure Boot for $Vm" + } + if($Disabled) { + $secureBootSetting = $false + $reconfigMessage = "Disabling Secure Boot for $Vm" + } + + $spec = New-Object VMware.Vim.VirtualMachineConfigSpec + $bootOptions = New-Object VMware.Vim.VirtualMachineBootOptions + $bootOptions.EfiSecureBootEnabled = $secureBootSetting + $spec.BootOptions = $bootOptions + + Write-Host "`n$reconfigMessage ..." + $task = $vm.ExtensionData.ReconfigVM_Task($spec) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null +} diff --git a/Scripts/vSphereLogins.ps1 b/Scripts/vSphereLogins.ps1 new file mode 100755 index 0000000..516e4c7 --- /dev/null +++ b/Scripts/vSphereLogins.ps1 @@ -0,0 +1,29 @@ +Function Get-vSphereLogins { + <# + .SYNOPSIS Retrieve information for all currently logged in vSphere Sessions (excluding current session) + .NOTES Author: William Lam + .NOTES Site: www.virtuallyghetto.com + .REFERENCE Blog: http://www.virtuallyghetto.com/2016/11/an-update-on-how-to-retrieve-useful-information-from-a-vsphere-login.html + .EXAMPLE + Get-vSphereLogins + #> + if($DefaultVIServers -eq $null) { + Write-Host "Error: Please connect to your vSphere environment" + exit + } + + # Using the first connection + $VCConnection = $DefaultVIServers[0] + + $sessionManager = Get-View ($VCConnection.ExtensionData.Content.SessionManager) + + # Store current session key + $currentSessionKey = $sessionManager.CurrentSession.Key + + foreach ($session in $sessionManager.SessionList) { + # Ignore vpxd-extension logins as well as the current session + if($session.UserName -notmatch "vpxd-extension" -and $session.key -ne $currentSessionKey) { + $session | Select Username, IpAddress, UserAgent, @{"Name"="APICount";Expression={$Session.CallCount}}, LoginTime + } + } +} From fed74d9e02879f4854847b6f283bdbd477c7f309 Mon Sep 17 00:00:00 2001 From: William Lam Date: Sun, 20 Nov 2016 12:53:42 -0800 Subject: [PATCH 16/21] Fixing VMFS UNMAP sample --- Scripts/AutomaticVMFSUnmap.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/AutomaticVMFSUnmap.ps1 b/Scripts/AutomaticVMFSUnmap.ps1 index fa240d9..2f32617 100755 --- a/Scripts/AutomaticVMFSUnmap.ps1 +++ b/Scripts/AutomaticVMFSUnmap.ps1 @@ -19,6 +19,8 @@ Function Get-VMFSUnmap { [VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl]$Datastore ) + $datastoreInfo = $Datastore.ExtensionData.Info + if($datastoreInfo -is [VMware.Vim.VmfsDatastoreInfo] -and $datastoreInfo.Vmfs.MajorVersion -eq 6) { $datastoreInfo.Vmfs | select Name, UnmapPriority, UnmapGranularity } else { @@ -65,4 +67,4 @@ Function Set-VMFSUnmap { Write-Host "$reconfigMessage ..." $storageSystem.UpdateVmfsUnmapPriority($uuid,$enableUNMAP) -} \ No newline at end of file +} From 7da4ea673baa4127af947ad6baa053970ecc0b2d Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Wed, 23 Nov 2016 09:45:05 -0500 Subject: [PATCH 17/21] Create Update_PowerCLI_Scripts.ps1 Created for use when migrating over to PowerCLI 6.5 Functions which can be used to detect where scripts (.ps1) are referencing either the PowerCLI Initialization script or Add-PSSnapin functions to start working with PowerCLI: Get-PowerCLIInitialization Get-PowerCLISnapinUse Function used to update the path to the Initialization script found by the above function: Update-PowerCLIInitialization Function used to change from adding in the PowerCLI snapins to now importing the PowerCLI modules: Update-PowerCLISnapinUse --- Scripts/Update_PowerCLI_Scripts.ps1 | 228 ++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 Scripts/Update_PowerCLI_Scripts.ps1 diff --git a/Scripts/Update_PowerCLI_Scripts.ps1 b/Scripts/Update_PowerCLI_Scripts.ps1 new file mode 100644 index 0000000..ec5e09a --- /dev/null +++ b/Scripts/Update_PowerCLI_Scripts.ps1 @@ -0,0 +1,228 @@ +function Get-PowerCLIInitialization { +<# +.SYNOPSIS + Gathers information on PowerShell resources which refer to the old PowerCLI Initialization Script +.DESCRIPTION + Will provide an inventory of scripts, modules, etc refering to the old vSphere PowerCLI directory +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Path + Directory path to be searched +.EXAMPLE + Get-PowerCLIInitialization -Path C:\Temp\Scripts + Gathers information from the 'C:\Temp\Scripts\' directory +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$false,Position=0,ValueFromPipelineByPropertyName=$true)] + [string]$Path + ) + + Process { + + #Validate whether a path was passed as a parameter or not + if (!$Path) {$FullName = (Get-Location).ProviderPath} + else { + + #Validate whether the path passed as a parameter was a literal path or not, then establish the FullName of the desired path + if ((Test-Path -Path $Path -ErrorAction SilentlyContinue) -eq $true -and $Path -is [System.IO.DirectoryInfo]) {$FullName = (Get-Item -Path $Path).FullName} + elseif ((Test-Path -Path $Path -ErrorAction SilentlyContinue) -eq $true -and $Path -isnot [System.IO.DirectoryInfo]) {$FullName = (Get-Item -LiteralPath $Path).FullName} + else { + $currdir = (Get-Location).ProviderPath + Write-Warning "No valid path found at - $currdir\$Path" + } + + } + + if ($FullName) { + + #Gather scripts using ps1 extension and have a string matching "\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1" + $scripts = Get-ChildItem -Path $FullName -Recurse -Filter *.ps1 -File -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Select-String -Pattern "\\VMware\\Infrastructure\\vSphere PowerCLI\\Scripts\\Initialize-PowerCLIEnvironment.ps1" + + #Create a report of neccessary output based on the scripts gathered above + $scriptreport = @() + foreach ($script in $scripts) { + + $singleitem = New-Object System.Object + $singleitem | Add-Member -Type NoteProperty -Name Filename -Value $script.Filename + $singleitem | Add-Member -Type NoteProperty -Name LineNumber -Value $script.LineNumber + $singleitem | Add-Member -Type NoteProperty -Name Path -Value $script.Path.TrimEnd($script.Filename) + $singleitem | Add-Member -Type NoteProperty -Name FullPath -Value $script.Path + $scriptreport += $singleitem + + } + return $scriptreport + + } + + } # End of process +} # End of function + +function Get-PowerCLISnapinUse { +<# +.SYNOPSIS + Gathers information on PowerShell resources which refer to the old PowerCLI Snapins +.DESCRIPTION + Will provide an inventory of scripts, modules, etc refering to the old PowerCLI Snapins +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Path + Directory path to be searched +.EXAMPLE + Get-PowerCLISnapinUse -Path C:\Temp\Scripts + Gathers information from the 'C:\Temp\Scripts\' directory +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$false,Position=0,ValueFromPipelineByPropertyName=$true)] + [string]$Path + ) + + Process { + + #Validate whether a path was passed as a parameter or not + if (!$Path) {$FullName = (Get-Location).ProviderPath} + else { + + #Validate whether the path passed as a parameter was a literal path or not, then establish the FullName of the desired path + if ((Test-Path -Path $Path -ErrorAction SilentlyContinue) -eq $true -and $Path -is [System.IO.DirectoryInfo]) {$FullName = (Get-Item -Path $Path).FullName} + elseif ((Test-Path -Path $Path -ErrorAction SilentlyContinue) -eq $true -and $Path -isnot [System.IO.DirectoryInfo]) {$FullName = (Get-Item -LiteralPath $Path).FullName} + else { + $currdir = (Get-Location).ProviderPath + Write-Warning "No valid path found at - $currdir\$Path" + } + + } + + if ($FullName) { + + #Gather scripts using ps1 extension and have a string matching "add-pssnapin" and "VMware." on the same line + $scripts = Get-ChildItem -Path $FullName -Recurse -Filter *.ps1 -File -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Select-String -Pattern "add-pssnapin" | Where-Object {$_ | Select-String -Pattern "VMware."} + + #Create a report of neccessary output based on the scripts gathered above + $scriptreport = @() + foreach ($script in $scripts) { + + $singleitem = New-Object System.Object + $singleitem | Add-Member -Type NoteProperty -Name Filename -Value $script.Filename + $singleitem | Add-Member -Type NoteProperty -Name LineNumber -Value $script.LineNumber + $singleitem | Add-Member -Type NoteProperty -Name Path -Value $script.Path.TrimEnd($script.Filename) + $singleitem | Add-Member -Type NoteProperty -Name FullPath -Value $script.Path + $scriptreport += $singleitem + + } + + return $scriptreport + + } + + } # End of process +} # End of function + +function Update-PowerCLIInitialization { +<# +.SYNOPSIS + Updates the information in PowerShell resources which refer to the old PowerCLI Initialization Script +.DESCRIPTION + Will update scripts, modules, etc refering to the old vSphere PowerCLI directory +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Path + Directory path to be searched +.EXAMPLE + Update-PowerCLIInitialization -Path C:\Temp\Scripts + Gathers information from the 'C:\Temp\Scripts\' directory +#> +[CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory=$false,Position=0,ValueFromPipelineByPropertyName=$true)] + [string]$Path + ) + + Process { + + #Gather scripts using ps1 extension and have a string matching "\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1" + $scripts = Get-PowerCLIInitialization -Path $Path + + #Check to see if any scripts are found + if (!$scripts) {Write-Warning "No PowerShell resources found requiring update within $Path"} + else { + + foreach ($script in $scripts) { + + #Finds and updates the string to the new location of the Initialize-PowerCLIEnvironment.ps1 script + (Get-Content $script.FullPath).Replace("\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1","\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1") | Set-Content $script.FullPath + + } + + } + + + } # End of process +} # End of function + +function Update-PowerCLISnapinUse { +<# +.SYNOPSIS + Updates the information in PowerShell resources which refer to the old PowerCLI Initialization Script +.DESCRIPTION + Will update scripts, modules, etc refering to the old vSphere PowerCLI directory +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Path + Directory path to be searched +.EXAMPLE + Update-PowerCLISnapinUse -Path C:\Temp\Scripts + Gathers information from the 'C:\Temp\Scripts\' directory +#> +[CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory=$false,Position=0,ValueFromPipelineByPropertyName=$true)] + [string]$Path + ) + + Process { + + #Gather scripts using ps1 extension and have a string matching "\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1" + $scripts = Get-PowerCLISnapinUse -Path $Path | select -Unique FullPath + + #Check to see if any scripts are found + if (!$scripts) {Write-Warning "No PowerShell resources found requiring update within $Path"} + else { + + foreach ($script in $scripts) { + + #Finds and updates the string to import the PowerCLI modules + $scriptcontent = (Get-Content $script.FullPath) + + $newoutput = @() + [int]$counter = 0 + foreach ($line in $scriptcontent) { + + #Checks to see if the line includes adding in the VMware PSSnapins and, if so, comments it out + #On first discovery, adds the invokation of get-module for the PowerCLI modules + if ($line -like "Add-PSSnapin*VMware*" -and $counter -eq 0) { + + $newoutput += "Get-Module -ListAvailable VMware* | Import-Module" + $newoutput += $line.Insert(0,'#') + $counter = 1 + + } + elseif ($line -like "Add-PSSnapin*VMware*" -and $counter -eq 1) { + + $newoutput += $line.Insert(0,'#') + + } + else {$newoutput += $line} + + } + + #Updates the script + Set-Content -Value $newoutput -Path $script.FullPath + + } + + } + + } # End of process +} # End of function From c2bd42eb6cec37f77031e49b0a69872e3ef79df3 Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Fri, 25 Nov 2016 14:58:24 +0100 Subject: [PATCH 18/21] function Recommend-Sizing This Function collects Basic vSphere Informations for a Hardware Sizing Recomamndation. Focus is in Compute Ressources. --- Modules/Recommend-Sizing.psm1 | 199 ++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 Modules/Recommend-Sizing.psm1 diff --git a/Modules/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing.psm1 new file mode 100644 index 0000000..8aea554 --- /dev/null +++ b/Modules/Recommend-Sizing.psm1 @@ -0,0 +1,199 @@ +function Recommend-Sizing { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.11 ver 1.0 Base Release + =========================================================================== + External Code Sources: + http://www.lucd.info/2011/04/22/get-the-maximum-iops/ + https://communities.vmware.com/thread/485386 + =========================================================================== + Tested Against Environment: + vSphere Version: 5.5 U2 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2012 R2 + =========================================================================== + Keywords vSphere, ESXi, VM, Storage, Sizing + =========================================================================== + + .DESCRIPTION + This Function collects Basic vSphere Informations for a Hardware Sizing Recomamndation. Focus is in Compute Ressources. + + .Example + Recommend-Sizing -ClusterNames Cluster01, Cluster02 -StatsRange 480 -Verbose + + .Example + Recommend-Sizing -ClusterNames Cluster01, Cluster02 + + .Example + Recommend-Sizing -ClusterNames Cluster01 + + .PARAMETER ClusterNames + List of your vSphere Cluser Names to process. + + .PARAMETER StatsRange + Time Range in Minutes for the Stats Collection. + Default is 24h. + +#Requires PS -Version 4.0 +#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] + [Array] $ClusterNames, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] + [int] $StatsRange = 1440 + +) +Begin { + [int]$TimeRange = "-" + $StatsRange + $Validate = $True + #region: Check Clusters + Write-Verbose "$(Get-Date -Format G) Starting Cluster Validation..." + foreach ($ClusterName in $ClusterNames) { + $TestCluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue -Verbose:$False + if(!($TestCluster)){ + Write-Warning "No Custer found wth Name $ClusterName!" + $Validate = $False + } + elseif ($TestCluster.count -gt 1) { + Write-Warning "Multiple Custers found wth Name $ClusterName!`nUse a List of explicit Cluster Names: Recommend-Sizing -ClusterNames Cluster01, Cluster02 " + $Validate = $False + } + } + Write-Verbose "$(Get-Date -Format G) Cluster Validation completed" + #endregion +} + +Process { + $MyView = @() + if ($Validate -eq $True) { + foreach ($ClusterName in $ClusterNames) { + #region: Get Cluster Objects + Write-Verbose "$(Get-Date -Format G) Collect $ClusterName Cluster Objects..." + $Cluster = Get-Cluster -Name $ClusterName -Verbose:$False + $ClusterVMs = $Cluster | Get-VM -Verbose:$False + $ClusterVMsPoweredOn = $ClusterVMs | where {$_.PowerState -eq "PoweredOn"} + $ClusterDatastores = $Cluster | Get-Datastore -Verbose:$False + $ClusterHosts = $Cluster | Get-VMHost -Verbose:$False + $HostsAverageMemoryUsageGB = [math]::round( ($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average,1 ) + $HostsAverageMemoryUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average / ($ClusterHosts | Measure-Object -Average -Property MemoryTotalGB).Average) * 100,1 )) + $HostsAverageCpuUsageMhz = [math]::round( ($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average,1 ) + $HostsAverageCpuUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average / ($ClusterHosts | Measure-Object -Average -Property CpuTotalMhz).Average) * 100,1 )) + Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Cluster Objects completed" + #endregion + + #region: CPU Calculation + Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details..." + $VMvCPUs = ($ClusterVMs | Measure-Object -Sum -Property NumCpu).sum + $LogicalThreads = $Cluster.ExtensionData.Summary.NumCpuThreads + $CpuCores = $Cluster.ExtensionData.Summary.NumCpuCores + $vCPUpCPUratio = [math]::round( $VMvCPUs / $LogicalThreads,1 ) + Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details completed." + #endregion + + #region: Memory Calculation + Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details..." + $AllocatedVMMemoryGB = [math]::round( ($ClusterVMs | Measure-Object -Sum -Property MemoryGB).sum ) + $PhysicalMemory = [math]::round( $Cluster.ExtensionData.Summary.TotalMemory / 1073741824,1 ) + $MemoryUsage = [math]::round( ($AllocatedVMMemoryGB / $PhysicalMemory) * 100 ,1 ) + Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details completed" + #endregion + + #region: Creating Disk Metrics + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics..." + $DiskMetrics = "virtualDisk.numberReadAveraged.average","virtualDisk.numberWriteAveraged.average" + $start = (Get-Date).AddMinutes($TimeRange) + $DiskStats = Get-Stat -Stat $DiskMetrics -Entity $ClusterVMsPoweredOn -Start $start -Verbose:$False + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics completed" + #endregion + + #region: Creating IOPS Reports + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report..." + $reportDiskPerf = @() + $reportDiskPerf = $DiskStats | Group-Object -Property {$_.Entity.Name},Instance | %{ + New-Object PSObject -Property @{ + IOPSMax = ($_.Group | ` + Group-Object -Property Timestamp | ` + %{$_.Group[0].Value + $_.Group[1].Value} | ` + Measure-Object -Maximum).Maximum + } + } + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report completed" + #endregion + + #region: Create VM Disk Space Report + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report..." + $reportDiskSpace = @() + foreach ($ClusterVM in $ClusterVMs){ + $VMDKs = $ClusterVM | get-HardDisk -Verbose:$False + foreach ($VMDK in $VMDKs) { + if ($VMDK -ne $null){ + [int]$CapacityGB = $VMDK.CapacityKB/1024/1024 + $Report = [PSCustomObject] @{ + CapacityGB = $CapacityGB + } + $reportDiskSpace += $Report + } + } + } + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report completed" + #endregion + + #region: Create Datastore Space Report + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report..." + $DatastoreReport = @($ClusterDatastores | Select-Object @{N="CapacityGB";E={[math]::Round($_.CapacityGB,2)}}, @{N="FreeSpaceGB";E={[math]::Round($_.FreeSpaceGB,2)}}, @{N="UsedSpaceGB";E={[math]::Round($_.CapacityGB - $_.FreeSpaceGB,2)}}) + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report completed" + #endregion + + #region: Create Global Report + Write-Verbose "$(Get-Date -Format G) Process Global Report..." + $SizingReport = [PSCustomObject] @{ + Cluster = $Cluster.name + HAEnabled = $Cluster.HAEnabled + DrsEnabled = $Cluster.DrsEnabled + Hosts = $Cluster.ExtensionData.Summary.NumHosts + HostsAverageMemoryUsageGB = $HostsAverageMemoryUsageGB + HostsAverageMemoryUsage = "$HostsAverageMemoryUsage %" + HostsAverageCpuUsageMhz = $HostsAverageCpuUsageMhz + HostsAverageCpuUsage = "$HostsAverageCpuUsage %" + PhysicalCPUCores = $CpuCores + LogicalCPUThreads = $LogicalThreads + VMs = $ClusterVMs.count + ActiveVMs = $ClusterVMsPoweredOn.count + VMvCPUs = $VMvCPUs + vCPUpCPUratio = "$vCPUpCPUratio : 1" + PhysicalMemoryGB = $PhysicalMemory + AllocatedVMMemoryGB = $AllocatedVMMemoryGB + ClusterMemoryUsage = "$MemoryUsage %" + SumMaxVMIOPS = [math]::round( ($reportDiskPerf | Measure-Object -Sum -Property IOPSMax).sum, 1 ) + AverageMaxVMIOPs = [math]::round( ($reportDiskPerf | Measure-Object -Average -Property IOPSMax).Average,1 ) + SumVMDiskSpaceGB = [math]::round( ($reportDiskSpace | Measure-Object -Sum -Property CapacityGB).sum, 1 ) + SumDatastoreSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property CapacityGB).sum, 1 ) + SumDatastoreUsedSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property UsedSpaceGB).sum, 1 ) + } + $MyView += $SizingReport + Write-Verbose "$(Get-Date -Format G) Process Global Report completed" + #endregion + } + + } + Else { + Write-Error "Validation Failed! Processing Skipped" + } + + } + + End { + $MyView + } + +} \ No newline at end of file From 5bf60badf13961bac3a6a6b2d3585be7511ffbe2 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Sun, 27 Nov 2016 21:57:51 -0800 Subject: [PATCH 19/21] Example Horizon View Desktop function Example Horizon View Desktop function --- .../Horizon View Example Desktop Script.ps1 | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Scripts/Horizon View Example Desktop Script.ps1 diff --git a/Scripts/Horizon View Example Desktop Script.ps1 b/Scripts/Horizon View Example Desktop Script.ps1 new file mode 100644 index 0000000..d0b571c --- /dev/null +++ b/Scripts/Horizon View Example Desktop Script.ps1 @@ -0,0 +1,49 @@ +Function Get-HVDesktop { + <# + .SYNOPSIS + This cmdlet retrieves the virtual desktops on a horizon view Server. + .DESCRIPTION + This cmdlet retrieves the virtual desktops on a horizon view Server. + .NOTES + Author: Alan Renouf, @alanrenouf,virtu-al.net + .PARAMETER State + Hash table containing states to filter on + .EXAMPLE + List All Desktops + Get-HVDesktop + + .EXAMPLE + List All Problem Desktops + Get-HVDesktop -state @('PROVISIONING_ERROR', + 'ERROR', + 'AGENT_UNREACHABLE', + 'AGENT_ERR_STARTUP_IN_PROGRESS', + 'AGENT_ERR_DISABLED', + 'AGENT_ERR_INVALID_IP', + 'AGENT_ERR_NEED_REBOOT', + 'AGENT_ERR_PROTOCOL_FAILURE', + 'AGENT_ERR_DOMAIN_FAILURE', + 'AGENT_CONFIG_ERROR', + 'UNKNOWN') + #> +Param ( + $State + ) + + $ViewAPI = $global:DefaultHVServers[0].ExtensionData + $query_service = New-Object "Vmware.Hv.QueryServiceService" + $query = New-Object "Vmware.Hv.QueryDefinition" + $query.queryEntityType = 'MachineSummaryView' + if ($State) { + [VMware.Hv.QueryFilter []] $filters = @() + foreach ($filterstate in $State) { + $filters += new-object VMware.Hv.QueryFilterEquals -property @{'memberName' = 'base.basicState'; 'value' = $filterstate} + } + $orFilter = new-object VMware.Hv.QueryFilterOr -property @{'filters' = $filters} + $query.Filter = $orFilter + } + $Desktops = $query_service.QueryService_Query($ViewAPI,$query) + $Desktops.Results.Base +} + + From a0d2450ff1b5aba4dbb99186f6a9d6bed425a624 Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Mon, 28 Nov 2016 11:19:09 +0100 Subject: [PATCH 20/21] Optional Stats Collection. Temporary workaround for problems with vSphere 6.x --- Modules/Recommend-Sizing.psm1 | 65 ++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/Modules/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing.psm1 index 8aea554..db01181 100644 --- a/Modules/Recommend-Sizing.psm1 +++ b/Modules/Recommend-Sizing.psm1 @@ -8,6 +8,7 @@ function Recommend-Sizing { =========================================================================== Changelog: 2016.11 ver 1.0 Base Release + 2016.11 ver 1.1 Optional Stats Collection. =========================================================================== External Code Sources: http://www.lucd.info/2011/04/22/get-the-maximum-iops/ @@ -26,7 +27,7 @@ function Recommend-Sizing { This Function collects Basic vSphere Informations for a Hardware Sizing Recomamndation. Focus is in Compute Ressources. .Example - Recommend-Sizing -ClusterNames Cluster01, Cluster02 -StatsRange 480 -Verbose + Recommend-Sizing -ClusterNames Cluster01, Cluster02 -Stats -StatsRange 60 -Verbose .Example Recommend-Sizing -ClusterNames Cluster01, Cluster02 @@ -37,6 +38,11 @@ function Recommend-Sizing { .PARAMETER ClusterNames List of your vSphere Cluser Names to process. + .PARAMETER Stats + Enables Stats Collection. + + Warning: At the moment this is only tested and supported with vSphere 5.5! + .PARAMETER StatsRange Time Range in Minutes for the Stats Collection. Default is 24h. @@ -49,12 +55,18 @@ function Recommend-Sizing { param( [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] [Array] $ClusterNames, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2)] + [switch] $Stats, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2)] [int] $StatsRange = 1440 ) Begin { - [int]$TimeRange = "-" + $StatsRange + if ($Stats) { + Write-Warning "Stats Collection enabled.`nAt the moment this is only tested and supported with vSphere 5.5" + [int]$TimeRange = "-" + $StatsRange + } + $Validate = $True #region: Check Clusters Write-Verbose "$(Get-Date -Format G) Starting Cluster Validation..." @@ -108,27 +120,32 @@ Process { Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details completed" #endregion - #region: Creating Disk Metrics - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics..." - $DiskMetrics = "virtualDisk.numberReadAveraged.average","virtualDisk.numberWriteAveraged.average" - $start = (Get-Date).AddMinutes($TimeRange) - $DiskStats = Get-Stat -Stat $DiskMetrics -Entity $ClusterVMsPoweredOn -Start $start -Verbose:$False - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics completed" - #endregion - - #region: Creating IOPS Reports - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report..." - $reportDiskPerf = @() - $reportDiskPerf = $DiskStats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - IOPSMax = ($_.Group | ` - Group-Object -Property Timestamp | ` - %{$_.Group[0].Value + $_.Group[1].Value} | ` - Measure-Object -Maximum).Maximum + if ($Stats) { + #region: Creating Disk Metrics + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics..." + $DiskMetrics = "virtualDisk.numberReadAveraged.average","virtualDisk.numberWriteAveraged.average" + $start = (Get-Date).AddMinutes($TimeRange) + $DiskStats = Get-Stat -Stat $DiskMetrics -Entity $ClusterVMsPoweredOn -Start $start -Verbose:$False + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) IOPS Metrics completed" + #endregion + + #region: Creating IOPS Reports + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report..." + $reportDiskPerf = @() + $reportDiskPerf = $DiskStats | Group-Object -Property {$_.Entity.Name},Instance | %{ + New-Object PSObject -Property @{ + IOPSMax = ($_.Group | ` + Group-Object -Property Timestamp | ` + %{$_.Group[0].Value + $_.Group[1].Value} | ` + Measure-Object -Maximum).Maximum + } } + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report completed" + #endregion + } + else { + Write-Verbose "$(Get-Date -Format G) Stats Cellocetion skipped..." } - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) IOPS Report completed" - #endregion #region: Create VM Disk Space Report Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report..." @@ -174,11 +191,11 @@ Process { PhysicalMemoryGB = $PhysicalMemory AllocatedVMMemoryGB = $AllocatedVMMemoryGB ClusterMemoryUsage = "$MemoryUsage %" - SumMaxVMIOPS = [math]::round( ($reportDiskPerf | Measure-Object -Sum -Property IOPSMax).sum, 1 ) - AverageMaxVMIOPs = [math]::round( ($reportDiskPerf | Measure-Object -Average -Property IOPSMax).Average,1 ) SumVMDiskSpaceGB = [math]::round( ($reportDiskSpace | Measure-Object -Sum -Property CapacityGB).sum, 1 ) SumDatastoreSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property CapacityGB).sum, 1 ) SumDatastoreUsedSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property UsedSpaceGB).sum, 1 ) + SumMaxVMIOPS = [math]::round( ($reportDiskPerf | Measure-Object -Sum -Property IOPSMax).sum, 1 ) + AverageMaxVMIOPs = [math]::round( ($reportDiskPerf | Measure-Object -Average -Property IOPSMax).Average,1 ) } $MyView += $SizingReport Write-Verbose "$(Get-Date -Format G) Process Global Report completed" From 530b07d73ee69ca7d192aa30f1a2d17b786b613d Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Wed, 30 Nov 2016 11:46:53 -0500 Subject: [PATCH 21/21] Updating the Approval Process --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae27a3d..7d2825c 100644 --- a/README.md +++ b/README.md @@ -189,4 +189,4 @@ Members: * Rynardt Spies (Community Member) ## Approval of Additions -Items added to the repository, including items from the Board members, require 2 votes from the board members before being added to the repository. The approving members will have ideally downloaded and tested the item. When two “Approved for Merge” comments are added from board members, the pull can then be committed to the repository. +Items added to the repository, including items from the Board members, require a review and approval from at least one board member before being added to the repository. The approving member/s will have verified for a lack of malicious code. Once an “Approved for Merge” comment has been added from a board member, the pull can then be committed to the repository. \ No newline at end of file