From ef1308c96e181c911bb35a6bed71bdebe50519ac Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Fri, 2 Dec 2016 19:26:33 +0100 Subject: [PATCH 01/90] Get-VMmaxIOPS v1.1 Added vSphere 6.5 Support, New Counters, More Error Handling --- Modules/Get-VMmaxIOPS.psm1 | 127 +++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 49 deletions(-) diff --git a/Modules/Get-VMmaxIOPS.psm1 b/Modules/Get-VMmaxIOPS.psm1 index 8ef86e3..27af1ad 100644 --- a/Modules/Get-VMmaxIOPS.psm1 +++ b/Modules/Get-VMmaxIOPS.psm1 @@ -1,11 +1,30 @@ function Get-VMmaxIOPS { <# - - .SYNOPSIS - Report VM Disk IOPS of VMs + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.10 ver 1.0 Base Release + 2016.11 ver 1.1 Added vSphere 6.5 Support, New Counters, More Error Handling + =========================================================================== + 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, 6.5 + PowerCLI Version: PowerCLI 6.3 R1, 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Windows Server 2012 R2 + =========================================================================== + Keywords vSphere, ESXi, VM, Storage + =========================================================================== .DESCRIPTION - This Function will Create a VM Disk IOPS Report + This Function will Create a VM Disk IOPS Report .Example Get-VM TST* | Get-VMmaxIOPS -Minutes 60 | FT -Autosize @@ -20,15 +39,6 @@ function Get-VMmaxIOPS { .PARAMETER Minutes Specify the Minutes to report (10080 is one Week) - .Notes - NAME: Get-VMmaxIOPS.ps1 - LASTEDIT: 08/23/2016 - VERSION: 1.1 - KEYWORDS: VMware, vSphere, ESXi, IOPS - - .Link - http://mycloudrevolution.com/ - #Requires PS -Version 4.0 #Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} #> @@ -36,50 +46,69 @@ function Get-VMmaxIOPS { [CmdletBinding()] param( [Parameter(Mandatory=$true, ValueFromPipeline=$True, Position=0)] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] - $VMs, - [Parameter(Mandatory=$false, Position=1)] + [ValidateNotNullorEmpty()] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $VMs, + [Parameter(Mandatory=$false, Position=1, HelpMessage = "Specify the Minutes to report (10080 is one Week)")] + [ValidateNotNullorEmpty()] [int] $Minutes = 30 ) - +Begin { + # none + } Process { + if ($_.PowerState -eq "PoweredOn") { + #region: Global Definitions + [int]$TimeRange = "-" + $Minutes + #endregion - #region: Global Definitions - [int]$TimeRange = "-" + $Minutes - #endregion + #region: Creating VM Stats + Write-Verbose "$(Get-Date -Format G) Create VM Stats..." + $VMMetrics = "virtualdisk.numberwriteaveraged.average","virtualdisk.numberreadaveraged.average" + $Start = (Get-Date).AddMinutes($TimeRange) + $stats = Get-Stat -Realtime -Stat $VMMetrics -Entity $VMs -Start $Start -Verbose:$False + Write-Verbose "$(Get-Date -Format G) Create VM Stats completed" + #endregion - #region: Creating Metrics - Write-Debug "Starting to Create Metrics..." - $metrics = "virtualDisk.numberReadAveraged.average","virtualDisk.numberWriteAveraged.average" - $start = (Get-Date).AddMinutes($TimeRange) - $stats = Get-Stat -Stat $metrics -Entity $VMs -Start $start - #endregion + #region: Creating HD-Tab + Write-Verbose "$(Get-Date -Format G) Create HD Tab..." + $hdTab = @{} + foreach($hd in (Get-Harddisk -VM $VMs)){ + $controllerKey = $hd.Extensiondata.ControllerKey + $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey} + $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('[') + } + Write-Verbose "$(Get-Date -Format G) Create HD Tab completed" + #endregion - #region: Creating HD-Tab - Write-Debug "Starting to Create HD-Tab..." - $hdTab = @{} - foreach($hd in (Get-Harddisk -VM $VMs)){ - $controllerKey = $hd.Extensiondata.ControllerKey - $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey} - $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('[') - } - #endregion + #region: Creating Reports + Write-Verbose "$(Get-Date -Format G) Create Report..." + $reportPerf = @() + $reportPerf = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{ + New-Object PSObject -Property @{ + VM = $_.Values[0] + Disk = $_.Values[1] + IOPSWriteAvg = [math]::round( ($_.Group | ` + where{$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} | ` + Measure-Object -Property Value -Average).Average,2) + IOPSReadAvg = [math]::round( ($_.Group | ` + where{$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} | ` + Measure-Object -Property Value -Average).Average,2) + Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]] + } + } + Write-Verbose "$(Get-Date -Format G) Create Report completed" + #endregion + - #region: Creating Reports - Write-Debug "Starting to Process IOPS Report..." - $reportPerf = @() - $reportPerf = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - VM = $_.Values[0] - Disk = $_.Values[1] - IOPSMax = ($_.Group | ` - Group-Object -Property Timestamp | ` - %{$_.Group[0].Value + $_.Group[1].Value} | ` - Measure-Object -Maximum).Maximum - Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]] } + Else { + Write-Error "VM $($_.Name) is Powered Off! Processing Skipped" + } + $reportPerf | Select-Object VM, Disk, Datastore, IOPSWriteAvg, IOPSReadAvg } - $reportPerf | Select-Object VM, Disk, Datastore, IOPSMax - #endregion + +End { + # none } + } \ No newline at end of file From 6967b1b7a69a481e8f5e4e09b195a855bce638f8 Mon Sep 17 00:00:00 2001 From: "Peter D. Jorgensen" Date: Fri, 2 Dec 2016 21:38:03 -0500 Subject: [PATCH 02/90] Add scripts for creating cluster groups and affinity rules. These scripts create DRS groups for virtual machines and hosts, and host affinity rules. --- Scripts/New-ClusterHostGroup.ps1 | 43 +++++++++++++++++++++++++++ Scripts/New-ClusterVmGroup.ps1 | 43 +++++++++++++++++++++++++++ Scripts/New-ClusterVmHostRule.ps1 | 48 +++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 Scripts/New-ClusterHostGroup.ps1 create mode 100644 Scripts/New-ClusterVmGroup.ps1 create mode 100644 Scripts/New-ClusterVmHostRule.ps1 diff --git a/Scripts/New-ClusterHostGroup.ps1 b/Scripts/New-ClusterHostGroup.ps1 new file mode 100644 index 0000000..c5cb9e7 --- /dev/null +++ b/Scripts/New-ClusterHostGroup.ps1 @@ -0,0 +1,43 @@ +<# + .NOTES + =========================================================================== + Script name: New-ClusterHostGroup.ps1 + Created on: 2016-10-25 + Author: Peter D. Jorgensen (@pjorg, pjorg.com) + Dependencies: None known + ===Tested Against Environment==== + vSphere Version: 5.5, 6.0 + PowerCLI Version: PowerCLI 6.5R1 + PowerShell Version: 5.0 + OS Version: Windows 10, Windows 7 + =========================================================================== + .DESCRIPTION + Creates a DRS Host Group in a vSphere cluster. + .Example + $ProdCluster = Get-Cluster *prod* + $OddHosts = $ProdCluster | Get-VMHost | ?{ $_.Name -match 'esxi-\d*[13579]+.\lab\.local' } + .\New-ClusterHostGroup.ps1 -Name 'OddProdHosts' -Cluster $ProdCluster -VMHost $OddHosts +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$True,Position=1)] + [String]$Name, + [Parameter(Mandatory=$True,ValueFromPipeline=$True,Position=2)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster, + [Parameter(Mandatory=$False,Position=3)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost[]]$VMHost +) + +$NewGroup = New-Object VMware.Vim.ClusterHostGroup -Property @{ + 'Name'=$Name + 'Host'=$VMHost.Id +} + +$spec = New-Object VMware.Vim.ClusterConfigSpecEx -Property @{ + 'GroupSpec'=(New-Object VMware.Vim.ClusterGroupSpec -Property @{ + 'Info'=$NewGroup + }) +} + +$ClusterToReconfig = Get-View -VIObject $Cluster -Property Name +$ClusterToReconfig.ReconfigureComputeResource($spec, $true) \ No newline at end of file diff --git a/Scripts/New-ClusterVmGroup.ps1 b/Scripts/New-ClusterVmGroup.ps1 new file mode 100644 index 0000000..b194a64 --- /dev/null +++ b/Scripts/New-ClusterVmGroup.ps1 @@ -0,0 +1,43 @@ +<# + .NOTES + =========================================================================== + Script name: New-ClusterVmGroup.ps1 + Created on: 2016-10-25 + Author: Peter D. Jorgensen (@pjorg, pjorg.com) + Dependencies: None known + ===Tested Against Environment==== + vSphere Version: 5.5, 6.0 + PowerCLI Version: PowerCLI 6.5R1 + PowerShell Version: 5.0 + OS Version: Windows 10, Windows 7 + =========================================================================== + .DESCRIPTION + Creates a DRS VM Group in a vSphere cluster. + .Example + $ProdCluster = Get-Cluster *prod* + $EvenVMs = $ProdCluster | Get-VM | ?{ $_.Name -match 'MyVM-\d*[02468]+' } + .\New-ClusterVmGroup.ps1 -Name 'EvenVMs' -Cluster $ProdCluster -VM $EvenVMs +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$True,Position=1)] + [String]$Name, + [Parameter(Mandatory=$True,ValueFromPipeline=$True,Position=2)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster, + [Parameter(Mandatory=$False,Position=3)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM +) + +$NewGroup = New-Object VMware.Vim.ClusterVmGroup -Property @{ + 'Name'=$Name + 'VM'=$VM.Id +} + +$spec = New-Object VMware.Vim.ClusterConfigSpecEx -Property @{ + 'GroupSpec'=(New-Object VMware.Vim.ClusterGroupSpec -Property @{ + 'Info'=$NewGroup + }) +} + +$ClusterToReconfig = Get-View -VIObject $Cluster -Property Name +$ClusterToReconfig.ReconfigureComputeResource($spec, $true) \ No newline at end of file diff --git a/Scripts/New-ClusterVmHostRule.ps1 b/Scripts/New-ClusterVmHostRule.ps1 new file mode 100644 index 0000000..217cbeb --- /dev/null +++ b/Scripts/New-ClusterVmHostRule.ps1 @@ -0,0 +1,48 @@ +<# + .NOTES + =========================================================================== + Script name: New-ClusterVmHostRule.ps1 + Created on: 2016-10-25 + Author: Peter D. Jorgensen (@pjorg, pjorg.com) + Dependencies: None known + ===Tested Against Environment==== + vSphere Version: 5.5, 6.0 + PowerCLI Version: PowerCLI 6.5R1 + PowerShell Version: 5.0 + OS Version: Windows 10, Windows 7 + =========================================================================== + .DESCRIPTION + Creates a VM to Host affinity rule in a vSphere cluster. + .Example + $ProdCluster = Get-Cluster *prod* + .\New-ClusterVmHostRule.ps1 -Name 'Even VMs to Odd Hosts' -AffineHostGroupName 'OddHosts' -VMGroupName 'EvenVMs' -Enabled:$true -Cluster $ProdCluster +#> +[CmdletBinding()] +Param( + [Parameter(Mandatory=$True,Position=1)] + [String]$Name, + [Parameter(Mandatory=$True,Position=2)] + [String]$AffineHostGroupName, + [Parameter(Mandatory=$True,Position=3)] + [String]$VMGroupName, + [Parameter(Mandatory=$False,Position=4)] + [Switch]$Enabled=$True, + [Parameter(Mandatory=$True,ValueFromPipeline=$True,Position=5)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster]$Cluster +) + +$NewRule = New-Object VMware.Vim.ClusterVmHostRuleInfo -Property @{ + 'AffineHostGroupName'=$AffineHostGroupName + 'VmGroupName'=$VMGroupName + 'Enabled'=$Enabled + 'Name'=$Name +} + +$spec = New-Object VMware.Vim.ClusterConfigSpecEx -Property @{ + 'RulesSpec'=(New-Object VMware.Vim.ClusterRuleSpec -Property @{ + 'Info'=$NewRule + }) +} + +$ClusterToReconfig = Get-View -VIObject $Cluster -Property Name +$ClusterToReconfig.ReconfigureComputeResource($spec, $true) \ No newline at end of file From 1d06122f4c8dfc958d2efd437e510e6c77121109 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Mon, 5 Dec 2016 19:49:45 -0800 Subject: [PATCH 03/90] Home Lab Scripts Script to deploy and delete home lab --- Scripts/Home Lab/Home Lab Delete settings.ps1 | 28 +++ Scripts/Home Lab/Home Lab Deployment.ps1 | 216 ++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 Scripts/Home Lab/Home Lab Delete settings.ps1 create mode 100644 Scripts/Home Lab/Home Lab Deployment.ps1 diff --git a/Scripts/Home Lab/Home Lab Delete settings.ps1 b/Scripts/Home Lab/Home Lab Delete settings.ps1 new file mode 100644 index 0000000..1846756 --- /dev/null +++ b/Scripts/Home Lab/Home Lab Delete settings.ps1 @@ -0,0 +1,28 @@ +$ESXIP = "192.168.1.201" +$ESXUser = "root" +$ESXPWD = "VMware1!" + +Connect-viserver $esxip -user $ESXUser -pass $ESXPWD + +#Leaving confirm off just in case someone happens to be connected to more than one vCenter/Host! +Get-VM | Stop-VM +Get-VM | Remove-VM + +$ESXCLI = Get-EsxCli -v2 -VMHost (get-VMHost) +$esxcli.vsan.cluster.leave.invoke() + +$VSANDisks = $esxcli.storage.core.device.list.invoke() | Where {$_.isremovable -eq "false"} | Sort size +$Performance = $VSANDisks[0] +$Capacity = $VSANDisks[1] + +$removal = $esxcli.vsan.storage.remove.CreateArgs() +$removal.ssd = $performance.Device +$esxcli.vsan.storage.remove.Invoke($removal) + +$capacitytag = $esxcli.vsan.storage.tag.remove.CreateArgs() +$capacitytag.disk = $Capacity.Device +$capacitytag.tag = "capacityFlash" +$esxcli.vsan.storage.tag.remove.Invoke($capacitytag) + +Set-VMHostSysLogServer $null +Remove-VMHostNtpServer (Get-VMHostNtpServer) -Confirm:$false \ No newline at end of file diff --git a/Scripts/Home Lab/Home Lab Deployment.ps1 b/Scripts/Home Lab/Home Lab Deployment.ps1 new file mode 100644 index 0000000..b119966 --- /dev/null +++ b/Scripts/Home Lab/Home Lab Deployment.ps1 @@ -0,0 +1,216 @@ +# ESX Start Host deployment Settings +$ESXIP = "192.168.1.201" +$ESXUser = "root" +$ESXPWD = "VMware1!" + +# VCSA Configuration +$VCSACDDrive = "D:\" +$SSODomainName = "corp.local" +$VCNAME = "VC01" +$VCUser = "Administrator@$SSODomainName" +$VCPass = "VMware1!" +$VCIP = "192.168.1.200" +$VCDNS = "192.168.1.1" +$VCGW = "192.168.1.1" +$VCNetPrefix = "24" +$VCSADeploymentSize = "tiny" + +# vCenter Configuration +$SSOSiteName = "Site01" +$datacenter = "DC01" +$cluster = "Cluster01" +$ntpserver = "pool.ntp.org" +$webClientTheme = "CormacHogan" + +# VSAN Configuration +$VSANPolicy = '(("hostFailuresToTolerate" i1) ("forceProvisioning" i1))' +$VMKNetforVSAN = "Management Network" + +# General Settings +$verboseLogFile = "$ENV:Temp\vsphere65-NUC-lab-deployment.log" + +# End of configuration +Function My-Logger { + param( + [Parameter(Mandatory=$true)] + [String]$message + ) + + $timeStamp = Get-Date -Format "MM-dd-yyyy_hh:mm:ss" + + Write-Host -NoNewline -ForegroundColor White "[$timestamp]" + Write-Host -ForegroundColor Green " $message" + $logMessage = "[$timeStamp] $message" + $logMessage | Out-File -Append -LiteralPath $verboseLogFile +} + +$StartTime = Get-Date + +Write-Host "Writing Log files to $verboselogfile" -ForegroundColor Yellow +Write-Host "" + +If (-not (Test-Path "$($VCSACDDrive)vcsa-cli-installer\win32\vcsa-deploy.exe")){ + Write-Host "VCSA media not found at $($VCSACDDrive) please mount it and try again" + return +} +My-Logger "Connecting to ESXi Host: $ESXIP ..." +$connection = Connect-viserver $ESXIP -user $ESXUser -Password $ESXPWD -WarningAction SilentlyContinue + +My-Logger "Enabling SSH Service for future troubleshooting ..." +Start-VMHostService -HostService (Get-VMHost | Get-VMHostService | Where { $_.Key -eq "TSM-SSH"} ) | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Configuring NTP server to $ntpserver and retarting service ..." +Get-VMHost | Add-VMHostNtpServer $ntpserver | Out-File -Append -LiteralPath $verboseLogFile +Get-VMHost | Get-VMHostFirewallException | where {$_.Name -eq "NTP client"} | Set-VMHostFirewallException -Enabled:$true | Out-File -Append -LiteralPath $verboseLogFile +Get-VMHost | Get-VmHostService | Where-Object {$_.key -eq "ntpd"} | Start-VMHostService | Out-File -Append -LiteralPath $verboseLogFile +Get-VMhost | Get-VmHostService | Where-Object {$_.key -eq "ntpd"} | Set-VMHostService -policy "automatic" | Out-File -Append -LiteralPath $verboseLogFile + +#Configure VSAN Bootstrap (http://www.virtuallyghetto.com/2013/09/how-to-bootstrap-vcenter-server-onto_9.html) +My-Logger "Setting the VSAN Policy for ForceProvisiong ..." +$esxcli = get-esxcli -V2 +$VSANPolicyDefaults = $esxcli.vsan.policy.setdefault.CreateArgs() +$VSANPolicyDefaults.policy = $VSANPolicy +$VSANPolicyDefaults.policyclass = "vdisk" +$esxcli.vsan.policy.setdefault.Invoke($VSANPolicyDefaults) | Out-File -Append -LiteralPath $verboseLogFile +$VSANPolicyDefaults.policyclass = "vmnamespace" +$esxcli.vsan.policy.setdefault.Invoke($VSANPolicyDefaults) | Out-File -Append -LiteralPath $verboseLogFile + +# Create new VSAN Cluster +My-Logger "Creating a new VSAN Cluster ..." +$esxcli.vsan.cluster.new.Invoke() | Out-File -Append -LiteralPath $verboseLogFile +$VSANDisks = $esxcli.storage.core.device.list.invoke() | Where {$_.isremovable -eq "false"} | Sort size +"Found the following disks to use for VSAN:" | Out-File -Append -LiteralPath $verboseLogFile +$VSANDisks | FT | Out-File -Append -LiteralPath $verboseLogFile +$Performance = $VSANDisks[0] +"Using $($Performance.Model) for Performance disk" | Out-File -Append -LiteralPath $verboseLogFile +$Capacity = $VSANDisks[1] +"Using $($Capacity.Model) for Capacity disk" | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Tagging $($Capacity.Model) as Capacity ..." +$capacitytag = $esxcli.vsan.storage.tag.add.CreateArgs() +$capacitytag.disk = $Capacity.Device +$capacitytag.tag = "capacityFlash" +$esxcli.vsan.storage.tag.add.Invoke($capacitytag) | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Create VSAN Diskgroup to back VSAN Cluster ..." +$addvsanstorage = $esxcli.vsan.storage.add.CreateArgs() +$addvsanstorage.ssd = $Performance.Device +$addvsanstorage.disks = $Capacity.device +$esxcli.vsan.storage.add.Invoke($addvsanstorage) | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Deploying VCSA using Script CLI + JSON ..." +$config = (get-content –raw “$($VCSACDDrive)vcsa-cli-installer\templates\install\embedded_vCSA_on_ESXi.json” ) | convertfrom-json +$config.'new.vcsa'.esxi.hostname = $ESXIP +$config.'new.vcsa'.esxi.username = $ESXUser +$config.'new.vcsa'.esxi.password = $ESXPWD +$config.'new.vcsa'.esxi.datastore = "vsanDatastore" +$config.'new.vcsa'.network.ip = $VCIP +$config.'new.vcsa'.network.'dns.servers'[0] = $VCDNS +$config.'new.vcsa'.network.gateway = $VCGW +$config.'new.vcsa'.network.'system.name' = $VCIP #Change to $VCName if you have DNS setup +$config.'new.vcsa'.network.prefix = $VCNetPrefix +$config.'new.vcsa'.os.password = $VCPass +$config.'new.vcsa'.appliance.'deployment.option' = $VCSADeploymentSize +$config.'new.vcsa'.sso.password = $VCPass +$config.'new.vcsa'.sso.'site-name' = $SSOSiteName +$config.'new.vcsa'.sso.'domain-name' = $SSODomainName +$config | convertto-json | set-content –path “$($ENV:Temp)\jsontemplate.json” +invoke-expression “$($VCSACDDrive)vcsa-cli-installer\win32\vcsa-deploy.exe install --no-esx-ssl-verify --accept-eula --acknowledge-ceip $($ENV:Temp)\jsontemplate.json” | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Enable VSAN Traffic on VMKernel Network ..." +$VMKernel = Get-VMHost $ESXIP | Get-VMHostNetworkAdapter -VMKernel | Where {$_.PortGroupName -eq $VMKNetforVSAN } +$IsVSANEnabled = $VMKernel | Where { $_.VsanTrafficEnabled} +If (-not $IsVSANEnabled) { + My-Logger "Enabling VSAN Kernel on $VMKernel ..." + $VMKernel | Set-VMHostNetworkAdapter -VsanTrafficEnabled $true -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile +} Else { + My-Logger "VSAN Kernel already enabled on $VmKernel ..." +} + +My-Logger "Disconnecting from ESXi Host: $ESXIP ..." +Disconnect-VIServer $ESXIP -Force -Confirm:$false -WarningAction SilentlyContinue + +My-Logger "Connecting to vCenter: $VCIP ..." +$connection = Connect-VIServer -Server $VCIP -User $VCUser -Password $vcpass -WarningAction SilentlyContinue + +My-Logger "Creating Datacenter: $datacenter ..." +New-Datacenter -Name $datacenter -Location (Get-Folder -Type Datacenter) | Out-File -Append -LiteralPath $verboseLogFile +My-Logger "Creating Cluster: $cluster ..." +New-Cluster -Name $cluster -Location (Get-Datacenter -Name $datacenter) -DrsEnabled -VsanEnabled | Out-File -Append -LiteralPath $verboseLogFile +My-Logger "Adding ESXi Host $ESXIP to vCenter ..." +Add-VMHost -Location (Get-Cluster -Name $cluster) -User $ESXUser -Password $ESXPWD -Name $ESXIP -Force | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Setting the VCSA NTP server to: $NTPServer ..." +Connect-CISServer -Server $VCIP -User $VCUser -Password $VCPass +(Get-CISService com.vmware.appliance.techpreview.ntp.server).set(@($NTPServer)) | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Configuring Host syslog to VC ..." +Get-VMHost | Set-VMHostSysLogServer -SysLogServer $VCIP | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Acknowledging Alarms on the cluster ..." +$alarmMgr = Get-View AlarmManager +Get-Cluster | where {$_.ExtensionData.TriggeredAlarmState} | %{ + $cluster = $_ + $Cluster.ExtensionData.TriggeredAlarmState | %{ + $alarmMgr.AcknowledgeAlarm($_.Alarm,$vm.ExtensionData.MoRef) | Out-File -Append -LiteralPath $verboseLogFile + } +} + +My-Logger "Creating @lamw Content Library with Nested ESXi Images ..." + +# Get a Datastore to create the content library on +$datastoreID = (Get-Datastore "vsanDatastore").extensiondata.moref.value + +# Get the Service that works with Subscribed content libraries +$ContentCatalog = Get-CisService com.vmware.content.subscribed_library + +# Create a Subscribed content library on an existing datastore +$createSpec = $ContentCatalog.help.create.create_spec.CreateExample() +$createSpec.subscription_info.authentication_method = "NONE" +$createSpec.subscription_info.ssl_thumbprint = "69:d9:9e:e9:0b:4b:68:24:09:2b:ce:14:d7:4a:f9:8c:bd:c6:5a:e9" +$createSpec.subscription_info.automatic_sync_enabled = $true +$createSpec.subscription_info.subscription_url = "https://s3-us-west-1.amazonaws.com/vghetto-content-library/lib.json" +$createSpec.subscription_info.on_demand = $false +$createSpec.subscription_info.password = $null +$createSpec.server_guid = $null +$createspec.name = "virtuallyGhetto CL" +$createSpec.description = "@lamw CL: http://www.virtuallyghetto.com/2015/04/subscribe-to-vghetto-nested-esxi-template-content-library-in-vsphere-6-0.html" +$createSpec.type = "SUBSCRIBED" +$createSpec.publish_info = $null +$datastoreID = [VMware.VimAutomation.Cis.Core.Types.V1.ID]$datastoreID +$StorageSpec = New-Object PSObject -Property @{ + datastore_id = $datastoreID + type = "DATASTORE" + } +$CreateSpec.storage_backings.Add($StorageSpec) +$UniqueID = [guid]::NewGuid().tostring() +$ContentCatalog.create($UniqueID, $createspec) | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Changing the default VSAN VM Storage Policy to FTT=0 & Force Provisioning to Yes ..." +$VSANPolicy = Get-SpbmStoragePolicy "Virtual SAN Default Storage Policy" +$Ruleset = New-SpbmRuleSet -Name “Rule-set 1” -AllOfRules @((New-SpbmRule -Capability VSAN.forceProvisioning $True), (New-SpbmRule -Capability VSAN.hostFailuresToTolerate 0)) +$VSANPolicy | Set-SpbmStoragePolicy -RuleSet $Ruleset | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Enabling VM Autostart for the VCSA VM ..." +$VCVM = Get-VM +$vmstartpolicy = Get-VMStartPolicy -VM $VCVM +Set-VMHostStartPolicy (Get-VMHost $ESXIP | Get-VMHostStartPolicy) -Enabled:$true | Out-File -Append -LiteralPath $verboseLogFile +Set-VMStartPolicy -StartPolicy $vmstartpolicy -StartAction PowerOn -StartDelay 0 | Out-File -Append -LiteralPath $verboseLogFile + +My-Logger "Enabling SSH on VCSA for easier troubleshooting ..." +$vcsassh = Get-CIsService com.vmware.appliance.access.ssh +$vcsassh.set($true) + +$EndTime = Get-Date +$duration = [math]::Round((New-TimeSpan -Start $StartTime -End $EndTime).TotalMinutes,2) + +My-Logger "================================" +My-Logger "vSphere Lab Deployment Complete!" +My-Logger "StartTime: $StartTime" +My-Logger " EndTime: $EndTime" +My-Logger " Duration: $duration minutes" +Write-Host "" +My-Logger "Access the vSphere Web Client at https://$VCIP/vsphere-client/" +My-Logger "Access the HTML5 vSphere Web Client at https://$VCIP/ui/" +My-Logger "Browse the vSphere REST APIs using the API Explorer here: https://$VCIP/apiexplorer/" +My-Logger "================================" \ No newline at end of file From 8dee2ec4985ac75af4c06109813ff0b34e092eba Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Wed, 7 Dec 2016 11:50:12 -0800 Subject: [PATCH 04/90] Removed web client customization for now Removed web client customization option as this will be available in a future script --- Scripts/Home Lab/Home Lab Deployment.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Scripts/Home Lab/Home Lab Deployment.ps1 b/Scripts/Home Lab/Home Lab Deployment.ps1 index b119966..9cd1658 100644 --- a/Scripts/Home Lab/Home Lab Deployment.ps1 +++ b/Scripts/Home Lab/Home Lab Deployment.ps1 @@ -20,7 +20,6 @@ $SSOSiteName = "Site01" $datacenter = "DC01" $cluster = "Cluster01" $ntpserver = "pool.ntp.org" -$webClientTheme = "CormacHogan" # VSAN Configuration $VSANPolicy = '(("hostFailuresToTolerate" i1) ("forceProvisioning" i1))' @@ -213,4 +212,4 @@ Write-Host "" My-Logger "Access the vSphere Web Client at https://$VCIP/vsphere-client/" My-Logger "Access the HTML5 vSphere Web Client at https://$VCIP/ui/" My-Logger "Browse the vSphere REST APIs using the API Explorer here: https://$VCIP/apiexplorer/" -My-Logger "================================" \ No newline at end of file +My-Logger "================================" From 8201e075a47339cc23af9939f95ef6b7947dfec7 Mon Sep 17 00:00:00 2001 From: William Lam Date: Thu, 8 Dec 2016 05:43:04 -0800 Subject: [PATCH 05/90] Adding VCHA module --- Modules/VCHA.psm1 | 413 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 Modules/VCHA.psm1 diff --git a/Modules/VCHA.psm1 b/Modules/VCHA.psm1 new file mode 100644 index 0000000..160f0e7 --- /dev/null +++ b/Modules/VCHA.psm1 @@ -0,0 +1,413 @@ +Function Get-VCHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the VCHA Configuration which provides you with + the current state, mode as well as the IP Addresses of the Active, + Passive & Witness Node. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to return VCHA Configuration + .EXAMPLE + Get-VCHAConfig +#> + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + + $vcHAState = $vcHAConfig.State + switch($vcHAState) { + configured { + $activeIp = $vcHAConfig.FailoverNodeInfo1.ClusterIpSettings.Ip.IpAddress + $passiveIp = $vcHAConfig.FailoverNodeInfo2.ClusterIpSettings.Ip.IpAddress + $witnessIp = $vcHAConfig.WitnessNodeInfo.IpSettings.Ip.IpAddress + + $vcHAClusterManager = Get-View failoverClusterManager + $vcHAMode = $vcHAClusterManager.getClusterMode() + + Write-Host "" + Write-Host -NoNewline -ForegroundColor Green "VCHA State: " + Write-Host -ForegroundColor White "$vcHAState" + Write-Host -NoNewline -ForegroundColor Green " VCHA Mode: " + Write-Host -ForegroundColor White "$vcHAMode" + Write-Host -NoNewline -ForegroundColor Green " ActiveIP: " + Write-Host -ForegroundColor White "$activeIp" + Write-Host -NoNewline -ForegroundColor Green " PassiveIP: " + Write-Host -ForegroundColor White "$passiveIp" + Write-Host -NoNewline -ForegroundColor Green " WitnessIP: " + Write-Host -ForegroundColor White "$witnessIp`n" + ;break + } + invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} + notConfigured { Write-Host "VCHA is not configured";break} + prepared { Write-Host "VCHA is being prepared, please try again in a little bit ...";break} + } +} + +Function Get-VCHAClusterHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the VCHA Cluster Health which provides more info + on each of the individual. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to return VCHA Cluster Health + .EXAMPLE + Get-VCHAClusterHealth +#> + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + $vcHAState = $vcHAConfig.State + + switch($vcHAState) { + invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} + notConfigured { Write-Host "VCHA is not configured";break} + prepared { Write-Host "VCHA is being prepared ...";break} + configured { + $vcHAClusterManager = Get-View failoverClusterManager + $healthInfo = $vcHAClusterManager.GetVchaClusterHealth() + + $vcClusterState = $healthInfo.RuntimeInfo.ClusterState + $nodeState = $healthInfo.RuntimeInfo.NodeInfo + + Write-Host "" + Write-Host -NoNewline -ForegroundColor Green "VCHA Cluster State: " + Write-Host -ForegroundColor White "$vcClusterState" + Write-Host -NoNewline -ForegroundColor Green "VCHA Node Information: " + $nodeState | Select NodeIp, NodeRole, NodeState + ;break + } + } +} + +Function Set-VCHAClusterMode { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you to set the mode of the VCHA Cluster whether + that is Enabled, Disabled or in Maintenance Mode. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to set VCHA Cluster Mode + .EXAMPLE + Set-VCHAClusterMode -Enabled $true + .EXAMPLE + Set-VCHAClusterMode -Disabled $true + .EXAMPLE + Set-VCHAClusterMode -Maintenance $true +#> + param( + [Switch]$Enabled, + [Switch]$Disabled, + [Switch]$Maintenance + ) + + $vcHAClusterManager = Get-View failoverClusterManager + + if($Enabled) { + Write-Host "Setting VCHA Cluster to Enabled ..." + $task = $vcHAClusterManager.setClusterMode_Task("enabled") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } elseIf($Maintenance) { + Write-Host "Setting VCHA Cluster to Maintenance ..." + $task = $vcHAClusterManager.setClusterMode_Task("maintenance") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } elseIf($Disabled) { + Write-Host "`nSetting VCHA Cluster to Disabled ...`n" + $task = $vcHAClusterManager.setClusterMode_Task("disabled") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } +} + +Function New-VCHABasicConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you create a new "Basic" VCHA Cluster, it does not + cover the "Advanced" use case. You will need to ensure that you have a + "Self Managed" vCenter Server before attempting this workflow. + This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to create "Basic" VCHA Cluster + .PARAMETER VCSAVM + The name of the vCenter Server Appliance (VCSA) in which you wish to enable VCHA on (must be self-managed) + .PARAMETER HANetwork + The name of the Virtual Portgroup or Distributed Portgroup used for the HA Network + .PARAMETER ActiveHAIp + The IP Address for the Active VCSA node + .PARAMETER ActiveNetmask + The Netmask for the Active VCSA node + .PARAMETER PassiveHAIp + The IP Address for the Passive VCSA node + .PARAMETER PassiveNetmask + The Netmask for the Passive VCSA node + .PARAMETER WitnessHAIp + The IP Address for the Witness VCSA node + .PARAMETER WitnessNetmask + The Netmask for the Witness VCSA node + .PARAMETER PassiveDatastore + The name of the datastore to deploy the Passive node to + .PARAMETER WitnessDatastore + The name of the datastore to deploy the Witness node to + .PARAMETER VCUsername + The VCSA username (e.g. administrator@vghetto.local) + .PARAMETER VCPassword + The VCSA password + .EXAMPLE + New-VCHABasicConfig -VCSAVM "vcenter65-1" -HANetwork "DVPG-VCHA-Network" ` + -ActiveHAIp 192.168.1.70 ` + -ActiveNetmask 255.255.255.0 ` + -PassiveHAIp 192.168.1.71 ` + -PassiveNetmask 255.255.255.0 ` + -WitnessHAIp 192.168.1.72 ` + -WitnessNetmask 255.255.255.0 ` + -PassiveDatastore "vsanDatastore" ` + -WitnessDatastore "vsanDatastore" ` + -VCUsername "administrator@vghetto.local" ` + -VCPassword "VMware1!" +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$VCSAVM, + [String]$HANetwork, + [String]$ActiveHAIp, + [String]$ActiveNetmask, + [String]$PassiveHAIp, + [String]$PassiveNetmask, + [String]$PassiveDatastore, + [String]$WitnessHAIp, + [String]$WitnessNetmask, + [String]$WitnessDatastore, + # Crappy Implementation but need to research more into using PSH Credential + [String]$VCUsername, + [String]$VCPassword + ) + + $VCSAVMView = Get-View -ViewType VirtualMachine -Filter @{"name"=$VCSAVM} + if($VCSAVMView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Virtual Machine $VCSAVM" + return + } + + $HANetworkView = Get-View -ViewType Network -Filter @{"name"=$HANetwork} + if($HANetworkView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Network $HANetwork" + return + } + + $PassiveDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$PassiveDatastore} + if($PassiveDatastoreView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Passive Datastore $PassiveDatastore" + return + } + + $WitnessDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$WitnessDatastore} + if($WitnessDatastoreView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Witness Datastore $WitnessDatastore" + return + } + + $vcIP = $VCSAVMView.Guest.IpAddress + if($vcIP -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to automatically retrieve the IP Address of $VCSAVM which is needed to use this function" + return + } + + # Retrieve Source VC SSL Thumbprint + $vcurl = "https://$vcIP" +add-type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + + public class IDontCarePolicy : ICertificatePolicy { + public IDontCarePolicy() {} + public bool CheckValidationResult( + ServicePoint sPoint, X509Certificate cert, + WebRequest wRequest, int certProb) { + return true; + } + } +"@ + [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy + # Need to do simple GET connection for this method to work + Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null + + $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") + # Get Thumbprint + add colons for a valid Thumbprint + $vcSSLThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' + + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $spec = New-Object VMware.Vim.VchaClusterDeploymentSpec + + $activeNetworkConfig = New-Object VMware.Vim.ClusterNetworkConfigSpec + $activeNetworkConfig.NetworkPortGroup = $HANetworkView.MoRef + $ipSettings = New-Object Vmware.Vim.CustomizationIPSettings + $ipSettings.SubnetMask = $ActiveNetmask + $activeIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $activeIpSpec.IpAddress = $ActiveHAIp + $ipSettings.Ip = $activeIpSpec + $activeNetworkConfig.IpSettings = $ipSettings + $spec.ActiveVcNetworkConfig = $activeNetworkConfig + + $activeVCConfig = New-Object Vmware.Vim.SourceNodeSpec + $activeVCConfig.ActiveVc = $VCSAVMView.MoRef + $serviceLocator = New-Object Vmware.Vim.ServiceLocator + $credential = New-Object VMware.Vim.ServiceLocatorNamePassword + $credential.username = $VCUsername + $credential.password = $VCPassword + $serviceLocator.Credential = $credential + $serviceLocator.InstanceUuid = $global:DefaultVIServer.InstanceUuid + $serviceLocator.Url = $vcurl + $serviceLocator.SslThumbprint = $vcSSLThumbprint + $activeVCConfig.ManagementVc = $serviceLocator + $spec.ActiveVcSpec = $activeVCConfig + + $passiveSpec = New-Object VMware.Vim.PassiveNodeDeploymentSpec + $passiveSpec.Folder = (Get-View (Get-Folder vm)).MoRef + $passiveIpSettings = New-object Vmware.Vim.CustomizationIPSettings + $passiveIpSettings.SubnetMask = $passiveNetmask + $passiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $passiveIpSpec.IpAddress = $passiveHAIp + $passiveIpSettings.Ip = $passiveIpSpec + $passiveSpec.IpSettings = $passiveIpSettings + $passiveSpec.NodeName = $VCSAVMView.Name + "-Passive" + $passiveSpec.datastore = $PassiveDatastoreView.MoRef + $spec.PassiveDeploymentSpec = $passiveSpec + + $witnessSpec = New-Object VMware.Vim.NodeDeploymentSpec + $witnessSpec.Folder = (Get-View (Get-Folder vm)).MoRef + $witnessSpec.NodeName = $VCSAVMView.Name + "-Witness" + $witnessIpSettings = New-object Vmware.Vim.CustomizationIPSettings + $witnessIpSettings.SubnetMask = $witnessNetmask + $witnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $witnessIpSpec.IpAddress = $witnessHAIp + $witnessIpSettings.Ip = $witnessIpSpec + $witnessSpec.IpSettings = $witnessIpSettings + $witnessSpec.datastore = $WitnessDatastoreView.MoRef + $spec.WitnessDeploymentSpec = $witnessSpec + + Write-Host "`nDeploying VCHA Cluster ...`n" + $task = $vcHAClusterConfig.deployVcha_Task($spec) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task -Verbose +} + +Function Remove-VCHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you destroy a VCHA Cluster. In addition, you have + the option to specify whether you would like both the Passive & Witness + Virtual Machines be deleted after the VCHA Cluster has been destroyed. + This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to destroy a VCHA Cluster Mode + .EXAMPLE + Remove-VCHAConfig + .EXAMPLE + Remove-VCHAConfig -Confirm:$false + .EXAMPLE + Remove-VCHAConfig -DeleteVM $true -Confirm:$false + .NOTES + Before you can destroy a VCHA Cluster, you must make sure it is first + disabled. Run the Set-VCHAClusterMode -Disabled $true to do so +#> + param( + [Boolean]$Confirm=$true, + [Switch]$DeleteVM=$false + ) + + $Verified = $false + if($Confirm -eq $true) { + Write-Host -ForegroundColor Yellow "`nDo you want to destroy VCHA Cluster?" + $answer = Read-Host -Prompt "Do you accept (Y or N)" + if($answer -eq "Y" -or $answer -eq "y") { + $Verified = $true + } + } else { + $Verified = $true + } + + if($Verified) { + $vcHAClusterManager = Get-View failoverClusterManager + $vcHAMode = $vcHAClusterManager.getClusterMode() + + if($vcHAMode -ne "disabled") { + Write-Host -ForegroundColor Yellow "To destroy VCHA Cluster, you must first set the VCHA Cluster Mode to `"Disabled`"" + Exit + } + + # Query BIOS UUID of the Passive/Witness to be able to delete + if($DeleteVM) { + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + $passiveBiosUUID = $vcHAConfig.FailoverNodeInfo2.biosUuid + $witnessBiosUUID = $vcHAConfig.WitnessNodeInfo.biosUuid + } + + $vcHAClusterConfig = Get-View failoverClusterConfigurator + + Write-Host "Destroying VCHA Cluster ..." + $task = $vcHAClusterConfig.destroyVcha_Task() + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + + # After VCHA Cluster has been destroyed, we can now delete the VMs we had queried earlier + if($DeleteVM) { + if($passiveBiosUUID -ne $null -and $witnessBiosUUID -ne $null) { + $searchIndex = Get-View searchIndex + + $passiveVM = $searchIndex.FindByUuid($null,$passiveBiosUUID,$true,$null) + $witnessVM = $searchIndex.FindByUuid($null,$witnessBiosUUID,$true,$null) + + if($passiveVM -ne $null -and $witnessVM -ne $null) { + Write-Host "Powering off & deleting Passive VM ..." + Stop-VM -VM (Get-View $passiveVM).Name -Confirm:$false | Out-Null + Remove-VM (Get-View $passiveVM).Name -DeletePermanently -Confirm:$false + Write-Host "Powering off & deleting Witness VM ..." + Stop-VM -VM (Get-View $witnessVM).Name -Confirm:$false | Out-Null + Remove-VM (Get-View $witnessVM).Name -DeletePermanently -Confirm:$false + } + } + } + } +} From 1b41e221161e5d2c45afd594954b341e980fff6d Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Thu, 8 Dec 2016 11:44:03 -0500 Subject: [PATCH 06/90] Datastore SIOC Statistics Collection Functions Created two functions to monitor and manage the SIOC statistic collection of a datastore --- Scripts/DatastoreSIOCStatistics.ps1 | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Scripts/DatastoreSIOCStatistics.ps1 diff --git a/Scripts/DatastoreSIOCStatistics.ps1 b/Scripts/DatastoreSIOCStatistics.ps1 new file mode 100644 index 0000000..a407506 --- /dev/null +++ b/Scripts/DatastoreSIOCStatistics.ps1 @@ -0,0 +1,108 @@ +function Get-DatastoreSIOCStatCollection { +<# +.SYNOPSIS + Gathers information on the status of SIOC statistics collection for a datastore +.DESCRIPTION + Will provide the status on a datastore's SIOC statistics collection +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Datastore + Datastore to be ran against +.EXAMPLE + Get-DatastoreSIOCStatCollection -Datastore ExampleDatastore + Retreives the status of SIOC statistics collection for the provided datastore +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + $Datastore + ) + + Process { + + #Collect information about the desired datastore/s and verify existance + $ds = Get-Datastore $datastore -warningaction silentlycontinue -erroraction silentlycontinue + if (!$ds) {Write-Warning -Message "No Datastore found"} + else { + + $report = @() + + #Loops through each datastore provided and feeds back information about the SIOC Statistics Collection status + foreach ($item in $ds) { + + $tempitem = "" | select Name,SIOCStatCollection + $tempitem.Name = $item.Name + $tempitem.SIOCStatCollection = $item.ExtensionData.IormConfiguration.statsCollectionEnabled + $report += $tempitem + + } + + #Returns the output to the user + return $report + + } + + } + +} + + +function Set-DatastoreSIOCStatCollection { +<# +.SYNOPSIS + Configures the status of SIOC statistics collection for a datastore +.DESCRIPTION + Will modify the status on a datastore's SIOC statistics collection +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Datastore + Datastore to be ran against +.EXAMPLE + Set-DatastoreSIOCStatCollection -Datastore ExampleDatastore -Enable $true + Enables SIOC statistics collection for the provided datastore +#> +[CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + $Datastore, + [Switch]$Enable, + [Switch]$Disable + ) + + Process { + + #Collect information about the desired datastore/s and verify existance + $ds = Get-Datastore $datastore -warningaction silentlycontinue -erroraction silentlycontinue + if (!$ds) {Write-Warning -Message "No Datastore found"} + else { + + $report = @() + + #Loops through each datastore provided and modifies the SIOC Statistics Collection status + foreach ($dsobj in $ds) { + + $_this = Get-View -id 'StorageResourceManager-StorageResourceManager' + $spec = New-Object vmware.vim.storageiormconfigspec + + if ($Enable) { + + $spec.statsCollectionEnabled = $true + + } elseif ($Disable) { + + $spec.statsCollectionEnabled = $false + + } + + $_this.ConfigureDatastoreIORM_Task($dsobj.ExtensionData.MoRef,$spec) | out-null + start-sleep -s 1 + $report += Get-View -Id $dsobj.ExtensionData.MoRef -Property Name,Iormconfiguration.statsCollectionEnabled | select Name,@{N='SIOCStatCollection';E={$_.Iormconfiguration.statsCollectionEnabled}} + + } + + #Returns the output to the user + return $report + } + } + +} \ No newline at end of file From b49b3f9430c9dcc973160994f9771a920bebc629 Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Thu, 8 Dec 2016 11:49:49 -0500 Subject: [PATCH 07/90] Update DatastoreSIOCStatistics.ps1 --- Scripts/DatastoreSIOCStatistics.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/DatastoreSIOCStatistics.ps1 b/Scripts/DatastoreSIOCStatistics.ps1 index a407506..0121b07 100644 --- a/Scripts/DatastoreSIOCStatistics.ps1 +++ b/Scripts/DatastoreSIOCStatistics.ps1 @@ -58,7 +58,7 @@ function Set-DatastoreSIOCStatCollection { .PARAMETER Datastore Datastore to be ran against .EXAMPLE - Set-DatastoreSIOCStatCollection -Datastore ExampleDatastore -Enable $true + Set-DatastoreSIOCStatCollection -Datastore ExampleDatastore -Enable Enables SIOC statistics collection for the provided datastore #> [CmdletBinding(SupportsShouldProcess)] @@ -105,4 +105,4 @@ function Set-DatastoreSIOCStatCollection { } } -} \ No newline at end of file +} From 1977251e8fe9dca2a14f413760e558df47e7e5c0 Mon Sep 17 00:00:00 2001 From: baoyinqiao Date: Fri, 9 Dec 2016 15:31:03 +0800 Subject: [PATCH 08/90] Add a VMEncryption module Add a module which include VMEncryption related functions/cmdlets. --- Modules/VMware.VMEncryption/README.md | 7 + .../VMware.VMEncryption.psd1 | Bin 0 -> 5090 bytes .../VMware.VMEncryption.psm1 | 2045 +++++++++++++++++ 3 files changed, 2052 insertions(+) create mode 100644 Modules/VMware.VMEncryption/README.md create mode 100644 Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 create mode 100644 Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 diff --git a/Modules/VMware.VMEncryption/README.md b/Modules/VMware.VMEncryption/README.md new file mode 100644 index 0000000..9e38900 --- /dev/null +++ b/Modules/VMware.VMEncryption/README.md @@ -0,0 +1,7 @@ +Prerequisites/Steps to use this module: + +1. This module only works for vSphere products that support VM Encryption. E.g. vSphere 6.5 and later. +2. All the functions in this module only work for KMIP Servers. +3. Install the latest version of Powershell and PowerCLI(6.5). +4. Import this module by running: Import-Module -Name "location of this module" +5. Get-Command -Module "This module Name" to list all available functions. \ No newline at end of file diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..d3106326576f014d290d1010af53ab6bbfbeac3c GIT binary patch literal 5090 zcmcgwTTc^F5T57$icNSZ8mWS)U}7TSA|!yMBKq2HFK+D3(pru2udClT)5AHZ+rrXR z(zLsK_RP#T_c{IhXH817CW)L$Um7x&jx_Pqm74SYJ~-Z*?vM42L@cA*ZYR~<-us>jcqmMy$9yVPKfYfz_H@#rUb8p#*X z@i)Vcyag*$BvnxNu`jmw8&H(hN{07J?Q;!Y)HA_%4Aga9O|aIQk>&NFoRIe{CzG@Z zUp))toyY+=j)C0#F@g*}GX}ol+JyeJO9pSzd-Q8dS3-?FijusOk78U+VhcWcf$tXR zw`E5*;Hebcp5tyC>)Tl0!1^wHSjSxxpG{!obr-T;sdsk*%zMB^>E^%NY*LbVuOSL! zjqz!g-v_@g;)L-}jg9+vpy&Z@5m_;Zb8zZ{I+488Cs;iJ-xS`65ey)E2phMdAG41+ zXz@l~w7<{DMIIoISw4ng-kWuxcm%F4i{e}iktVQa2cINB$cSeJVEhb))@npL9q2n& z1a?SzUarLv?_k5kJPY4f%|D_7CQ=)xUMwms6=~m02dfjQTDeE+CivnYDtR%je69_epXYfORS=_Qcpc&-Gud(^^W#3`j9udw)Ksl zZ#+ipoK;8*Yg{;mPz!4L78ZYW(`)zCM@^up16v0A+qI=0HTcD5l+`1ik0*~io+jON zNbKwAdIM;&sd3}|X7jPj3Hpswo%2wtzTt#9#2i3A>>jK3lAAz7^w+39eRQxeXXbh8 z5`XT#K$cY*qMqI2C zKaQqxb^_++NNvBI?0q2fT#CrOg0ESTYg!v!Wn^yMSMNNhBJw%oQ#vR5!MF3TQ&8aalB9eC;l zdB^{c68tAn#gp^xqD-ZXSz1RqD}!ge77*~CD@F~c09=^4%;R&99Y(_$Q)+T09vf>?Gl+eTDj6a4Q%+dBL%Vq+b7dgt72x`g}P Me+=e<-#Tag10n%j>;M1& literal 0 HcmV?d00001 diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 new file mode 100644 index 0000000..149fd49 --- /dev/null +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -0,0 +1,2045 @@ +# Script Module : VMware.VMEncryption +# Version : 1.0 + +# Copyright © 2016 VMware, Inc. All Rights Reserved. + +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +New-VIProperty -Name AESNIStatus -ObjectType VMHost -Value { + Param ($VMHost) + $FeatureCap = $VMHost.ExtensionData.Config.FeatureCapability + foreach ($Feature in $FeatureCap) { + if ($Feature.FeatureName -eq "cpuid.AES") { + ($Feature.Value -eq "1") + } + } +} -BasedOnExtensionProperty 'Config.FeatureCapability' -Force | Out-Null + +New-VIProperty -Name CryptoSafeSupported -ObjectType VMHost -Value { + Param ($VMHost) + $VMHost.ExtensionData.Runtime.CryptoState -ne $null +} -BasedOnExtensionProperty 'Runtime.CryptoState' -Force + +New-VIProperty -Name CryptoSafe -ObjectType VMHost -Value { + Param ($VMHost) + $VMHost.ExtensionData.Runtime.CryptoState -eq "safe" +} -BasedOnExtensionProperty 'Runtime.CryptoState' -Force + +New-VIProperty -Name Encrypted -ObjectType VirtualMachine -Value { + Param ($VM) + $VM.ExtensionData.Config.KeyId -ne $null +} -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null + +New-VIProperty -Name EncryptionKeyId -ObjectType VirtualMachine -Value { + Param ($VM) + if ($VM.Encrypted) { + $VM.ExtensionData.Config.KeyId + } +} -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null + +New-VIProperty -Name Locked -ObjectType VirtualMachine -Value { + Param ($VM) + ($vm.extensiondata.Runtime.ConnectionState -eq "invalid") -and ($vm.extensiondata.Config.KeyId) +} -BasedOnExtensionProperty 'Runtime.ConnectionState','Config.KeyId' -Force | Out-Null + +New-VIProperty -Name Encrypted -ObjectType HardDisk -Value { + Param ($hardDisk) + $hardDisk.ExtensionData.Backing.KeyId -ne $null +} -BasedOnExtensionProperty 'Backing.KeyId' -Force | Out-Null + +New-VIProperty -Name EncryptionKeyId -ObjectType HardDisk -Value { + Param ($Disk) + if ($Disk.Encrypted) { + $Disk.ExtensionData.Backing.KeyId + } +} -BasedOnExtensionProperty 'Backing.KeyId' -Force | Out-Null + +Function Enable-VMHostCryptoSafe { + <# + .SYNOPSIS + This cmdlet enables the VMHost's CryptoSate to safe. + + .DESCRIPTION + This cmdlet enables the VMHost's CryptoSate to safe. + + .PARAMETER VMHost + Specifies the VMHost you want to enable. + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID which you want to use to generate the encrytion key. + + .EXAMPLE + C:\PS>$VMHost = Get-VMHost -name $VMHostName + C:\PS>Enable-VMHostCryptoSafe -VMHost $VMHost + + Enables the specified VMHost's CryptoSate to safe. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost] $VMHost, + + [Parameter(Mandatory=$False)] + [String] $KMSClusterId + ) + + Process { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + if (!$VMHost.CryptoSafeSupported) { + Write-Error "The VMHost: $VMHost does not support CryptoSafe!`n" + return + } + + if ($VMHost.CryptoSafe) { + Write-Error "The VMHost: $VMHost CryptoSafe already enabled!`n" + return + } + + # Generate key from the specified KMS cluster + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + + $VMHostView = Get-View $VMHost + $VMHostView.ConfigureCryptoKey($KeyResult.KeyId) + } +} + +Function Set-VMHostCryptoKey { + <# + .SYNOPSIS + This cmdlet changes the VMHost CryptoKey. + + .DESCRIPTION + This cmdlet changes the VMHost CryptoKey if VMHost is already in Crypto safe state. + + .PARAMETER VMHost + Specifies the VMHost whose CryptoKey you want to update. + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID which you want to use to generate the encryption key. + + .EXAMPLE + C:\PS>$VMHost = Get-VMHost -Name $VMHostName + C:\PS>Set-VMHostCryptoKey -VMHost $VMHost + + Changes the VMHost CryptoKey to a new CryptoKey. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost] $VMHost, + + [Parameter(Mandatory=$False)] + [String] $KMSClusterId + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + if (!$VMHost.CryptoSafeSupported) { + Write-Error "The VMHost: $VMHost does not support CryptoSafe!`n" + return + } + + if (!$VMHost.CryptoSafe) { + Write-Error "The VMHost: $VMHost has not enabled the CrytoSate to safe!" + return + } + + $VMHostView = Get-View $VMHost + $OldKey = $VMHostView.Runtime.CryptoKeyId + + # Generate key from the specified KMSCluster + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + + try { + $VMHostView.ConfigureCryptoKey($KeyResult.KeyId) + Write-Verbose "Change Crypto Key on VMHost: $VMHost succeeded!`n" + } catch { + Write-Error "Change Crypto Key on VMHost: $VMHost failed.$_!`n" + return + } + + # Remove the old host key + Write-Verbose "Removing the old hostKey: $($OldKey.KeyId) on $VMHost...`n" + $VMHostCM = Get-View $VMHostView.ConfigManager.CryptoManager + $VMHostCM.RemoveKeys($OldKey, $true) + } +} + +Function Enable-VMEncryption { + <# + .SYNOPSIS + This cmdlet encrypts the specified VM. + + .DESCRIPTION + This cmdlet encrypts the specified VM. + + .PARAMETER SkipHardDisks + If specified, skips the encryption of the hard disks of the specified VM. + + .PARAMETER VM + Specifies the VM you want to encrypt. + + .PARAMETER Policy + Specifies the encryption policy you want to use. + + .PARAMETER KMSClusterId + Specifies the KMS clusterId you want to use to generate new key for encryption. + + .EXAMPLE + C:\PS>Get-VM -Name win2012|Enable-VMEncryption + + Encrypts the whole VM with default encryption policy. + + .EXAMPLE + C:\PS>$SP = Get-SpbmStoragePolicy -name "EncryptionPol" + C:\PS>Get-VM -Name win2012 |Enable-VMEncryption -Policy $SP -SkipHardDisks + + Encrypts the VM Home with the encryption policy 'EncryptionPol' and skips hard disks encryption. + + .NOTES + This cmdlet assumes there already is KMS defined in vCenter Server. + If VM Home is already encrypted, the cmdlet quits. + If VM Home is not encrypted, encrypt VM Home if SkipHardDisks specified. Otherwise encrypt the VM Home and VM-attached disks. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$False)] + [VMware.VimAutomation.Storage.Types.V1.Spbm.SpbmStoragePolicy] $Policy, + + [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$False)] + [String] $KMSClusterId, + + [Parameter(Mandatory=$False)] + [switch]$SkipHardDisks=$False + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + # VM Home is already encrypted + if ($VM.Encrypted) { + $ErrMsg = "VM $VM is already encrypted, please use: "+ + "Enable-VMDiskEncryption if you want to "+ + "encrypt disks which not encrypted yet!`n" + Write-Error $ErrMsg + return + } + + Write-Verbose "Checking if the VMHost supports CryptoSafe...`n" + $VMhost = $VM|Get-VMHost + if (!$VMHost.CryptoSafeSupported) { + Write-Error "The VMHost: $VMHost does not support CryptoSafe.`n" + return + } + + Write-Verbose "Checking if $VM has no snapshots...`n" + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshots, please remove all snapshots and try again!`n" + return + } + + Write-Verbose "Checking if $VM powered off...`n" + if ($VM.PowerState -ne "PoweredOff") { + $ErrMsg = "The VM can only be encrypted when powered off, "+ + "but the current power state of $VM is $($VM.PowerState)!`n" + Write-Error $ErrMsg + return + } + + $PolicyToBeUsed = $null + $BuiltInEncPolicy = Get-SpbmStoragePolicy -Name "VM Encryption Policy" + + if ($Policy) { + # Known issue: If the provided policy is created/cloned from + # the default "VM Encryption Policy", + # Or When creating the policy you didn't select 'Custom', + # there will be null-valued Exception. + Write-Verbose "Checking if the provided policy: $Policy is an encryption policy`n" + if (($Policy.Name -ne "VM Encryption Policy") -and !$Policy.CommonRule.Capability.Category.Contains("ENCRYPTION")) { + Write-Error "The policy $Policy is not an encryption policy, exit!" + return + } + $PolicyToBeUsed = $Policy + } else { + Write-Verbose "No storage policy specified, try to use the built-in policy.`n" + if ($BuiltInEncPolicy) { + $PolicyToBeUsed = $BuiltInEncPolicy + } else { + Throw "The built-in policy does not exist, please use: New-SpbmStoragePolicy to create one first!`n" + } + } + + # Encrypt the VM disks if SkipHardDisk not specified + if (!$SkipHardDisks) { + $Disks = $VM|Get-HardDisk + } + + $VMView = Get-View $VM + $ProfileSpec = New-Object VMware.Vim.VirtualMachineDefinedProfileSpec + $ProfileSpec.ProfileId = $PolicyToBeUsed.Id + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $VMCfgSpec.VmProfile = $ProfileSpec + + if ($KMSClusterId) { + # Generate a new key from KMS + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the specified KMS Cluster exists!`n" + } + + $CryptoKeyId = $KeyResult.KeyId + $CryptoSpec = New-Object VMware.Vim.CryptoSpecEncrypt + $CryptoSpec.CryptoKeyId = $CryptoKeyId + $VMCfgSpec.Crypto = $CryptoSpec + } + + $DeviceChanges = @() + foreach ($Disk in $Disks) { + Write-Verbose "Attaching policy: $PolicyToBeUsed to $Disk`n" + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $BackingSpec = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + $DeviceChange.Profile = $ProfileSpec + $BackingSpec.Crypto = $CryptoSpec + $DeviceChange.Backing = $BackingSpec + $DeviceChanges += $deviceChange + } + + if ($Devicechanges) { + $VMCfgSpec.deviceChange = $Devicechanges + } + + return $VMView.ReconfigVM_Task($VMCfgSpec) + } +} + +Function Enable-VMDiskEncryption { + <# + .SYNOPSIS + This cmdlet encrypts the specified hard disks. + + .DESCRIPTION + This cmdlet encrypts the specified hard disks. + + .PARAMETER VM + Specifies the VM whose hard disks you want to encrypt. + + .PARAMETER Policy + Specifies the encryption policy you want to use. + + .PARAMETER HardDisk + Specifies the hard disks you want to encrypt. + + .PARAMETER KMSClusterId + Specifies the KMS clusterId you want to use to generate new key for encryption. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$VMDisks= $VM|Get-Harddisk|Select -last 2 + C:\PS>Enable-VMDiskEncryption -VM $VM -$HardDisk $VMDisks + + Encrypts the VM disks with the default encryption policy and use the VM encryption key. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk[]] $HardDisk, + + [Parameter(Mandatory=$False)] + [VMware.VimAutomation.Storage.Types.V1.Spbm.SpbmStoragePolicy] $Policy, + + [Parameter(Mandatory=$False)] + [String] $KMSClusterId + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + Write-Verbose "Checking if $VM is encrypted..." + if (!$VM.Encrypted) { + Write-Error "$VM is not encrypted, please use:Enable-VMEncryption to encrypt the VM.`n" + return + } + + # Validate the hard disks + Write-Verbose "Checking the hard disks...`n" + ConfirmHardDiskIsValid -VM $VM -HardDisk $HardDisk + + Write-Verbose "Checking if $VM has no snapshots..." + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshots, please remove all snapshots!`n" + return + } + + Write-Verbose "Checking if $VM is powered off..." + if ($VM.powerstate -ne "PoweredOff") { + $ErrMsg = "The VM can only be ecrypted when powered off, "+ + "but the current power state of $VM is $($VM.PowerState)!`n" + Write-Error $ErrMsg + return + } + + $PolicyToBeUsed = $null + + if ($Policy) { + # Known issue: If the provided policy is created/cloned from + # the default "VM Encryption Policy", + # Or When creating the policy you didn't select 'Custom', + # there will be null-valued Exception. + Write-Verbose "Checking if the provided policy: $Policy is an encryption policy`n" + if (($Policy.Name -ne "VM Encryption Policy") -and !$Policy.CommonRule.Capability.Category.Contains("ENCRYPTION")) { + Throw "The policy $Policy is not an encryption policy, exit!" + } + $PolicyToBeUsed = $Policy + } else { + Write-Verbose "No storage policy specified, try to use the VM Home policy.`n" + $PolicyToBeUsed = (Get-SpbmEntityConfiguration -VM $VM).StoragePolicy + if (!$PolicyToBeUsed) { + Write-Warning "The VM Home policy is not available, try to use the built-in policy.`n" + $BuiltInEncPolicy = Get-SpbmStoragePolicy -Name "VM Encryption Policy" + if ($BuiltInEncPolicy) { + $PolicyToBeUsed = $BuiltInEncPolicy + } else { + Throw "The built-in policy does not exist, please use: New-SpbmStoragePolicy to create one first!`n" + } + } + } + + # Specify the key used to encrypt disk + if ($KMSClusterId) { + # Generate a new key from KMS + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + + $CryptoKeyId = $KeyResult.KeyId + $CryptoSpec = New-Object VMware.Vim.CryptoSpecEncrypt + $CryptoSpec.CryptoKeyId = $CryptoKeyId + } + + Write-Verbose "Encrypting the hard disks: $HardDisk...`n" + + $VMView = Get-View $VM + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $ProfileSpec = New-Object VMware.Vim.VirtualMachineDefinedProfileSpec + $ProfileSpec.ProfileId = $PolicyToBeUsed.Id + + $DeviceChanges = @() + + foreach ($Disk in $HardDisk) { + Write-Verbose "Attaching policy: $PolicyToBeUsed to $Disk`n" + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $BackingSpec = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + $DeviceChange.Profile = $ProfileSpec + $BackingSpec.Crypto = $CryptoSpec + $DeviceChange.Backing = $BackingSpec + $DeviceChanges += $DeviceChange + } + + if ($DeviceChanges) { + $VMCfgSpec.deviceChange = $DeviceChanges + } + + return $VMView.ReconfigVM_Task($VMCfgSpec) + } +} + +Function Disable-VMEncryption { + <# + .SYNOPSIS + This cmdlet decrypts the specified VM. + + .DESCRIPTION + This cmdlet decrypts the specified VM. + + .PARAMETER VM + Specifies the VM you want to decrypt. + + .EXAMPLE + C:\PS>Get-VM -Name win2012 | Disable-VMEncryption + + Decrypts the VM Home and all encrypted disks. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>Disable-VMEncryption -VM $VM + + Decrypts the whole VM, including the encrypted disks. + + .NOTES + If the VM is not encrypted, the cmdlet quits. + + .NOTES + Author : Carrie Yang. + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + Write-Verbose "Checking if $VM is encrypted..." + if (!$VM.Encrypted) { + Write-Error "$VM is not encrypted.`n" + return + } + + Write-Verbose "Checking if $VM has no snapshots..." + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshots, it can not be decrypted!`n" + return + } + + Write-Verbose "Checking if $VM is powered off..." + if ($VM.powerstate -ne "PoweredOff") { + $ErrMsg = "The VM can only be decrypted when powered off, "+ + "but the current power state of $VM is $($VM.PowerState)!`n" + Write-Error $ErrMsg + return + } + + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $Profile = New-Object VMware.Vim.VirtualMachineEmptyProfileSpec + $DecryptCrypto = New-Object VMware.Vim.CryptoSpecDecrypt + $DisksToDecrypt = $VM|Get-HardDisk|Where {$_.Encrypted} + + $VMCfgSpec.VmProfile = $Profile + $VMCfgSpec.Crypto = $DecryptCrypto + + $DeviceChanges = @() + foreach ($Disk in $DisksToDecrypt) { + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + $DeviceChange.Profile = $Profile + $DeviceChange.Backing = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.Backing.Crypto = $DecryptCrypto + $DeviceChanges += $DeviceChange + } + + if ($Devicechanges) { + $VMCfgSpec.deviceChange = $Devicechanges + } + + return (Get-View $VM).ReconfigVM_Task($VMCfgSpec) + } +} + +Function Disable-VMDiskEncryption { + <# + .SYNOPSIS + This cmdlet decrypts the specified hard disks in a given VM. + + .DESCRIPTION + This cmdlet decrypts the specified hard disks in a given VM. + + .PARAMETER VM + Specifies the VM which the hard disks belong to. + + .PARAMETER HardDisk + Specifies the hard disks you want to decrypt. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$HardDisk = $VM|Get-HardDisk|select -last 1 + C:\PS>Disable-VMDiskEncryption -VM $VM -HardDisk $HardDisk + + Decrypts the last hard disk in the VM. + + .NOTES + If the VM is not encrypted, the cmdlet quits. + + .NOTES + Author : Carrie Yang. + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk[]] $HardDisk + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + Write-Verbose "Checking if $VM is encrypted..." + if (!$VM.Encrypted) { + Write-Error "$VM is not encrypted.`n" + return + } + + # Validate the hard disks + Write-Verbose "Checking the hard disks...`n" + ConfirmHardDiskIsValid -VM $VM -HardDisk $HardDisk + + $DisksToDecrypt = $HardDisk |Where {$_.Encrypted} + + if ($DisksToDecrypt.Length -eq 0) { + Write-Error "The provided disks are not encrypted.`n" + return + } + + Write-Verbose "Checking if $VM has no snapshots..." + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshots, it can not be decrypted!`n" + return + } + + Write-Verbose "Checking if $VM is powered off..." + if ($VM.powerstate -ne "PoweredOff") { + $ErrMsg = "The VM can only be decrypted when powered off, "+ + "but the current power state of $VM is $($VM.PowerState)!`n" + Write-Error $ErrMsg + return + } + + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $Profile = New-Object VMware.Vim.VirtualMachineEmptyProfileSpec + $DecryptCrypto = New-Object VMware.Vim.CryptoSpecDecrypt + + + $DeviceChanges = @() + foreach ($Disk in $DisksToDecrypt) { + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + $DeviceChange.Profile = $Profile + $DeviceChange.Backing = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.Backing.Crypto = $DecryptCrypto + $DeviceChanges += $DeviceChange + } + + $VMCfgSpec.deviceChange = $DeviceChanges + + return (Get-View $VM).ReconfigVM_Task($VMCfgSpec) + } +} + +Function Set-VMEncryptionKey { + <# + .SYNOPSIS + This cmdlet sets the encryption key of VM or hard disks. + + .DESCRIPTION + This cmdlet sets the encryption key of VM or hard disks. + + .PARAMETER VM + Specifies the VM you want to rekey. + + .PARAMETER KMSClusterId + Specifies the KMS clusterId you want to use for getting a new key for rekey operation. + + .PARAMETER Deep + When it's specified, both the key encryption key (KEK) and + the internal data encryption key (DEK) will be updated. + This is implemented through a full copy; It's a slow operation that + must be performed while the virtual machine is powered off. + A shallow key change will only update the KEK and the operation can be performed + while the virtual machine is running. + + .PARAMETER SkipHardDisks + Skip updating the hard disk keys. + + .EXAMPLE + C:\PS>Get-VM -Name win2012 | Set-VMEncryptionKey + + Rekeys the VM win2012 VM Home and all its disks. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$VM|Set-VMEncryptionKey -SkipHardDisks + + Rekeys the VM Home only. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$VM|Set-VMEncryptionKey -Deep + + Rekeys the VM Home and all its disks with Deep option. + + .EXAMPLE + C:\PS>$KMSCluster = Get-KMSCluster | select -last 1 + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$VM|Set-VMEncryptionKey -KMSClusterId $KMSCluster.Id -Deep + + Deep rekeys the VM Home and all its disks using a new key. + The key is generted from the KMS whose clusterId is $KMSCluster.Id. + + .NOTES + This cmdlet assumes there is already a KMS in vCenter Server. If VM is not encrypted, the cmdlet quits. + You should use Enable-VMEncryption cmdlet to encrypt the VM first. + + .NOTES + Author : Carrie Yang. + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$False)] + [String] $KMSClusterId, + + [Parameter(Mandatory=$False)] + [switch]$Deep = $FALSE, + + [Parameter(Mandatory=$False)] + [switch]$SkipHardDisks = $False + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + Write-Verbose "Checking if $VM is encrypted...`n" + if (!$VM.Encrypted) { + Write-Error "$VM is not encrypted." + return + } + + Write-Verbose "Checking if $VM has no snapshots...`n" + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshot, please remove all snapshots and try again!`n" + return + } + + if ($Deep) { + Write-Verbose "Checking if $VM powered off...`n" + if ($VM.powerstate -ne "PoweredOff") { + $ErrMsg = "The VM can only be recrypted when powered off, "+ + "but the current power state of $VM is $($VM.PowerState)!`n" + Write-Error $ErrMsg + return + } + } + + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $ProfileSpec = New-Object VMware.Vim.VirtualMachineDefinedProfileSpec + $CryptoSpec = New-Object VMware.Vim.CryptoSpecShallowRecrypt + + if ($Deep) { + $CryptoSpec = New-Object VMware.Vim.CryptoSpecDeepRecrypt + $VMPolicy = (Get-SpbmEntityConfiguration -VM $VM).StoragePolicy + $ProfileSpec.ProfileId = $VMPolicy.Id + $VMCfgSpec.VmProfile = $ProfileSpec + } + + # Generate a key from KMS + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + $CryptoSpec.NewKeyId = $KeyResult.KeyId + $VMCfgSpec.Crypto = $CryptoSpec + + if (!$SkipHardDisks) { + $DisksToRecrypt = $VM|Get-HardDisk|Where {$_.Encrypted} + + $DeviceChanges = @() + foreach ($disk in $DisksToRecrypt) { + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + if ($Deep) { + $DiskProfileSpec = New-Object VMware.Vim.VirtualMachineDefinedProfileSpec + $DiskProfileSpec.ProfileId = ($Disk|Get-SpbmEntityConfiguration).StoragePolicy.Id + $DeviceChange.Profile = $DiskProfileSpec + } + $DeviceChange.Backing = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.Backing.Crypto = $CryptoSpec + $DeviceChanges += $DeviceChange + } + + if ($DeviceChanges.Length -gt 0) { + $VMCfgSpec.deviceChange = $DeviceChanges + } + } + + return (Get-View $VM).ReconfigVM_Task($VMCfgSpec) + } +} + +Function Set-VMDiskEncryptionKey { + <# + .SYNOPSIS + This cmdlet sets the encryption key of the hard disks in the VM. + + .DESCRIPTION + This cmdlet sets the encryption key of the hard disks in the VM. + + .PARAMETER VM + Specifies the VM from which you want to rekey its disks. + + .PARAMETER HardDisk + Specifies the hard disks you want to rekey. + + .PARAMETER KMSClusterId + Specifies the KMS clusterId you want to use for getting a new key for rekey operation. + + .PARAMETER Deep + When it's specified, both the key encryption key (KEK) and + the internal data encryption key (DEK) will be updated. + This is implemented through a full copy; It's a slow operation that + must be performed while the virtual machine is powered off. + A shallow key change will only update the KEK and the operation can be performed + while the virtual machine is running. + + .EXAMPLE + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$HardDisk = $VM|Get-HardDisk|select -last 2 + C:\PS>Set-VMDiskEncryptionKey -VM $VM -HardDisk $HardDisk + + Rekeys the last 2 hard disks in the VM. + + .EXAMPLE + C:\PS>$VM=Get-VM -Name win2012 + C:\PS>$HardDisk = get-vm $vm|Get-HardDisk|Select -last 2 + C:\PS>Set-VMDiskEncryptionKey -VM $VM -HardDisk $HardDisk -Deep + + Deep rekeys the last 2 hard disks in the VM. + + .EXAMPLE + C:\PS>$KMSCluster = Get-KMSCluster | select -last 1 + C:\PS>$VM = Get-VM -Name win2012 + C:\PS>$HardDisk = get-vm $vm|Get-HardDisk + C:\PS>$HardDisk|$Set-VMEncryptionKey -VM $VM -KMSClusterId $KMSCluster.Id -Deep + + Deep rekeys all the disks of the $VM using a new key. + The key is generted from the KMS whose clusterId is $KMSCluster.Id. + + .NOTES + This cmdlet assumes there is already a KMS in vCenter Server. + If VM is not encrypted, the cmdlet quits. + You should use Enable-VMEncryption cmdlet to encrypt the VM first. + + .NOTES + Author : Carrie Yang. + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk[]] $HardDisk, + + [Parameter(Mandatory=$False)] + [String] $KMSClusterId, + + [Parameter(Mandatory=$False)] + [switch]$Deep = $FALSE + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + Write-Verbose "Checking if $VM is encrypted...`n" + if (!$VM.Encrypted) { + Write-Error "$VM is not encrypted." + return + } + + # Valid the hard disks + Write-Verbose "Checking the hard disks...`n" + ConfirmHardDiskIsValid -VM $VM -HardDisk $HardDisk + + Write-Verbose "Checking if $VM has no snapshots...`n" + if ($VM|Get-Snapshot) { + Write-Error "$VM has snapshot, please remove all snapshots and try again!`n" + return + } + + if ($Deep) { + Write-Verbose "Checking if $VM powered off...`n" + if ($VM.powerstate -ne "PoweredOff") { + $ErrMsg = "Deep rekey could be done only when VM powered off,"+ + "but current VM power state is: $($VM.powerstate)!`n" + Write-Error $ErrMsg + return + } + } + + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $CryptoSpec = New-Object VMware.Vim.CryptoSpecShallowRecrypt + if ($Deep) { + $CryptoSpec = New-Object VMware.Vim.CryptoSpecDeepRecrypt + } + + # Generate a key from KMS + try { + $KeyResult = NewEncryptionKey -KMSClusterId $KMSClusterId + } catch { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + $CryptoSpec.NewKeyId = $KeyResult.KeyId + + $DeviceChanges = @() + foreach ($disk in $HardDisk) { + $DeviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $DeviceChange.operation = "edit" + $DeviceChange.device = $Disk.extensiondata + if ($Deep) { + $ProfileSpec = New-Object VMware.Vim.VirtualMachineDefinedProfileSpec + $ProfileSpec.ProfileId = ($Disk|Get-SpbmEntityConfiguration).StoragePolicy.Id + $DeviceChange.Profile = $ProfileSpec + } + $DeviceChange.Backing = New-Object VMware.Vim.VirtualDeviceConfigSpecBackingSpec + $DeviceChange.Backing.Crypto = $CryptoSpec + $DeviceChanges += $DeviceChange + } + + $VMCfgSpec.deviceChange = $DeviceChanges + + return (Get-View $VM).ReconfigVM_Task($VMCfgSpec) + } +} + +Function Get-VMEncryptionInfo { + <# + .SYNOPSIS + This cmdlet gets the encryption information of VM and its disks. + + .DESCRIPTION + This cmdlet gets the encryption information of VM and its disks. + + .PARAMETER VM + Specifies the VM for which you want to retrieve the encryption information. + + .PARAMETER HardDisk + Specifies the hard disks for which you want to retrieve the encryption information. + + .EXAMPLE + C:\PS>Get-VM|Get-VMEncryptionInfo + + Retrieves all VM's encryption information. + + .EXAMPLE + C:\PS>Get-VMEncryptionInfo -VM $vm -HardDisk $HardDisks + + Retrieves only disks' encryption information. + + .NOTES + If $HardDisk is specified, then only the encryption information of the disks specified in $HardDisk is obtained. + Otherwise, all disks' encryption information of the specified VM is returned. + + .NOTES + Author : Carrie Yang. + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk[]] $HardDisk + ) + + Process { + $DisksInfo = @() + + if ($HardDisk) { + # Validate the hard disks + Write-Verbose "Checking the hard disks...`n" + ConfirmHardDiskIsValid -VM $VM -HardDisk $HardDisk + } + + foreach ($DK in $HardDisk) { + $DKInfo = @{} + $DKInfo.index = $DK.ExtensionData.Key + $DKInfo.label = $DK.ExtensionData.DeviceInfo.Label + $diskSize = $DK.ExtensionData.CapacityInKB + $formattedSize = "{0:N0}" -f $diskSize + $DKInfo.summary = "$formattedSize KB" + $DKInfo.profile = ($DK|Get-SpbmEntityConfiguration).StoragePolicy + $DKInfo.fileName = $DK.Filename + $DKInfo.uuid = $DK.ExtensionData.Backing.Uuid + $DKInfo.keyId = $DK.ExtensionData.Backing.KeyId + $DKInfo.iofilter = $DK.ExtensionData.Iofilter + $DisksInfo += $DKInfo + } + + $VMInfo = @{} + $VMInfo.name = $VM.Name + $VMInfo.connectState = $VM.ExtensionData.Runtime.ConnectionState + $VMInfo.profile = ($VM | Get-SpbmEntityConfiguration).StoragePolicy + $VMInfo.keyId = $VM.ExtensionData.Config.KeyId + $VMInfo.disks = $DisksInfo + + return $VMInfo + } +} + +Function Get-EntityByCryptoKey { + <# + .SYNOPSIS + This cmdlet gets all the related objects in which it has the key associated. + + .DESCRIPTION + This cmdlet gets all the related objects in which it has the key associated. + + .PARAMETER KeyId + Specifies the KeyId string. + + .PARAMETER KMSClusterId + Specifies the KMSClusterId string. + + .PARAMETER SearchVMHosts + Specifies whether to search the VMHosts. + + .PARAMETER SearchVMs + Specifies whether to search the VMs. + + .PARAMETER SearchDisks + Specifies whether to search the HardDisks. + + .EXAMPLE + C:\PS>Get-EntityByCryptoKeyId -SearchVMHosts -KeyId 'keyId' + + Gets the VMHosts whose CryptoKeyId's KeyId matches exactly the 'keyId'. + + .EXAMPLE + C:\PS>Get-EntityByCryptoKeyId -SearchVMs -KMSClusterId 'clusterId' + + Gets the VMs whose CryptoKeyId's ProfileId.Id matches exactly the 'clusterId'. + + .EXAMPLE + C:\PS>Get-EntityByCryptoKey -SearchVMHosts -SearchVMs -KMSClusterId 'clusterId' + + Gets VMHosts and VMs whose CryptoKeyId's ProviderId.Id matches the 'clusterId'. + + .NOTES + At least one of the KeyId and KMSClusterId parameters is required. + If the SearchVMHosts, SearchVMs and SearchDisks all not specified, the cmdlet return $null. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$false,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [String] $keyId, + + [Parameter(Mandatory=$false,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [String] $KMSClusterId, + + [Parameter(Mandatory=$False)] + [switch] $SearchVMHosts, + + [Parameter(Mandatory=$False)] + [switch] $SearchVMs, + + [Parameter(Mandatory=$False)] + [switch] $SearchDisks + ) + + if (!$KeyId -and !$KMSClusterId) { + Throw "One of the keyId or KMSClusterId must be specified!`n" + } + + # The returned Items + $Entities = @{} + + # Find VMHosts + $CryptoSafeVMHosts = Get-VMHost|Where {$_.CryptoSafe} + + # Quit if no VMHosts found. + if (!$CryptoSafeVMHosts) { + Throw "No VMHosts enabled the CrytoState to Safe!`n" + } + + if ($SearchVMHosts) { + Write-Verbose "Starting to search VMHosts...`n" + $VMHostList = $CryptoSafeVMHosts| Where {$_.ExtensionData.Runtime.CryptoKeyId|MatchKeys -KeyId $KeyId -KMSClusterId $KMSClusterId} + $Entities.VMHostList = $VMHostList + } + + # Find the VMs which encrypted: Look for both VMHome and Disks + $VMs = Get-VM|Where {$_.Encrypted} + if ($SearchVMs) { + Write-Verbose "Starting to search VMs...`n" + $VMList = @() + $Disks = $VMs|Get-HardDisk|Where {$_.Encrypted} + $VMDiskList = $Disks|Where {$_.EncryptionKeyId|MatchKeys -KeyId $keyId -KMSClusterId $KMSClusterId} + + $VMList += $VMs|Where {$_.EncryptionKeyId|MatchKeys -KeyId $keyId -KMSClusterId $KMSClusterId} + $VMList += $VMDiskList.Parent + $VMList = $VMList|sort|Get-Unique + $Entities.VMList = $VMList + } + + # Find the Disks + if ($SearchDisks) { + Write-Verbose "Starting to search Disks...`n" + if ($SearchVMs) { + $DiskList = $VMDiskList + } else { + $Disks = $VMs|Get-HardDisk|Where {$_.Encrypted} + $DiskList = $Disks|Where {$_.EncryptionKeyId|MatchKeys -KeyId $keyId -KMSClusterId $KMSClusterId} + } + + $Entities.DiskList = $DiskList + } + + return $Entities +} + +Function New-KMServer { + <# + .SYNOPSIS + This cmdlet adds a Key Management Server. + + .DESCRIPTION + This cmdlet adds a Key Management Server to vCenter Server and verifies it. + + .PARAMETER KMServer + Specifies the Key Management Server IP address or FQDN. + + .PARAMETER KMSClusterId + Specifies the ID of the KMS cluster. KMSs with the same cluster ID are in one cluster and provide the same keys for redundancy. + + .PARAMETER UserName + Specifies user name to authenticate to the KMS. + + .PARAMETER Password + Specifies password to authenticate to the KMS. + + .PARAMETER Name + Specifies the name of the KMS. + + .PARAMETER Port + Specifies the port of the KMS. + + .PARAMETER ProxyServer + Specifies the address of the proxy server. + + .PARAMETER ProxyPort + Specifies the port of the proxy server. + + .PARAMETER Protocol + Specifies the KMS library protocol handler, for example KMS1. + + .EXAMPLE + C:\PS>New-KMServer -KMServer 1.1.1.1 -KMSClusterId clsName -UserName "YourKMSUserName" -Password '***' -Name "KMS1" + + Adds the Key Management Server 1.1.1.1 into vCenter with the cluster name 'clsname' and KMS name 'KMS1'. + + .NOTES + This cmdlet only supports PyKMIP Server. For other KMS vendors, modify the script accordingly. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [String]$KMServer, + + [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [String]$KMSClusterId, + + [Parameter(Mandatory=$False)] + [String] $UserName, + + [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [String] $Name, + + [Parameter(Mandatory=$False)] + [String] $Password, + + [Parameter(Mandatory=$False)] + [Int] $Port=5696, + + [Parameter(Mandatory=$False)] + [String] $ProxyServer, + + [Parameter(Mandatory=$False)] + [Int] $ProxyPort, + + [Parameter(Mandatory=$False)] + [String] $Protocol + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + } + + Process { + if ([string]::IsNullOrWhiteSpace($KMSClusterId)) { + Write-Error "The KMSClusterId parameter is mandatory, please specify a valid value!`n" + return + } + + if ([string]::IsNullOrWhiteSpace($KMServer)) { + Write-Error "The KMServer parameter is mandatory, please specify a valid value!`n" + return + } + + if ([string]::IsNullOrWhiteSpace($Name)) { + Write-Error "The KMSName parameter is mandatory. Please specify a valid value!`n" + return + } + + Write-Verbose "Starting to add Key Management Server: $KMServer......`n" + # Construct KMServerInfo and Spec + $KMServerInfo = New-Object VMware.Vim.KmipServerInfo + $KMServerSpec = New-Object VMware.Vim.KmipServerSpec + $KMServerInfo.Address = $KMServer + $KMServerInfo.Name = $Name + + if ($UserName) { + $KMServerInfo.UserName = $UserName + } + + if ($KMSPassword) { + $KMServerSpec.Password = $Password + } + + if ($Port) { + $KMServerInfo.Port = $Port + } + + if ($ProxyServer) { + $KMServerInfo.ProxyAddress = $ProxyServer + } + + if ($ProxyPort) { + $KMServerInfo.ProxyPort = $ProxyPort + } + + if ($Protocol) { + $KMServerInfo.Protocol = $Protocol + } + + $ProviderID = New-Object VMware.Vim.KeyProviderId + $ProviderID.Id = $KMSClusterId + $KMServerSpec.ClusterId = $ProviderID + $KMServerSpec.Info = $KMServerInfo + + Write-Verbose "Registering $KMServer to vCenter Server....`n" + + try { + $CM.RegisterKmipServer($KMServerSpec) + } catch { + Write-Error "Exception: $_ !" + return + } + + Write-Verbose "Establishing trust between vCenter Server and the Key Management Server: $KMServer`n" + try { + $KMServerCert = $CM.RetrieveKmipServerCert($providerID,$KMServerInfo) + $CM.UploadKmipServerCert($providerID,$KMServerCert.Certificate) + } catch { + Write-Error "Error occurred while retrieveing and uploading certification!`n" + return + } + + $CM.updateviewdata() + if (!(Get-DefaultKMSCluster) -and + ($CM.KmipServers|foreach {$_.servers}|foreach {$_.Address}) -contains $KMServer) { + Write-Verbose "No default Key Management Server yet. Marking $KMServer as default!`n" + Set-DefaultKMSCluster -KMSClusterId $ProviderID.Id + } + + Write-Verbose "Verifying KMS registration.....`n" + $CM.updateviewdata() + $KMServers = $CM.Kmipservers|where {($_.servers|foreach {$_.Address}) -contains $KMServer} + if ($KMServers) { + Write-Verbose "Key Management Server registered successfully!`n" + $KMServers + } else { + Write-Error "Key Management Server registration failed!`n" + } + } +} + +Function Remove-KMServer { + <# + .SYNOPSIS + This cmdlet removes a Key Management Server. + + .DESCRIPTION + This cmdlet removes a Key Management Server from vCenter Server. + + .PARAMETER Name + Specifies the name or alias of the Key Management Server. + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID string to be used as Key Management Server cluster. + + .EXAMPLE + C:\PS>Remove-KMServer -KMSClusterId "ClusterIdString" -KMSName "KMServerName" + + Removes the KMS from vCenter Server which has the KMS name and KMS cluster ID. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True)] + [String]$KMSClusterId, + + [Parameter(Mandatory=$True)] + [String]$Name + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + } + + Process { + if ([string]::IsNullOrWhiteSpace($Name) -Or + [string]::IsNullOrWhiteSpace($KMSClusterId)) { + $ErrMsg = "The KMSName and KMSClusterId parameters are mandatory "+ + "and should not be null or empty!`n" + Write-Error $ErrMsg + return + } + + $KMServers = $CM.KmipServers + if (!$KMServers) { + Write-Error "There are no Key Managerment Servers in vCenter Server!`n" + return + } + + if ($KMServers|Where { ($_.ClusterId.Id -eq $KMSClusterId) -and ($_.Servers|Where {$_.Name -eq $Name})}) { + #Start to remove the specified Km Server + try { + $ProviderID = New-Object VMware.Vim.KeyProviderId + $ProviderID.Id = $KMSClusterId + $CM.RemoveKmipServer($providerID, $Name) + } catch { + Write-Error "Exception: $_!`n" + return + } + } else { + $KMSNotFounErrMsg = "Cannot find the KMS with Name:$Name and KMS ClusterId:$KMSClusterId,"+ + "please make sure you specified correct parameters!`n" + Write-Error $KMSNotFounErrMsg + return + } + } +} + +Function Get-KMSCluster { + <# + .SYNOPSIS + This cmdlet retrieves all KMS clusters. + + .DESCRIPTION + This cmdlet retrieves all KMS clusters. + + .EXAMPLE + C:\PS>Get-KMSCluster + + Retrieves all KMS clusters. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + + # Get all KMS Clusters + return $CM.KmipServers.ClusterId +} + +Function Get-KMSClusterInfo { + <# + .SYNOPSIS + This cmdlet retrieves the KMS cluster information. + + .DESCRIPTION + This cmdlet retrieves the KMS cluster Information by providing the KMS cluster ID string. + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID. + + .EXAMPLE + C:\PS>Get-KMSClusterInfo + + Retrieves all KMS cluster information. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [String] $KMSClusterId + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + } + + Process { + # Get all Km Clusters if no KMSClusterId specified + if (!$KMSClusterId) { + return $CM.KmipServers + } + return $CM.KmipServers|where {$_.ClusterId.Id -eq $KMSClusterId} + } +} + +Function Get-KMServerInfo { + <# + .SYNOPSIS + This cmdlet retireves the Key Management Servers' information. + + .DESCRIPTION + This cmdlet retireves the Key Management Servers' information by providing the KMS cluster ID string. + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID. + + .EXAMPLE + C:\PS>Get-KMServerInfo + + Retrieves information about all Key Management Servers. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [String] $KMSClusterId + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + } + + Process { + # Get all KMS Info if no clusterId specified + if ($KMSClusterId) { + $FindCluster = (Get-KMSCluster).Contains($KMSClusterId) + if (!$FindCluster) { + Write-Error "Cannot find the specified KMS ClusterId in vCenter Server!" + return + } + + $ClsInfo = Get-KMSClusterInfo -KMSClusterId $KMSClusterId + + return $ClsInfo.Servers + } + + return $CM.KmipServers.Servers + } +} + +Function Get-KMServerStatus { + <# + .SYNOPSIS + This cmdlet retrieves the KMS status. + + .DESCRIPTION + This cmdlet retrieves the KMS status by providing the KMS cluster ID String + + .PARAMETER KMSClusterId + Specifies the KMS cluster ID from which to retrieve the servers' status. + + .EXAMPLE + C:\PS>Get-KMServerStatus -KMSClusterId 'ClusterIdString' + + Retrieves the specified KMS cluster 'ClusterIdString' server status. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [String] $KMSClusterId + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + } + + Process { + $ClusterInfo = @() + if ($KMSClusterId) { + # Quit if the ClusterID cannot be found + $FindCluster = (Get-KMSCluster).Contains($KMSClusterId) + if (!$FindCluster) { + Write-Error "Cannot find the specified KMS ClusterId in vCenter Server!" + return + } + + $ClsInfo = New-Object VMware.Vim.KmipClusterInfo + $ProviderId = New-Object VMware.Vim.KeyProviderId + $ProviderId.Id = $KMSClusterId + $ClsInfo.ClusterId = $providerId + $ClsInfo.Servers = (Get-KMSClusterInfo -KMSClusterId $KMSClusterId).Servers + $ClusterInfo += $ClsInfo + $KMSClsStatus = $CM.RetrieveKmipServersStatus($ClusterInfo) + } else { + $ClusterInfo = Get-KMSClusterInfo + $KMSClsStatus = $CM.RetrieveKmipServersStatus($ClusterInfo) + } + + if ($KMSClsStatus) { + return $KMSClsStatus + } else { + Write-Error "Failed to get the KMS status`n" + return $null + } + } +} + +Function Get-DefaultKMSCluster { + <# + .SYNOPSIS + This cmdlet retrieves the default KMS cluster. + + .DESCRIPTION + This cmdlet retrieves the default KMS cluster. + + .EXAMPLE + C:\PS>Get-DefaultKMSCluster + + Retrieves the default KMS cluster. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + + return ($CM.KmipServers|where {$_.UseAsDefault}).ClusterId.Id +} + +Function Set-DefaultKMSCluster { + <# + .SYNOPSIS + This cmdlet sets the provided KMS cluster as the default KMS cluster. + + .DESCRIPTION + This cmdlet sets the provided KMS cluster as the default KMS cluster. + + .PARAMETER KMSClusterId + Specifies KMS cluster ID which will be used to mark as default KMS cluster. + + .EXAMPLE + C:\PS>Set-DefaultKMSCluster -KMSClusterId 'ClusterIdString' + + Sets the KMS cluster whose cluster ID is 'ClusterIdString' as the default KMS cluster. + + .NOTES + Author : Baoyin Qiao. + Author email : bqiao@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True)] + [String] $KMSClusterId + ) + + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + $ProviderId = New-Object VMware.Vim.KeyProviderId + $ProviderId.Id = $KMSClusterId + + $CM.MarkDefault($ProviderId) +} + +Function ConfirmIsVCenter{ + <# + .SYNOPSIS + This function confirms the connected VI server is vCenter Server. + + .DESCRIPTION + This function confirms the connected VI server is vCenter Server. + + .EXAMPLE + C:\PS>ConfirmIsVCenter + + Throws exception if the connected VIServer is not vCenter Server. + #> + + $SI = Get-View Serviceinstance + $VIType = $SI.Content.About.ApiType + + if ($VIType -ne "VirtualCenter") { + Throw "Operation requires vCenter Server!" + } +} + +Function ConfirmHardDiskIsValid { + <# + .SYNOPSIS + This function confirms the hard disks is valid. + + .DESCRIPTION + This function confirms the hard disks is valid. + + .PARAMETER VM + Specifies the VM which you want to used to validate against. + + .PARAMETER HardDisk + Specifies the hard disks which you want to use to validate. + + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$True)] + [VMware.VimAutomation.ViCore.Types.V1.VirtualDevice.HardDisk[]] $HardDisk + ) + + $NonVMHardDisks = $HardDisk|Where {$_.Parent -ne $VM} + + if ($NonVMHardDisks.Length -ge 1) { + Throw "Some of the provided hard disks: $($NonVMHardDisks.FileName) do not belong to VM: $VM`n" + } +} + +Function MatchKeys { + <# + .SYNOPSIS + This function checks whether the given keys matched or not. + + .DESCRIPTION + This function checks whether the given keys matched or not with the provided KeyId or KMSClusterId. + + .PARAMETER KeyToMatch + Specifies the CryptoKey to match for. + + .PARAMETER KeyId + Specifies the keyId should be matched. + + .PARAMETER KMSClusterId + Specifies the KMSClusterId should be matched. + + .NOTES + Returns the true/false depends on the match result. + One of keyId or KMSClusterId parameter must be specified. + #> + + [CmdLetBinding()] + + Param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True)] + [VMware.Vim.CryptoKeyId] $KeyToMatch, + + [Parameter(Mandatory=$false)] + [String] $KeyId, + + [Parameter(Mandatory=$false)] + [String] $KMSClusterId + ) + + Process { + if (!$KeyId -and !$KMSClusterId) { + Throw "One of the keyId or KMSClusterId must be specified!`n" + } + + $Match = $True + if ($KeyId -and ($KeyId -ne $KeyToMatch.KeyId)) { + $Match = $false + } + + if ($KMSClusterId) { + if (!$KeyToMatch.ProviderId) { + $Match = $false + } + + if ($KMSClusterId -ne $KeyToMatch.ProviderId.Id) { + $Match = $false + } + } + return $Match + } +} + +Function NewEncryptionKey { + <# + .SYNOPSIS + This function generates new encryption key from KMS. + + .DESCRIPTION + This function generates new encryption from KMS, if no KMSClusterId specified the default KMS will be used. + + .PARAMETER KMSClusterId + Specifies the KMS cluster id. + + .EXAMPLE + C:\PS>NewEncryptionKey -KMSClusterId 'ClusterIdString' + + Generates a new encryption key from the specified KMS which cluster id is 'ClusterIdString'. + #> + + Param ( + [Parameter(Mandatory=$False)] + [String]$KMSClusterId + ) + + # Confirm the connected VIServer is vCenter + ConfirmIsVCenter + + # Get the cryptoManager of vCenter Server + $CM = GetCryptoManager + $ProviderId = New-Object VMware.Vim.KeyProviderId + + Write-Verbose "Generate a CryptoKey.`n" + if ($KMSClusterId) { + $ProviderId.Id = $KMSClusterId + } else { + $ProviderId = $null + } + + $KeyResult = $CM.GenerateKey($ProviderId) + if (!$keyResult.Success) { + Throw "Key generation failed, make sure the KMS Cluster exists!`n" + } + return $KeyResult +} + +Function GetCryptoManager { + <# + .SYNOPSIS + This function retrieves the cryptoManager according to the given type. + + .DESCRIPTION + This function retrieves the cryptoManager according to the given type. + + .PARAMETER Type + Specifies the type of CryptoManager instance to get, the default value is KMS. + + .EXAMPLE + C:\PS>GetCryptoManager -Type "CryptoManagerKmip" + + Retrieves the 'CryptoManagerKmip' type CryptoManager. + #> + + Param ( + [Parameter(Mandatory=$false)] + [String] $Type + ) + + Process { + $SI = Get-View Serviceinstance + $CM = Get-View $SI.Content.CryptoManager + $cryptoMgrType = $CM.GetType().Name + + if (!$Type) { + # As the type is not cared, so return the CM directly + return $CM + } + if ($cryptoMgrType -eq $Type) { + return $CM + } + + Throw "Failed to get CryptoManager instance of the required type {$Type}!" + } +} + +Export-ModuleMember *-* From 554f6d691d7c2d461465e0c72f4d4ae033b313d8 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 13 Dec 2016 17:20:51 +0530 Subject: [PATCH 09/90] Get-HVMachine, Get-HVMachineSummary changes Adding new advanced functions Get-HVMachine and Get-HVMachineSummary. Queries and returns virtual machines information, the machines list would be determined based on queryable fields poolName, dnsName, machineName, state. When more than one fields are used for query the virtual machines which satisfy all fields criteria would be returned. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 347 +++++++++++++++++- 1 file changed, 330 insertions(+), 17 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 6884703..2a8b70a 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -242,7 +242,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A try { $desktopPool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVPool advanced function is loaded, $_" + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($desktopPool) { @@ -1087,7 +1087,7 @@ function Find-HVFarm { 'farmType' = 'data.type'; } - $parms = $Param + $params = $Param $query_service_helper = New-Object VMware.Hv.QueryServiceService $query = New-Object VMware.Hv.QueryDefinition @@ -1097,10 +1097,10 @@ function Find-HVFarm { $query.queryEntityType = 'FarmSummaryView' [VMware.Hv.queryfilter[]]$filterSet = @() foreach ($setting in $farmSelectors.Keys) { - if ($null -ne $parms[$setting]) { + if ($null -ne $params[$setting]) { $equalsFilter = New-Object VMware.Hv.QueryFilterEquals $equalsFilter.memberName = $farmSelectors[$setting] - $equalsFilter.value = $parms[$setting] + $equalsFilter.value = $params[$setting] $filterSet += $equalsFilter } } @@ -1370,17 +1370,17 @@ function Find-HVPool { 'provisioningEnabled' = 'desktopSummaryData.provisioningEnabled' } - $parms = $Param + $params = $Param $query_service_helper = New-Object VMware.Hv.QueryServiceService $query = New-Object VMware.Hv.QueryDefinition $wildCard = $false #Only supports wild card '*' - if ($parms['PoolName'] -and $parms['PoolName'].contains('*')) { + if ($params['PoolName'] -and $params['PoolName'].contains('*')) { $wildcard = $true } - if ($parms['PoolDisplayName'] -and $parms['PoolDisplayName'].contains('*')) { + if ($params['PoolDisplayName'] -and $params['PoolDisplayName'].contains('*')) { $wildcard = $true } # build the query values @@ -1388,10 +1388,10 @@ function Find-HVPool { if (! $wildcard) { [VMware.Hv.queryfilter[]]$filterSet = @() foreach ($setting in $poolSelectors.Keys) { - if ($null -ne $parms[$setting]) { + if ($null -ne $params[$setting]) { $equalsFilter = New-Object VMware.Hv.QueryFilterEquals $equalsFilter.memberName = $poolSelectors[$setting] - $equalsFilter.value = $parms[$setting] + $equalsFilter.value = $params[$setting] $filterSet += $equalsFilter } } @@ -1408,11 +1408,11 @@ function Find-HVPool { $queryResults = $query_service_helper.QueryService_Query($services,$query) $strFilterSet = @() foreach ($setting in $poolSelectors.Keys) { - if ($null -ne $parms[$setting]) { + if ($null -ne $params[$setting]) { if ($wildcard -and (($setting -eq 'PoolName') -or ($setting -eq 'PoolDisplayName')) ) { - $strFilterSet += '($_.' + $poolSelectors[$setting] + ' -like "' + $parms[$setting] + '")' + $strFilterSet += '($_.' + $poolSelectors[$setting] + ' -like "' + $params[$setting] + '")' } else { - $strFilterSet += '($_.' + $poolSelectors[$setting] + ' -eq "' + $parms[$setting] + '")' + $strFilterSet += '($_.' + $poolSelectors[$setting] + ' -eq "' + $params[$setting] + '")' } } } @@ -3062,7 +3062,7 @@ function New-HVPool { try { $sourcePool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVPool advanced function is loaded, $_" + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($sourcePool) { @@ -3888,7 +3888,7 @@ function Remove-HVPool { try { $myPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVPool advanced function is loaded, $_" + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($myPools) { @@ -4258,7 +4258,7 @@ function Set-HVPool { try { $desktopPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVPool advanced function is loaded, $_" + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($desktopPools) { @@ -4767,7 +4767,7 @@ function Start-HVPool { try { $poolObj = Get-HVPoolSummary -poolName $item -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVPool advanced function is loaded, $_" + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($poolObj) { @@ -4943,4 +4943,317 @@ function Get-HVTaskSpec { return $spec } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool +function Find-HVMachine { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Param + ) + + $params = $Param + + try { + if ($params['PoolName']) { + $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -hvServer $params['HvServer'] + if ($poolObj.Length -ne 1) { + Write-Error "Failed to retrieve specific pool object with given PoolName : $params['PoolName']" + break; + } else { + $desktopId = $poolObj.Id + } + } + } catch { + Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" + break + } + # + # This translates the function arguments into the View API properties that must be queried + $machineSelectors = @{ + 'PoolName' = 'base.desktop'; + 'MachineName' = 'base.name'; + 'DnsName' = 'base.dnsName'; + 'State' = 'base.basicState'; + } + + + $query_service_helper = New-Object VMware.Hv.QueryServiceService + $query = New-Object VMware.Hv.QueryDefinition + + $wildCard = $false + #Only supports wild card '*' + if ($params['MachineName'] -and $params['MachineName'].contains('*')) { + $wildcard = $true + } + if ($params['DnsName'] -and $params['DnsName'].contains('*')) { + $wildcard = $true + } + # build the query values, MachineNamesView is having more info than + # MachineSummaryView + $query.queryEntityType = 'MachineNamesView' + if (! $wildcard) { + [VMware.Hv.queryfilter[]]$filterSet = @() + foreach ($setting in $machineSelectors.Keys) { + if ($null -ne $params[$setting]) { + $equalsFilter = New-Object VMware.Hv.QueryFilterEquals + $equalsFilter.memberName = $machineSelectors[$setting] + if ($equalsFilter.memberName -eq 'base.desktop') { + $equalsFilter.value = $desktopId + } else { + $equalsFilter.value = $params[$setting] + } + $filterSet += $equalsFilter + } + } + if ($filterSet.Count -gt 0) { + $andFilter = New-Object VMware.Hv.QueryFilterAnd + $andFilter.Filters = $filterset + $query.Filter = $andFilter + } + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $machineList = $queryResults.results + } + if ($wildcard -or [string]::IsNullOrEmpty($machineList)) { + $query.Filter = $null + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $strFilterSet = @() + foreach ($setting in $machineSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and (($setting -eq 'MachineName') -or ($setting -eq 'DnsName')) ) { + $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } + } + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $machineList = $queryResults.results | where $scriptBlock + } + return $machineList +} + + +function Get-HVMachine { +<# +.Synopsis + Gets virtual Machine(s) information with given search parameters. + +.DESCRIPTION + Queries and returns virtual machines information, the machines list would be determined + based on queryable fields poolName, dnsName, machineName, state. When more than one + fields are used for query the virtual machines which satisfy all fields criteria would be returned. + +.PARAMETER PoolName + Pool name to query for. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has name same as value will be returned. + +.PARAMETER MachineName + The name of the Machine to query for. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER DnsName + DNS name for the Machine to filter with. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER State + The basic state of the Machine to filter with. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Get-HVDesktop -PoolName 'ManualPool' + +.EXAMPLE + Get-HVDesktop -MachineName 'PowerCLIVM' + +.EXAMPLE + Get-HVDesktop -State CUSTOMIZING + +.EXAMPLE + Get-HVDesktop -DnsName 'powercli-*' -State CUSTOMIZING + +.OUTPUTS + Returns list of objects of type MachineInfo + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + [string] + $PoolName, + + [Parameter(Mandatory = $false)] + [string] + $MachineName, + + [Parameter(Mandatory = $false)] + [string] + $DnsName, + + [Parameter(Mandatory = $false)] + [ValidateSet('PROVISIONING','PROVISIONING_ERROR','WAIT_FOR_AGENT','CUSTOMIZING', + 'DELETING','MAINTENANCE','ERROR','PROVISIONED','AGENT_UNREACHABLE','UNASSIGNED_USER_CONNECTED', + 'CONNECTED','UNASSIGNED_USER_DISCONNECTED','DISCONNECTED','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','ALREADY_USED','AVAILABLE','IN_PROGRESS','DISABLED', + 'DISABLE_IN_PROGRESS','VALIDATING','UNKNOWN')] + [string] + $State, + + [Parameter(Mandatory = $false)] + [string] + $JsonFilePath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $machineList = Find-HVMachine -Param $PSBoundParameters + + if ($full) { + $queryResults = @() + $desktop_helper = New-Object VMware.Hv.MachineService + foreach ($id in $machineList.id) { + $info = $desktop_helper.Machine_Get($services,$id) + $queryResults += $info + } + $machineList = $queryResults + } + return $machineList +} + +function Get-HVMachineSummary { +<# +.Synopsis + Gets virtual Machine(s) summary with given search parameters. + +.DESCRIPTION + Queries and returns virtual machines information, the machines list would be determined + based on queryable fields poolName, dnsName, machineName, state. When more than one + fields are used for query the virtual machines which satisfy all fields criteria would be returned. + +.PARAMETER PoolName + Pool name to query for. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has name same as value will be returned. + +.PARAMETER MachineName + The name of the Machine to query for. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER DnsName + DNS name for the Machine to filter with. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER State + The basic state of the Machine to filter with. + If the value is null or not provided then filter will not be applied, + otherwise the virtual machines which has display name same as value will be returned. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Get-HVDesktopSummary -PoolName 'ManualPool' + +.EXAMPLE + Get-HVDesktopSummary -MachineName 'PowerCLIVM' + +.EXAMPLE + Get-HVDesktopSummary -State CUSTOMIZING + +.EXAMPLE + Get-HVDesktopSummary -DnsName 'powercli-*' -State CUSTOMIZING + +.OUTPUTS + Returns list of objects of type MachineNamesView + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + [string] + $PoolName, + + [Parameter(Mandatory = $false)] + [string] + $MachineName, + + [Parameter(Mandatory = $false)] + [string] + $DnsName, + + [Parameter(Mandatory = $false)] + [ValidateSet('PROVISIONING','PROVISIONING_ERROR','WAIT_FOR_AGENT','CUSTOMIZING', + 'DELETING','MAINTENANCE','ERROR','PROVISIONED','AGENT_UNREACHABLE','UNASSIGNED_USER_CONNECTED', + 'CONNECTED','UNASSIGNED_USER_DISCONNECTED','DISCONNECTED','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','ALREADY_USED','AVAILABLE','IN_PROGRESS','DISABLED', + 'DISABLE_IN_PROGRESS','VALIDATING','UNKNOWN')] + [string] + $State, + + [Parameter(Mandatory = $false)] + [string] + $JsonFilePath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $machineList = Find-HVMachine -Param $PSBoundParameters + return $machineList +} + + + From 7a5ae91379309c643985129c1132b8e86367876a Mon Sep 17 00:00:00 2001 From: baoyinqiao Date: Wed, 14 Dec 2016 08:57:43 +0800 Subject: [PATCH 10/90] Add a new VIProperty: KMSserver for VM As Mike suggested, add a new VI Property to retrieve the KMSserver ClusterId for encrypted VMs. --- Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index 149fd49..f14973a 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -59,6 +59,13 @@ New-VIProperty -Name Locked -ObjectType VirtualMachine -Value { ($vm.extensiondata.Runtime.ConnectionState -eq "invalid") -and ($vm.extensiondata.Config.KeyId) } -BasedOnExtensionProperty 'Runtime.ConnectionState','Config.KeyId' -Force | Out-Null +New-VIProperty -Name KMSserver -ObjectType VirtualMachine -Value { + Param ($VM) + if ($VM.Encrypted) { + $VM.EncryptionKeyId.ProviderId.Id + } + } -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null + New-VIProperty -Name Encrypted -ObjectType HardDisk -Value { Param ($hardDisk) $hardDisk.ExtensionData.Backing.KeyId -ne $null From 9f8d094e387566a4d90f37bad4b37276a57f3718 Mon Sep 17 00:00:00 2001 From: baoyinqiao Date: Wed, 14 Dec 2016 09:02:47 +0800 Subject: [PATCH 11/90] Fix the indentation of Encrypted Property of disk Identation fix. --- Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index f14973a..8594bf9 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -67,8 +67,8 @@ New-VIProperty -Name KMSserver -ObjectType VirtualMachine -Value { } -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null New-VIProperty -Name Encrypted -ObjectType HardDisk -Value { - Param ($hardDisk) - $hardDisk.ExtensionData.Backing.KeyId -ne $null + Param ($hardDisk) + $hardDisk.ExtensionData.Backing.KeyId -ne $null } -BasedOnExtensionProperty 'Backing.KeyId' -Force | Out-Null New-VIProperty -Name EncryptionKeyId -ObjectType HardDisk -Value { From 8b0750a94ee3a2d3f6de1b6ef22d50b7d4651076 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 16 Dec 2016 10:04:04 +0530 Subject: [PATCH 12/90] Write-Error to Write-Host In Find-HVMachine using Write-Host instead of Write-Error --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 2a8b70a..17bba20 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1,7 +1,7 @@ #Script Module : VMware.Hv.Helper #Version : 1.0 -#Copyright 2016 VMware, Inc. All Rights Reserved. +#Copyright © 2016 VMware, Inc. All Rights Reserved. #Permission is hereby granted, free of charge, to any person obtaining a copy of #this software and associated documentation files (the "Software"), to deal in @@ -1844,7 +1844,7 @@ function New-HVFarm { Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description  'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern  "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" .EXAMPLE New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json @@ -4956,7 +4956,7 @@ function Find-HVMachine { if ($params['PoolName']) { $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -hvServer $params['HvServer'] if ($poolObj.Length -ne 1) { - Write-Error "Failed to retrieve specific pool object with given PoolName : $params['PoolName']" + Write-Host "Failed to retrieve specific pool object with given PoolName : "$params['PoolName'] break; } else { $desktopId = $poolObj.Id From df17793edeedc64f80456592b4f4873e82450647 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Mon, 19 Dec 2016 13:07:44 +0530 Subject: [PATCH 13/90] Fix for formatting Get-HVMachineSummary and empty search results. Displays proper meesage for Get-HVMachine/Get-HVMAchineSummary, incase of empty search results. Dispalying Agent sataus for Get-HVMachineSummary result. --- .../VMware.HV.Helper.format.ps1xml | 31 ++++++++++--------- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 25 +++++++++------ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml index dc4cca4..62ce001 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml @@ -29,7 +29,7 @@ - 8 + 7 @@ -117,27 +117,27 @@ - 16 + 15 - 16 + 12 - 16 + 12 - 16 + 8 - 16 + 15 - 8 + 5 @@ -145,9 +145,8 @@ - 10 + 15 - Right @@ -169,13 +168,13 @@ $_.ManagedMachineNamesData.HostName - $_.Data.AgentVersion + $_.Base.AgentVersion $_.ManagedMachineNamesData.DatastorePaths - $_.Data.BasicState + $_.Base.BasicState @@ -212,12 +211,16 @@ $_.ManagedMachineNamesData.HostName - + + $_.Base.AgentVersion + + + $_.ManagedMachineNamesData.DatastorePaths - - $_.Data.BasicState + + $_.Base.BasicState diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 17bba20..583933d 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5136,16 +5136,17 @@ function Get-HVMachine { } $machineList = Find-HVMachine -Param $PSBoundParameters - - if ($full) { - $queryResults = @() - $desktop_helper = New-Object VMware.Hv.MachineService - foreach ($id in $machineList.id) { - $info = $desktop_helper.Machine_Get($services,$id) - $queryResults += $info - } - $machineList = $queryResults + if (!$machineList) { + Write-Host "No Virtual Machine(s) Found with given search parameters" + break } + $queryResults = @() + $desktop_helper = New-Object VMware.Hv.MachineService + foreach ($id in $machineList.id) { + $info = $desktop_helper.Machine_Get($services,$id) + $queryResults += $info + } + $machineList = $queryResults return $machineList } @@ -5252,8 +5253,12 @@ function Get-HVMachineSummary { } $machineList = Find-HVMachine -Param $PSBoundParameters + if (!$machineList) { + Write-Host "No Virtual Machine(s) Found with given search parameters" + break + } return $machineList } - +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool From ce69869e8a6518c26214be82e27aaf9c76728d8a Mon Sep 17 00:00:00 2001 From: yangm Date: Mon, 19 Dec 2016 00:29:52 -0800 Subject: [PATCH 14/90] add 1 property to VM, 1 function --- .../VMware.VMEncryption.psm1 | 88 +++++++++++++++---- 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index 8594bf9..5f23ca0 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -59,6 +59,11 @@ New-VIProperty -Name Locked -ObjectType VirtualMachine -Value { ($vm.extensiondata.Runtime.ConnectionState -eq "invalid") -and ($vm.extensiondata.Config.KeyId) } -BasedOnExtensionProperty 'Runtime.ConnectionState','Config.KeyId' -Force | Out-Null +New-VIProperty -Name vMotionEncryption -ObjectType VirtualMachine -Value { + Param ($VM) + $VM.ExtensionData.Config.MigrateEncryption +} -BasedOnExtensionProperty 'Config.MigrateEncryption' -Force | Out-Null + New-VIProperty -Name KMSserver -ObjectType VirtualMachine -Value { Param ($VM) if ($VM.Encrypted) { @@ -108,7 +113,6 @@ Function Enable-VMHostCryptoSafe { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -177,7 +181,6 @@ Function Set-VMHostCryptoKey { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -231,6 +234,71 @@ Function Set-VMHostCryptoKey { } } +Function Set-vMotionEncryptionConfig { + <# + .SYNOPSIS + This cmdlet sets the vMotionEncryption property of a VM. + + .DESCRIPTION + Use this function to set the vMotionEncryption settings for a VM. + The 'Encryption' parameter is set up with Tab-Complete for the available + options. + + .PARAMETER VM + Specifies the VM you want to set the vMotionEncryption property. + + .PARAMETER Encryption + Specifies the value you want to set to the vMotionEncryption property. + The Encryption options are: Disabled, Opportunistic, and Required. + + .EXAMPLE + PS C:\> Get-VM | Set-vMotionEncryptionConfig -Encryption opportunistic + + Sets the vMotionEncryption of all the VMs + + .NOTES + Author : Carrie Yang + Author email : yangm@vmware.com + Version : 1.0 + + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 + VMware vCenter Server Version : 6.5 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 3.0 + #> + + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$VM, + + [ValidateSet("disabled", "opportunistic", "required")] + [String]$Encryption + ) + + process{ + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + + if ($VM.Encrypted -and $VM.vMotionEncryption -ne $Encryption) { + Write-Error "Cannot change encrypted vMotion state for an encrypted VM." + return + } + + $VMView = $VM | get-view + $config = new-object VMware.Vim.VirtualMachineConfigSpec + $config.MigrateEncryption = New-object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes + $config.MigrateEncryption = "$encryption" + + $VMView.ReconfigVM($config) + + $VM.ExtensionData.UpdateViewData() + $VM.vMotionEncryption + } +} + Function Enable-VMEncryption { <# .SYNOPSIS @@ -277,7 +345,6 @@ Function Enable-VMEncryption { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -438,7 +505,6 @@ Function Enable-VMDiskEncryption { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -591,7 +657,6 @@ Function Disable-VMEncryption { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -688,7 +753,6 @@ Function Disable-VMDiskEncryption { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -824,7 +888,6 @@ Function Set-VMEncryptionKey { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -981,7 +1044,6 @@ Function Set-VMDiskEncryptionKey { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1105,7 +1167,6 @@ Function Get-VMEncryptionInfo { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1205,7 +1266,6 @@ Function Get-EntityByCryptoKey { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1331,7 +1391,6 @@ Function New-KMServer { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1491,7 +1550,6 @@ Function Remove-KMServer { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1569,7 +1627,6 @@ Function Get-KMSCluster { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> # Confirm the connected VIServer is vCenter Server @@ -1661,7 +1718,6 @@ Function Get-KMServerInfo { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1723,7 +1779,6 @@ Function Get-KMServerStatus { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1795,7 +1850,6 @@ Function Get-DefaultKMSCluster { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> # Confirm the connected VIServer is vCenter Server @@ -1833,7 +1887,6 @@ Function Set-DefaultKMSCluster { VMware vCenter Server Version : 6.5 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1889,7 +1942,6 @@ Function ConfirmHardDiskIsValid { .PARAMETER HardDisk Specifies the hard disks which you want to use to validate. - #> [CmdLetBinding()] From 7d49541a258f79ba0e54162e734ccc33c14101e5 Mon Sep 17 00:00:00 2001 From: yangm Date: Tue, 20 Dec 2016 19:41:59 -0800 Subject: [PATCH 15/90] add 1 property and 1 function for vMotionEncryption --- .../VMware.VMEncryption.psm1 | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index 5f23ca0..5252f96 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -69,7 +69,7 @@ New-VIProperty -Name KMSserver -ObjectType VirtualMachine -Value { if ($VM.Encrypted) { $VM.EncryptionKeyId.ProviderId.Id } - } -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null +} -BasedOnExtensionProperty 'Config.KeyId' -Force | Out-Null New-VIProperty -Name Encrypted -ObjectType HardDisk -Value { Param ($hardDisk) @@ -238,7 +238,7 @@ Function Set-vMotionEncryptionConfig { <# .SYNOPSIS This cmdlet sets the vMotionEncryption property of a VM. - + .DESCRIPTION Use this function to set the vMotionEncryption settings for a VM. The 'Encryption' parameter is set up with Tab-Complete for the available @@ -248,17 +248,17 @@ Function Set-vMotionEncryptionConfig { Specifies the VM you want to set the vMotionEncryption property. .PARAMETER Encryption - Specifies the value you want to set to the vMotionEncryption property. - The Encryption options are: Disabled, Opportunistic, and Required. + Specifies the value you want to set to the vMotionEncryption property. + The Encryption options are: disabled, opportunistic, and required. .EXAMPLE PS C:\> Get-VM | Set-vMotionEncryptionConfig -Encryption opportunistic - + Sets the vMotionEncryption of all the VMs - + .NOTES - Author : Carrie Yang - Author email : yangm@vmware.com + Author : Brian Graf, Carrie Yang. + Author email : grafb@vmware.com, yangm@vmware.com Version : 1.0 ==========Tested Against Environment========== @@ -274,28 +274,31 @@ Function Set-vMotionEncryptionConfig { [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$VM, + [Parameter(Mandatory=$True] [ValidateSet("disabled", "opportunistic", "required")] - [String]$Encryption + [String]$Encryption ) process{ - # Confirm the connected VIServer is vCenter Server - ConfirmIsVCenter + if ($VM.vMotionEncryption -eq $Encryption) { + Write-Warning "The encrypted vMotion state is already $Encrypted, no need to change it." + return + } - if ($VM.Encrypted -and $VM.vMotionEncryption -ne $Encryption) { + if ($VM.Encrypted) { Write-Error "Cannot change encrypted vMotion state for an encrypted VM." return } $VMView = $VM | get-view - $config = new-object VMware.Vim.VirtualMachineConfigSpec - $config.MigrateEncryption = New-object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes - $config.MigrateEncryption = "$encryption" - + $Config = New-Object VMware.Vim.VirtualMachineConfigSpec + $Config.MigrateEncryption = New-Object VMware.Vim.VirtualMachineConfigSpecEncryptedVMotionModes + $Config.MigrateEncryption = $Encryption + $VMView.ReconfigVM($config) - $VM.ExtensionData.UpdateViewData() - $VM.vMotionEncryption + $VM.ExtensionData.UpdateViewData() + $VM.vMotionEncryption } } @@ -1156,7 +1159,7 @@ Function Get-VMEncryptionInfo { .NOTES If $HardDisk is specified, then only the encryption information of the disks specified in $HardDisk is obtained. Otherwise, all disks' encryption information of the specified VM is returned. - + .NOTES Author : Carrie Yang. Author email : yangm@vmware.com @@ -1255,7 +1258,7 @@ Function Get-EntityByCryptoKey { .NOTES At least one of the KeyId and KMSClusterId parameters is required. If the SearchVMHosts, SearchVMs and SearchDisks all not specified, the cmdlet return $null. - + .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com @@ -1876,7 +1879,7 @@ Function Set-DefaultKMSCluster { C:\PS>Set-DefaultKMSCluster -KMSClusterId 'ClusterIdString' Sets the KMS cluster whose cluster ID is 'ClusterIdString' as the default KMS cluster. - + .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com From 58ba21aacdae679b30b7ed9e088d51dc617f171c Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Thu, 22 Dec 2016 23:19:54 +0100 Subject: [PATCH 16/90] sets the Basic settings for a new ESXi This Function sets the Basic settings for a new ESXi. * NTP * SSH * Syslog * Power Management * HP 3PAR SATP/PSP Rule * ... --- Modules/Konfig-ESXi.psm1 | 234 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 Modules/Konfig-ESXi.psm1 diff --git a/Modules/Konfig-ESXi.psm1 b/Modules/Konfig-ESXi.psm1 new file mode 100644 index 0000000..f14386a --- /dev/null +++ b/Modules/Konfig-ESXi.psm1 @@ -0,0 +1,234 @@ +function Konfig-ESXi { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.12 ver 1.0 Base Release + 2016.12 ver 1.1 ESXi 6.5 Tests, Minor enhancements + =========================================================================== + External Code Sources: + Function My-Logger : http://www.virtuallyghetto.com/ + =========================================================================== + Tested Against Environment: + vSphere Version: ESXi 5.5 U2, ESXi 6.5 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2012 R2 + Keyword: ESXi, NTP, SSH, Syslog, SATP, + =========================================================================== + + .DESCRIPTION + This Function sets the Basic settings for a new ESXi. + + * NTP + * SSH + * Syslog + * Power Management + * HP 3PAR SATP/PSP Rule + * ... + + .Example + Konfig-ESXi -VMHost myesxi.lan.local -NTP 192.168.2.1, 192.168.2.2 -syslog "udp://loginsight.lan.local:514" + + .PARAMETER VMHost + Host to configure. + + .PARAMETER NTP + NTP Server(s) to set. + + .PARAMETER Syslog + Syslog Server to set, e.g. "udp://loginsight.lan.local:514" + + DNS Name must be resolvable! + + +#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)] + [String] $VMHost, + [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=1)] + [array]$NTP, + [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=2)] + [String] $syslog + +) + +Begin { + Function My-Logger { + param( + [Parameter(Mandatory=$true)] + [String]$message + ) + + $timeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" + + Write-Host -NoNewline -ForegroundColor White "[$timestamp]" + Write-Host -ForegroundColor Green " $message" + } + function Set-MyESXiOption { + [CmdletBinding()] + param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] + [String] $Name, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] + [String] $Value + ) + process { + $myESXiOption = Get-AdvancedSetting -Entity $ESXiHost -Name $Name + if ($myESXiOption.Value -ne $Value) { + My-Logger " Setting ESXi Option $Name to Value $Value" + $myESXiOption | Set-AdvancedSetting -Value $Value -Confirm:$false | Out-Null + } + else { + My-Logger " ESXi Option $Name already has Value $Value" + } + } + } +} + +Process { + $Validate = $True + + #region: Start vCenter Connection + My-Logger "Starting to Process ESXi Server Connection to $VMHost ..." + if (($global:DefaultVIServers).count -gt 0) { + Disconnect-VIServer -Force -Confirm:$False -ErrorAction SilentlyContinue + } + $VIConnection = Connect-VIServer -Server $VMHost + if (-not $VIConnection.IsConnected) { + Write-Error "ESXi Connection Failed." + $Validate = $False + } + elseif ($VIConnection.ProductLine -ne "EmbeddedEsx") { + Write-Error "Connencted System is not an ESXi." + $Validate = $False + } + else { + $ESXiHost = Get-VMHost + My-Logger "Connected ESXi Version: $($ESXiHost.Version) $($ESXiHost.Build) " + } + #endregion + + if ($Validate -eq $True) { + + #region: Enable SSH and disable SSH Warning + $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} + My-Logger "Starting SSH Service..." + if($SSHService.Running -ne $True){ + Start-VMHostService -HostService $SSHService -Confirm:$false | Out-Null + } + else { + My-Logger " SSH Service is already running" + } + My-Logger "Setting SSH Service to Automatic Start..." + if($SSHService.Policy -ne "automatic"){ + Set-VMHostService -HostService $SSHService -Policy "Automatic" | Out-Null + } + else { + My-Logger " SSH Service is already set to Automatic Start" + } + My-Logger "Disabling SSH Warning..." + Set-MyESXiOption -Name "UserVars.SuppressShellWarning" -Value "1" + #endregion + + #region: Config NTP + My-Logger "Removing existing NTP Server..." + try { + $ESXiHost | Remove-VMHostNtpServer -NtpServer (Get-VMHostNtpServer) -Confirm:$false + } + catch [System.Exception] { + Write-Warning "Error during removing existing NTP Servers." + } + My-Logger "Setting new NTP Servers..." + foreach ($myNTP in $NTP) { + $ESXiHost | Add-VMHostNtpServer -ntpserver $myNTP -confirm:$False | Out-Null + } + + My-Logger "Configure NTP Service..." + $NTPService = $ESXiHost | Get-VMHostService| Where-Object {$_.key -eq "ntpd"} + if($NTPService.Running -eq $True){ + Stop-VMHostService -HostService $NTPService -Confirm:$false | Out-Null + } + if($NTPService.Policy -ne "on"){ + Set-VMHostService -HostService $NTPService -Policy "on" -confirm:$False | Out-Null + } + + My-Logger "Configure Local Time..." + $HostTimeSystem = Get-View $ESXiHost.ExtensionData.ConfigManager.DateTimeSystem + $HostTimeSystem.UpdateDateTime([DateTime]::UtcNow) + + My-Logger "Start NTP Service..." + Start-VMHostService -HostService $NTPService -confirm:$False | Out-Null + #endregion + + #region: Remove default PG + My-Logger "Checking for Default Port Group ..." + if ($defaultPG = $ESXiHost | Get-VirtualSwitch -Name vSwitch0 | Get-VirtualPortGroup -Name "VM Network" -ErrorAction SilentlyContinue ){ + Remove-VirtualPortGroup -VirtualPortGroup $defaultPG -confirm:$False | Out-Null + My-Logger " Default PG Removed" + } + else { + My-Logger " No Default PG found" + } + #endregion + + #region: Configure Static HighPower + My-Logger "Setting PowerProfile to Static HighPower..." + try { + $HostView = ($ESXiHost | Get-View) + (Get-View $HostView.ConfigManager.PowerSystem).ConfigurePowerPolicy(1) + } + catch [System.Exception] { + Write-Warning "Error during Configure Static HighPower. See latest errors..." + } + #endregion + + #region: Conf Syslog + My-Logger "Setting Syslog Firewall Rule ..." + $SyslogFW = ($ESXiHost | Get-VMHostFirewallException | where {$_.Name -eq 'syslog'}) + if ($SyslogFW.Enabled -eq $False ){ + $SyslogFW | Set-VMHostFirewallException -Enabled:$true -Confirm:$false | Out-Null + My-Logger " Syslog Firewall Rule enabled" + } + else { + My-Logger " Syslog Firewall Rule already enabled" + } + My-Logger "Setting Syslog Server..." + Set-MyESXiOption -Name "Syslog.global.logHost" -Value $syslog + #endregion + + #region: Change Disk Scheduler + My-Logger "Changing Disk Scheduler..." + Set-MyESXiOption -Name "Disk.SchedulerWithReservation" -Value "0" + #endregion + + #region: Configure HP 3PAR SATP/PSP Rule + My-Logger "Configure HP 3PAR SATP/PSP Rule" + $esxcli2 = Get-ESXCLI -VMHost $ESXiHost -V2 + $arguments = $esxcli2.storage.nmp.satp.rule.add.CreateArgs() + $arguments.satp = "VMW_SATP_ALUA" + $arguments.psp = "VMW_PSP_RR" + $arguments.pspoption = "iops=100" + $arguments.claimoption = "tpgs_on" + $arguments.vendor = "3PARdata" + $arguments.model = "VV" + $arguments.description = "HP 3PAR custom SATP Claimrule" + try { + $esxcli2.storage.nmp.satp.rule.add.Invoke($arguments) + } + catch { + Write-Warning "Error during Configure HP 3PAR SATP/PSP Rule. See latest errors..." + } + #endregion + + } + } +} From 559db31acaba80ab59bfef9cb33751b435df750f Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Fri, 23 Dec 2016 14:30:57 +0100 Subject: [PATCH 17/90] added Get-NewAndRemovedVMs Function This Function report newly created and deleted VMs by Cluster. --- Modules/Get-NewAndRemovedVMs.psm1 | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Modules/Get-NewAndRemovedVMs.psm1 diff --git a/Modules/Get-NewAndRemovedVMs.psm1 b/Modules/Get-NewAndRemovedVMs.psm1 new file mode 100644 index 0000000..4a2e3ba --- /dev/null +++ b/Modules/Get-NewAndRemovedVMs.psm1 @@ -0,0 +1,131 @@ +function Get-NewAndRemovedVMs { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.12 ver 1.0 Base Release + =========================================================================== + External Code Sources: + https://github.com/alanrenouf/vCheck-vSphere + =========================================================================== + 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, VM + =========================================================================== + + .DESCRIPTION + This Function report newly created and deleted VMs by Cluster. + + .Example + Get-NewAndRemovedVMs -ClusterName Cluster* | ft -AutoSize + + .Example + Get-NewAndRemovedVMs -ClusterName Cluster01 -Days 90 + + .PARAMETER ClusterName + Name or Wildcard of your vSphere Cluster Name(s) to report. + + .PARAMETER Day + Range in Days to report. + + +#Requires PS -Version 4.0 +#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} +#> + +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0, HelpMessage = "Name or Wildcard of your vSphere Cluster Name to report")] + [ValidateNotNullorEmpty()] + [String]$ClusterName, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, HelpMessage = "Range in Days to report")] + [ValidateNotNullorEmpty()] + [String]$Days = "30" +) +Begin { + function Get-VIEventPlus { + + param( + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity, + [string[]]$EventType, + [DateTime]$Start, + [DateTime]$Finish = (Get-Date), + [switch]$Recurse, + [string[]]$User, + [Switch]$System, + [string]$ScheduledTask, + [switch]$FullMessage = $false, + [switch]$UseUTC = $false + ) + + process { + $eventnumber = 100 + $events = @() + $eventMgr = Get-View EventManager + $eventFilter = New-Object VMware.Vim.EventFilterSpec + $eventFilter.disableFullMessage = ! $FullMessage + $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity + $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} + $eventFilter.eventTypeId = $EventType + if($Start -or $Finish){ + $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime + if($Start){ + $eventFilter.time.beginTime = $Start + } + if($Finish){ + $eventFilter.time.endTime = $Finish + } + } + if($User -or $System){ + $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername + if($User){ + $eventFilter.UserName.userList = $User + } + if($System){ + $eventFilter.UserName.systemUser = $System + } + } + if($ScheduledTask){ + $si = Get-View ServiceInstance + $schTskMgr = Get-View $si.Content.ScheduledTaskManager + $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | + where {$_.Info.Name -match $ScheduledTask} | + Select -First 1 | + Select -ExpandProperty MoRef + } + if(!$Entity){ + $Entity = @(Get-Folder -NoRecursion) + } + $entity | %{ + $eventFilter.entity.entity = $_.ExtensionData.MoRef + $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + while($eventsBuffer){ + $events += $eventsBuffer + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + } + $eventCollector.DestroyCollector() + } + if (-not $UseUTC) + { + $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } + } + + $events + } +} +} + +process { + $result = Get-VIEventPlus -Start ((get-date).adddays(-$Days)) -EventType @("VmCreatedEvent", "VmBeingClonedEvent", "VmBeingDeployedEvent","VmRemovedEvent") + $sortedResult = $result | Select CreatedTime, @{N='Cluster';E={$_.ComputeResource.Name}}, @{Name="VMName";Expression={$_.vm.name}}, UserName, @{N='Type';E={$_.GetType().Name}}, FullFormattedMessage | Sort CreatedTime + $sortedResult | where {$_.Cluster -like $ClusterName} +} +} \ No newline at end of file From 804608f34a926c3356fb9e643f94c55749baa179 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 23 Dec 2016 19:07:40 +0530 Subject: [PATCH 18/90] Adding features WhatIf, Cloning, Get-HVDesktopSpec and Get-HVInternalName 1) Enabling WhatIf functionality to Advanced Functions (All get-xxx AF not required) 2) Cloning Added support for all AFs except instant clone pools(need to fix a bug in server side for instant clone) 3) Get-HVDesktopSpec Converts DesktopInfo to DesktopSpec 4) Converts View API ids to human readbale names --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 514 ++++++++++++++++-- 1 file changed, 462 insertions(+), 52 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 583933d..f3cb9ea 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1,7 +1,7 @@ #Script Module : VMware.Hv.Helper #Version : 1.0 -#Copyright © 2016 VMware, Inc. All Rights Reserved. +#Copyright 2016 VMware, Inc. All Rights Reserved. #Permission is hereby granted, free of charge, to any person obtaining a copy of #this software and associated documentation files (the "Software"), to deal in @@ -48,10 +48,8 @@ function Get-ViewAPIService { return $hvServer.ExtensionData } } elseif ($global:DefaultHVServers.Length -gt 0) { - if ($pscmdlet.ShouldProcess($global:DefaultHVServers[0].uid,'hvServer not specified, use default hvServer connection?')) { - $hvServer = $global:DefaultHVServers[0] - return $hvServer.ExtensionData - } + $hvServer = $global:DefaultHVServers[0] + return $hvServer.ExtensionData } return $null } @@ -296,7 +294,10 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A return } } - $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) + if ($pscmdlet.ShouldProcess($machineList)) { + $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) + } + return $machineList } default { Write-Error "Only Automated/Manual pool types support this add operation" @@ -347,6 +348,7 @@ function Get-MachinesByVCenter ($MachineList,$VcId) { } return $machines } + function Add-HVRDSServer { <# .SYNOPSIS @@ -430,7 +432,10 @@ function Add-HVRDSServer { 'MANUAL' { try { $serverList = Get-RegisteredRDSServer -services $services -serverList $rdsServers - $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) + if ($pscmdlet.ShouldProcess($serverList)) { + $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) + } + return $serverList } catch { Write-Error "Failed to Add RDS Server to Farm with error: $_" break @@ -443,7 +448,8 @@ function Add-HVRDSServer { [System.gc]::collect() } } -[System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null + + function Connect-HVEvent { <# @@ -506,6 +512,7 @@ function Connect-HVEvent { ) begin { + [System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null # Connect to Connection Server and call the View API service $services = Get-ViewAPIService -hvServer $hvServer if ($null -eq $services) { @@ -1350,6 +1357,10 @@ function Get-HVPoolSummary { break } $poolList = Find-HVPool -Param $psboundparameters + if (!$poolList) { + Write-Host "No Pool Found with given search parameters" + break + } Return $poolList } @@ -1844,7 +1855,7 @@ function New-HVFarm { Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description  'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern  "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" .EXAMPLE New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json @@ -2246,10 +2257,13 @@ function New-HVFarm { # Please uncomment below code, if you want to save the json file <# -$myDebug = convertto-json -InputObject $farmSpecObj -depth 12 -$myDebug | out-file -filepath c:\temp\copiedfarm.json -#> - $farm_service_helper.Farm_Create($services, $farmSpecObj) + $myDebug = convertto-json -InputObject $farmSpecObj -depth 12 + $myDebug | out-file -filepath c:\temp\copiedfarm.json + #> + if ($pscmdlet.ShouldProcess($farmSpecObj)) { + $Id = $farm_service_helper.Farm_Create($services, $farmSpecObj) + } + return $farmSpecObj } end { @@ -2454,8 +2468,6 @@ function Get-FarmSpec { if ($farmType -eq 'AUTOMATED') { $farm_spec_helper.getDataObject().AutomatedFarmSpec.RdsServerNamingSpec.PatternNamingSettings = $farm_helper.getFarmPatternNamingSettingsHelper().getDataObject() $farm_spec_helper.getDataObject().AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings = $farm_helper.getFarmViewComposerStorageSettingsHelper().getDataObject() - } elseif ($farmType -eq 'MANUAL') { - # No need to set } return $farm_spec_helper.getDataObject() } @@ -2988,10 +3000,13 @@ function New-HVPool { [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $false,ParameterSetName = "JSON_FILE")] + [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] [string[]]$VM, #farm [Parameter(Mandatory = $false,ParameterSetName = 'RDS')] + [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] + [string] $Farm, @@ -3163,7 +3178,7 @@ function New-HVPool { $poolType = $clonePool.type $desktopBase = $clonePool.base $desktopSettings = $clonePool.DesktopSettings - $provisioningType = $null + $provisioningType = $clonePool.source if ($clonePool.AutomatedDesktopData) { $provisioningType = $clonePool.AutomatedDesktopData.ProvisioningType $virtualCenterID = $clonePool.AutomatedDesktopData.VirtualCenter @@ -3176,8 +3191,25 @@ function New-HVPool { $desktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings $desktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings } - if (($null -eq $provisioningType) -or ($provisioningType -eq 'INSTANT_CLONE_ENGINE')) { - Write-Error "Only Automated linked clone or full clone pool support cloning" + elseif ($clonePool.ManualDesktopData) { + if (! $VM) { + Write-Error "ManualDesktop pool cloning requires list of machines, parameter VM is empty" + break + } + $source = $clonePool.source + $virtualCenterID = $clonePool.ManualDesktopData.VirtualCenter + $desktopUserAssignment = $clonePool.ManualDesktopData.userAssignment + $desktopVirtualCenterStorageSettings = $clonePool.ManualDesktopData.viewStorageAcceleratorSettings + $desktopVirtualCenterManagedCommonSettings = $clonePool.ManualDesktopData.virtualCenterManagedCommonSettings + } + elseif($clonePool.RdsDesktopData) { + if (! $Farm) { + Write-Error "RdsDesktop pool cloning requires farm, parameter Farm is not set" + break + } + } + if ($provisioningType -eq 'INSTANT_CLONE_ENGINE' -and $poolType -eq 'AUTOMATED') { + Write-Error "Cloning is not supported for instant clone pools" break } } else { @@ -3198,7 +3230,7 @@ function New-HVPool { elseif ($RDS) { $poolType = 'RDS' } } - $script:desktopSpecObj = Get-HVDesktopSpec -poolType $poolType -provisioningType $provisioningType -namingMethod $namingMethod + $script:desktopSpecObj = Get-DesktopSpec -poolType $poolType -provisioningType $provisioningType -namingMethod $namingMethod # # accumulate properties that are shared among various type @@ -3400,7 +3432,10 @@ function New-HVPool { $myDebug | out-file -filepath c:\temp\copieddesktop.json #> $desktop_helper = New-Object VMware.Hv.DesktopService - $desktop_helper.Desktop_create($services,$desktopSpecObj) + if ($pscmdlet.ShouldProcess($desktopSpecObj)) { + $id = $desktop_helper.Desktop_create($services,$desktopSpecObj) + } + return $desktopSpecObj } end { @@ -3457,7 +3492,7 @@ function Get-HVPoolProvisioningData { $folderList += $folders while ($folderList.Length -gt 0) { $item = $folderList[0] - if ($item -and !$_.folderdata.incompatiblereasons.inuse -and !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and ($item.folderdata.name -eq $vmFolder)) { + if ($item -and !$_.folderdata.incompatiblereasons.inuse -and !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and (($item.folderdata.path -eq $vmFolder) -or ($item.folderdata.name -eq $vmFolder))) { $vmObject.VmFolder = $item.id break } @@ -3473,7 +3508,7 @@ function Get-HVPoolProvisioningData { if ($hostOrCluster) { $vmFolder_helper = New-Object VMware.Hv.HostOrClusterService $hostClusterList = ($vmFolder_helper.HostOrCluster_GetHostOrClusterTree($services,$vmobject.datacenter)).treeContainer.children.info - $hostClusterObj = $hostClusterList | Where-Object { $_.name -eq $hostOrCluster } + $hostClusterObj = $hostClusterList | Where-Object { ($_.path -eq $hostOrCluster) -or ($_.name -eq $hostOrCluster) } if ($null -eq $hostClusterObj) { throw "No hostOrCluster found with Name: [$hostOrCluster]" } @@ -3482,7 +3517,7 @@ function Get-HVPoolProvisioningData { if ($resourcePool) { $resourcePool_helper = New-Object VMware.Hv.ResourcePoolService $resourcePoolList = $resourcePool_helper.ResourcePool_GetResourcePoolTree($services,$vmobject.HostOrCluster) - $resourcePoolObj = $resourcePoolList | Where-Object { $_.resourcepooldata.name -eq $resourcePool } + $resourcePoolObj = $resourcePoolList | Where-Object { ($_.resourcepooldata.path -eq $resourcePool) -or ($_.resourcepooldata.name -eq $resourcePool) } if ($null -eq $resourcePoolObj) { throw "No hostOrCluster found with Name: [$resourcePool]" } @@ -3526,7 +3561,7 @@ function Get-HVPoolStorageObject { $datastoreList = $datastore_helper.Datastore_ListDatastoresByHostOrCluster($services,$hostClusterID) $datastoresSelected = @() foreach ($ds in $datastores) { - $datastoresSelected += ($datastoreList | Where-Object { $_.datastoredata.name -eq $ds }).id + $datastoresSelected += ($datastoreList | Where-Object { ($_.DatastoreData.Path -eq $ds) -or ($_.datastoredata.name -eq $ds) }).id } foreach ($ds in $datastoresSelected) { $myDatastores = New-Object VMware.Hv.DesktopVirtualCenterDatastoreSettings @@ -3664,7 +3699,7 @@ function Get-CustomizationObject { } } -function Get-HVDesktopSpec { +function Get-DesktopSpec { param( [Parameter(Mandatory = $true)] @@ -3775,16 +3810,16 @@ function Remove-HVFarm { } if ($farmSpecObj) { foreach ($farmObj in $farmSpecObj) { - $farmList += $farmObj.id + $farmList += @{"id" = $farmObj.id; "Name" = $farmObj.data.name} } } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farmName]" break } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Farm) { foreach ($item in $farm) { - if ($item.GetType().name -eq 'FarmInfo' -or $item.GetType().name -eq 'FarmSummaryView') { - $farmList += $item.id + if (($item.GetType().name -eq 'FarmInfo') -or ($item.GetType().name -eq 'FarmSummaryView')) { + $farmList += @{"id" = $item.id; "Name" = $item.data.name} } else { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" @@ -3795,10 +3830,11 @@ function Remove-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList) { - $farm_service_helper.Farm_Delete($services, $item) + if ($pscmdlet.ShouldProcess($item)) { + $farm_service_helper.Farm_Delete($services, $item.id) + } + Write-Host "Farm Deleted: " $item.Name } - Write-Host "Farm Deleted" - } end { [System.gc]::collect() @@ -3893,7 +3929,7 @@ function Remove-HVPool { } if ($myPools) { foreach ($poolObj in $myPools) { - $poolList += $poolObj.id + $poolList += @{id = $poolObj.id; name = $poolObj.desktopSummaryData.name} } } else { Write-Error "No desktopsummarydata found with pool name: [$pool]" @@ -3901,8 +3937,11 @@ function Remove-HVPool { } } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { foreach ($item in $pool) { - if (($item.GetType().name -eq 'DesktopInfo') -or ($item.GetType().name -eq 'DesktopSummaryView')) { - $poolList += $item.id + if ($item.GetType().name -eq 'DesktopSummaryView') { + $poolList += @{id = $item.id; name = $item.desktopSummaryData.name} + } + elseif ($item.GetType().name -eq 'DesktopInfo') { + $poolList += @{id = $item.id; name = $item.base.name} } else { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" @@ -3917,9 +3956,8 @@ function Remove-HVPool { foreach ($item in $poolList) { if ($terminateSession) { #Terminate session - $queryResults = Get-HVQueryResults MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item) + $queryResults = Get-HVQueryResults MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item.id) $sessions += $queryResults.base.session - if ($null -ne $sessions) { $session_service_helper = New-Object VMware.Hv.SessionService try { @@ -3932,8 +3970,10 @@ function Remove-HVPool { Write-Host "No session found." } } - Write-Host "Deleting Pool" - $desktop_service_helper.Desktop_Delete($services,$item,$deleteSpec) + Write-Host "Deleting Pool: " $item.Name + if ($pscmdlet.ShouldProcess($deleteSpec)) { + $desktop_service_helper.Desktop_Delete($services,$item.id,$deleteSpec) + } } } @@ -4126,7 +4166,10 @@ function Set-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList) { - $farm_service_helper.Farm_Update($services,$item,$updates) + if ($pscmdlet.ShouldProcess($updates)) { + $farm_service_helper.Farm_Update($services,$item,$updates) + } + Write-Host "Updated Farm Member $updates.Key with value $updates.value" } } @@ -4326,7 +4369,10 @@ function Set-HVPool { } $desktop_helper = New-Object VMware.Hv.DesktopService foreach ($item in $poolList) { - $desktop_helper.Desktop_Update($services,$item,$updates) + if ($pscmdlet.ShouldProcess($updates)) { + $desktop_helper.Desktop_Update($services,$item,$updates) + } + Write-Host "Updated Pool member $updates.key with value $updates.value" } } @@ -4519,9 +4565,11 @@ function Start-HVFarm { $updates = @() $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - $farm_service_helper.Farm_Update($services,$item,$updates) - - $farm_service_helper.Farm_Recompose($services,$item,$spec) + if ($pscmdlet.ShouldProcess($spec)) { + $farm_service_helper.Farm_Update($services,$item,$updates) + $farm_service_helper.Farm_Recompose($services,$item,$spec) + } + Write-Host "Performed recompose task on farm: $farmList.item" } } } @@ -4800,14 +4848,20 @@ function Start-HVPool { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRebalanceSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - $desktop_helper.Desktop_Rebalance($services,$item,$spec) + if ($pscmdlet.ShouldProcess($spec)) { + $desktop_helper.Desktop_Rebalance($services,$item,$spec) + } + Write-Host "Performed rebalance task on Pool: $PoolList.item" } } 'REFRESH' { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRefreshSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - $desktop_helper.Desktop_Refresh($services,$item,$spec) + if ($pscmdlet.ShouldProcess($spec)) { + $desktop_helper.Desktop_Refresh($services,$item,$spec) + } + Write-Host "Performed refresh task on Pool: $PoolList.item" } } 'RECOMPOSE' { @@ -4823,8 +4877,10 @@ function Start-HVPool { $updates = @() $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - $desktop_helper.Desktop_Update($services,$item,$updates) - + if ($pscmdlet.ShouldProcess($spec)) { + $desktop_helper.Desktop_Update($services,$item,$updates) + } + Write-Host "Performed recompose task on Pool: $PoolList.item" } } 'PUSH_IMAGE' { @@ -4839,7 +4895,10 @@ function Start-HVPool { $spec.Settings.LogoffSetting = $logoffSetting $spec.Settings.StopOnFirstError = $stopOnFirstError if ($startTime) { $spec.Settings.startTime = $startTime } - $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) + if ($pscmdlet.ShouldProcess($spec)) { + $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) + } + Write-Host "Performed push_image task on Pool: $PoolList.item" } } 'CANCEL_PUSH_IMAGE' { @@ -4847,7 +4906,10 @@ function Start-HVPool { Write-Error "$poolList.$item is not a INSTANT CLONE pool" break } else { - $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) + if ($pscmdlet.ShouldProcess($spec)) { + $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) + } + Write-Host "Performed cancel_push_image task on Pool: $PoolList.item" } } } @@ -4956,7 +5018,7 @@ function Find-HVMachine { if ($params['PoolName']) { $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -hvServer $params['HvServer'] if ($poolObj.Length -ne 1) { - Write-Host "Failed to retrieve specific pool object with given PoolName : "$params['PoolName'] + Write-Host "Failed to retrieve specific pool object with given PoolName : " $params['PoolName'] break; } else { $desktopId = $poolObj.Id @@ -5260,5 +5322,353 @@ function Get-HVMachineSummary { return $machineList } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool +function Get-HVDesktopSpec { +<# +.Synopsis + Gets desktop specification +.DESCRIPTION + Converts DesktopInfo Object to DesktopSpec. Also Converts view API Ids to human readable names + +.PARAMETER DesktopInfo + An object with detailed description of a desktop instance. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Converts DesktopInfo to DesktopSpec + Get-HVDesktopSpec -DesktopInfo $DesktopInfoObj + +.EXAMPLE + Converts DesktopInfo to DesktopSpec and also dumps json object + Get-HVPool -PoolName 'LnkClnJson' | Get-HVDesktopSpec -FilePath "C:\temp\LnkClnJson.json" + +.OUTPUTS + Returns desktop specification + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [VMware.HV.DesktopInfo] + $DesktopInfo, + + [Parameter(Mandatory = $false)] + [String] + $FilePath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $DesktopSpec = New-Object VMware.HV.DesktopSpec + $DesktopPsObj = (($DesktopSpec | ConvertTo-Json -Depth 14) | ConvertFrom-Json) + $DesktopInfoPsObj = (($DesktopInfo | ConvertTo-Json -Depth 14) | ConvertFrom-Json) + $DesktopPsObj.Type = $DesktopInfoPsObj.Type + $DesktopPsObj.DesktopSettings = $DesktopInfoPsObj.DesktopSettings + + $entityId = New-Object VMware.HV.EntityId + $entityId.Id = $DesktopInfoPsObj.Base.AccessGroup.Id + $DesktopPsObj.Base = New-Object PsObject -Property @{ + name = $DesktopInfoPsObj.Base.Name; + displayName = $DesktopInfoPsObj.Base.displayName; + accessGroup = (Get-HVInternalName -EntityId $entityId); + description = $DesktopInfoPsObj.Base.description; + } + + if (! $DesktopInfoPsObj.GlobalEntitlementData.GlobalEntitlement) { + $DesktopPsObj.GlobalEntitlementData = $null + } else { + $entityId.Id = $DesktopInfoPsObj.GlobalEntitlementData.GlobalEntitlement.Id + $DesktopPsObj.GlobalEntitlementData = Get-HVInternalName -EntityId $entityId + } + + Switch ($DesktopInfo.Type) { + "AUTOMATED" { + $specificNamingSpecObj = $null + if ("SPECIFIED" -eq $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.NamingMethod) { + $specificNamingSpecObj = New-Object PsObject -Property @{ + specifiedNames = $null; + startMachinesInMaintenanceMode = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.SpecificNamingSettings.StartMachinesInMaintenanceMode; + numUnassignedMachinesKeptPoweredOn = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.SpecificNamingSettings.NumUnassignedMachinesKeptPoweredOn; + } + } + $vmNamingSpecObj = New-Object PsObject -Property @{ + namingMethod = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.NamingMethod; + patternNamingSettings = $DesktopInfoPsObj.AutomatedDesktopData.VmNamingSettings.PatternNamingSettings; + specificNamingSpec = $specificNamingSpecObj; + } + $virtualCenterProvisioningDataObj = New-Object PsObject @{ + template = $null; + parentVm = $null; + snapshot = $null; + datacenter = $null; + vmFolder = $null; + hostOrCluster = $null; + resourcePool= $null; + } + $ProvisioningSettingsObj = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Datacenter){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Datacenter.Id + $virtualCenterProvisioningDataObj.Datacenter = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.HostOrCluster){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.HostOrCluster.Id + $virtualCenterProvisioningDataObj.HostOrCluster = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.ResourcePool){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.ResourcePool.Id + $virtualCenterProvisioningDataObj.ResourcePool = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.ParentVm){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.ParentVm.Id + $virtualCenterProvisioningDataObj.ParentVm = Get-HVInternalName -EntityId $entityId ` + -VcId $DesktopInfo.AutomatedDesktopData.virtualCenter + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Snapshot){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Snapshot.Id + $virtualCenterProvisioningDataObj.Snapshot = Get-HVInternalName -EntityId $entityId ` + -BaseImageVmId $DesktopInfo.AutomatedDesktopData.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ParentVm + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Template){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Template.Id + $virtualCenterProvisioningDataObj.Template = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.VmFolder){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.VmFolder.Id + $virtualCenterProvisioningDataObj.VmFolder = Get-HVInternalName -EntityId $entityId + } + + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = ` + $virtualCenterProvisioningDataObj + $datastores = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores + $dataStoresObj = Get-DataStoreName -datastores $datastores + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores = ` + $dataStoresObj + $virtualCenterStorageSettingsObj = ` + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings + if($virtualCenterStorageSettingsObj.replicaDiskDatastore) { + $entityId.Id = $virtualCenterStorageSettingsObj.replicaDiskDatastore.Id + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.replicaDiskDatastore =` + Get-HVInternalName -EntityId $entityId + } + if($virtualCenterStorageSettingsObj.persistentDiskSettings) { + $datastores = $virtualCenterStorageSettingsObj.persistentDiskSettings.persistentDiskDatastores + $dataStoresObj = Get-DataStoreName -datastores $datastores + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.persistentDiskSettings.persistentDiskDatastores = ` + $dataStoresObj + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator = Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer = Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings) { + $entityId.Id = ` + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings.customizationSpec.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings.customizationSpec = ` + Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings) { + $entityId.Id = ` + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator = ` + Get-HVInternalName -EntityId $entityId + } + + $DesktopPsObj.AutomatedDesktopSpec = New-Object PsObject -Property @{ + provisioningType = $DesktopInfoPsObj.AutomatedDesktopData.ProvisioningType; + virtualCenter = $null; + userAssignment = $DesktopInfoPsObj.AutomatedDesktopData.UserAssignment; + virtualCenterProvisioningSettings = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings; + virtualCenterManagedCommonSettings = $DesktopInfoPsObj.AutomatedDesktopData.virtualCenterManagedCommonSettings; + customizationSettings = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings; + vmNamingSpec = $VmNamingSpecObj; + } + if ($DesktopInfoPsObj.AutomatedDesktopData.virtualCenter) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.virtualCenter.Id + $DesktopPsObj.AutomatedDesktopSpec.virtualCenter = Get-HVInternalName ` + -EntityId $entityId + } + break + } + "MANUAL" { + $DesktopPsObj.ManualDesktopSpec = New-Object PsObject -Property @{ + userAssignment = $DesktopInfoPsObj.ManualDesktopData.UserAssignment; + source = $DesktopInfoPsObj.ManualDesktopData.Source; + virtualCenter = $null; + machines = $null; + viewStorageAcceleratorSettings = $DesktopInfoPsObj.ManualDesktopData.ViewStorageAcceleratorSettings; + virtualCenterManagedCommonSettings = $DesktopInfoPsObj.ManualDesktopData.VirtualCenterManagedCommonSettings; + } + if ($DesktopInfoPsObj.ManualDesktopData.virtualCenter) { + $entityId.Id = $DesktopInfoPsObj.ManualDesktopData.virtualCenter.Id + $DesktopPsObj.ManualDesktopSpec.virtualCenter = Get-HVInternalName ` + -EntityId $entityId + } + break + } + "RDS" { + $DesktopPsObj.rdsDesktopSpec = New-Object PsObject -Property @{ + farm = $null; + } + break + } + } + $DesktopSpecJson = ($DesktopPsObj | ConvertTo-Json -Depth 14) + if ($filePath) { + $DesktopSpecJson | Out-File -FilePath $filePath + } + return $DesktopSpecJson +} + +function Get-DataStoreName { + param( + [Parameter(Mandatory = $true)] + $datastores + ) + $dataStoresObj = @() + $entityId = New-Object VMware.Hv.EntityId + $datastores | % { + $entityId.Id = $_.datastore.Id + $dataStoresObj += , (New-Object PsObject -Property @{ + datastore = Get-HVInternalName -EntityId $entityId; + storageOvercommit = $_.storageOvercommit; + }) + } + return $dataStoresObj +} + +function Get-HVInternalName { +<# +.Synopsis + Gets human readable name + +.DESCRIPTION + Converts Horizon API Ids to human readable names. Horizon API Ids are base64 encoded, this function + will decode and returns internal/human readable names. + +.PARAMETER EntityId + Representation of a manageable entity id. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Decodes and returns human readable name + Get-HVInternalName -EntityId $entityId + +.OUTPUTS + Returns human readable name + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [VMware.HV.EntityId] + $EntityId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [VMware.HV.VirtualCenterId] + $VcId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [VMware.HV.BaseImageVmId] + $BaseImageVmId, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $tokens = ($EntityId.id -split "/") + $serviceName = $tokens[0] + Switch ($serviceName) { + 'VirtualCenter' { + $vc_id = New-Object VMware.HV.VirtualCenterId + $vc_id.Id = $EntityId.Id + return ($services.VirtualCenter.VirtualCenter_Get($vc_id)).serverSpec.serverName + } + 'InstantCloneEngineDomainAdministrator' { + $Icid = New-Object VMware.HV.InstantCloneEngineDomainAdministratorId + $Icid.Id = $EntityId.Id + $Info = $services.InstantCloneEngineDomainAdministrator.InstantCloneEngineDomainAdministrator_Get($Icid) + return $Info.Base.Username + } + 'BaseImageVm' { + $info = $services.BaseImageVm.BaseImageVm_List($VcId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'BaseImageSnapshot' { + $info = $services.BaseImageSnapshot.BaseImageSnapshot_List($BaseImageVmId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'VmTemplate' { + $info = $services.VmTemplate.VmTemplate_List($VcId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'ViewComposerDomainAdministrator' { + $AdministratorId = New-Object VMware.HV.ViewComposerDomainAdministratorId + $AdministratorId.id = $EntityId.id + $info = $services.ViewComposerDomainAdministrator.ViewComposerDomainAdministrator_Get($AdministratorId) + return $info.base.userName + } + default { + $base64String = $tokens[$tokens.Length-1] + $mod = $base64String.Length % 4 + if ($mod -ne 0) { + #Length of a string must be multiples of 4 + $base64String = $base64String.PadRight(($base64String.Length + (4 - $mod)), "=") + } + #Convert 4 bytes to 3 bytes base64 decoding + return ([System.Text.Encoding]::ASCII.GetString([System.Convert]:: ` + FromBase64String($base64String))) + } + } + } + end { + [System.gc]::collect() + } +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVDesktopSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool From 608ca60d34220416855dbe9c769b320ecaf09040 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Wed, 4 Jan 2017 13:02:27 +0530 Subject: [PATCH 19/90] Text description for examples, wild card support to farm 1) Added text description for all the advanced function examples 2) Wild card support(only * character support) for farm (Get-HVFarm, Get-HVFarmSummary) --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 169 ++++++++++++++---- 1 file changed, 134 insertions(+), 35 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index f3cb9ea..e1ff3d6 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -918,22 +918,27 @@ function Get-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE + Queries and returns farmInfo based on given parameter farmName Get-HVFarm -FarmName 'Farm-01' .EXAMPLE + Queries and returns farmInfo based on given parameters farmName, farmDisplayName Get-HVFarm -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' .EXAMPLE + Queries and returns farmInfo based on given parameters farmName, farmType Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' .EXAMPLE + Queries and returns farmInfo based on given parameters farmName, FarmType etc Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Queries and returns farmInfo based on parameter farmName with wild character * + Get-HVFarm -FarmName 'Farm-0*' .OUTPUTs - Returns the list of FarmSummaryView or FarmInfo object matching the query criteria. + Returns the list of FarmInfo object matching the query criteria. .NOTES Author : Ankit Gupta. @@ -979,6 +984,10 @@ function Get-HVFarm { break } $farmList = Find-HVFarm -Param $PSBoundParameters + if (! $farmList) { + Write-Host "No farm Found with given search parameters" + breakss + } $farm_service_helper = New-Object VMware.Hv.FarmService $queryResults = @() foreach ($id in $farmList.id) { @@ -1015,22 +1024,27 @@ function Get-HVFarmSummary { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Queries and returns farmSummary objects based on given parameter farmName + Get-HVFarmSummary -FarmName 'Farm-01' .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Queries and returns farmSummary objects based on given parameters farmName, farmDisplayName + Get-HVFarmSummary -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' + Queries and returns farmSummary objects based on given parameters farmName, farmType + Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Queries and returns farmSummary objects based on given parameters farmName, FarmType etc + Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Queries and returns farmSummary objects based on given parameter farmName with wild character * + Get-HVFarmSummary -FarmName 'Farm-0*' .OUTPUTs - Returns the list of FarmSummaryView or FarmInfo object matching the query criteria. + Returns the list of FarmSummary object matching the query criteria. .NOTES Author : Praveen Mathamsetty. @@ -1076,6 +1090,10 @@ function Get-HVFarmSummary { break } $farmList = Find-HVFarm -Param $PSBoundParameters + if (! $farmList) { + Write-Host "No farm Found with given search parameters" + break + } return $farmList } @@ -1099,27 +1117,51 @@ function Find-HVFarm { $query_service_helper = New-Object VMware.Hv.QueryServiceService $query = New-Object VMware.Hv.QueryDefinition + $wildcard = $false # build the query values + if ($params['FarmName'] -and $params['FarmName'].contains('*')) { + $wildcard = $true + } + if ($params['FarmDisplayName'] -and $params['FarmDisplayName'].contains('*')) { + $wildcard = $true + } $query.queryEntityType = 'FarmSummaryView' - [VMware.Hv.queryfilter[]]$filterSet = @() - foreach ($setting in $farmSelectors.Keys) { - if ($null -ne $params[$setting]) { - $equalsFilter = New-Object VMware.Hv.QueryFilterEquals - $equalsFilter.memberName = $farmSelectors[$setting] - $equalsFilter.value = $params[$setting] - $filterSet += $equalsFilter + if (! $wildcard) { + [VMware.Hv.queryfilter[]]$filterSet = @() + foreach ($setting in $farmSelectors.Keys) { + if ($null -ne $params[$setting]) { + $equalsFilter = New-Object VMware.Hv.QueryFilterEquals + $equalsFilter.memberName = $farmSelectors[$setting] + $equalsFilter.value = $params[$setting] + $filterSet += $equalsFilter + } + } + if ($filterSet.Count -gt 0) { + $queryList = New-Object VMware.Hv.QueryFilterAnd + $queryList.Filters = $filterset + $query.Filter = $queryList } - } - if ($filterSet.Count -gt 0) { - $queryList = New-Object VMware.Hv.QueryFilterAnd - $queryList.Filters = $filterset - $query.Filter = $queryList - } - $queryResults = $query_service_helper.QueryService_Query($services, $query) - $farmList = $queryResults.results - + $queryResults = $query_service_helper.QueryService_Query($services, $query) + $farmList = $queryResults.results + } elseif ($wildcard -or [string]::IsNullOrEmpty($farmList)){ + $query.Filter = $null + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $strFilterSet = @() + foreach ($setting in $farmSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and (($setting -eq 'FarmName') -or ($setting -eq 'FarmDisplayName')) ) { + $strFilterSet += '($_.' + $farmSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $farmSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } + } + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $farmList = $queryResults.results | where $scriptBlock + } Return $farmList } @@ -1168,19 +1210,23 @@ function Get-HVPool { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE + Queries and returns pool object(s) based on given parameters poolName, poolType etc. Get-HVPool -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true .EXAMPLE + Queries and returns pool object(s) based on given parameters poolType and userAssignment Get-HVPool -PoolType AUTOMATED -UserAssignment FLOATING .EXAMPLE + Queries and returns pool object(s) based on given parameters poolName, PoolType etc. Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false .EXAMPLE + Queries and returns pool object(s) based on given parameters poolName and HvServer etc. Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs .OUTPUTS - Returns list of objects of type Desktop + Returns list of objects of type DesktopInfo .NOTES Author : Praveen Mathamsetty. @@ -1235,6 +1281,10 @@ function Get-HVPool { break } $poolList = Find-HVPool -Param $PSBoundParameters + if (! $poolList) { + Write-Host "No Pool Found with given search parameters" + break + } $queryResults = @() $desktop_helper = New-Object VMware.Hv.DesktopService foreach ($id in $poolList.id) { @@ -1290,15 +1340,19 @@ function Get-HVPoolSummary { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE + Queries and returns desktopSummaryView based on given parameters poolName, poolType etc. Get-HVPoolSummary -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true .EXAMPLE + Queries and returns desktopSummaryView based on given parameters poolType, userAssignment. Get-HVPoolSummary -PoolType AUTOMATED -UserAssignment FLOATING .EXAMPLE + Queries and returns desktopSummaryView based on given parameters poolName, poolType, userAssignment etc. Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false .EXAMPLE + Queries and returns desktopSummaryView based on given parameters poolName, HvServer etc. Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs .OUTPUTS @@ -1478,30 +1532,38 @@ function Get-HVQueryFilter { .EXAMPLE + Creates queryFilterEquals with given parameters memberName(position 0) and memberValue(position 2) Get-HVQueryFilter data.name -Eq vmware .EXAMPLE + Creates queryFilterEquals with given parameters memberName and memberValue Get-HVQueryFilter -MemberName data.name -Eq -MemberValue vmware .EXAMPLE + Creates queryFilterNotEquals filter with given parameters memberName and memberValue Get-HVQueryFilter data.name -Ne vmware .EXAMPLE + Creates queryFilterContains with given parameters memberName and memberValue Get-HVQueryFilter data.name -Contains vmware .EXAMPLE + Creates queryFilterStartsWith with given parameters memberName and memberValue Get-HVQueryFilter data.name -Startswith vmware .EXAMPLE + Creates queryFilterNot with given parameter filter $filter = Get-HVQueryFilter data.name -Startswith vmware Get-HVQueryFilter -Not $filter .EXAMPLE + Creates queryFilterAnd with given parameter filters array $filter1 = Get-HVQueryFilter data.name -Startswith vmware $filter2 = Get-HVQueryFilter data.name -Contains pool Get-HVQueryFilter -And @($filter1, $filter2) .EXAMPLE + Creates queryFilterOr with given parameter filters array $filter1 = Get-HVQueryFilter data.name -Startswith vmware $filter2 = Get-HVQueryFilter data.name -Contains pool Get-HVQueryFilter -Or @($filter1, $filter2) @@ -1630,22 +1692,24 @@ function Get-HVQueryResult { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE + Returns query results of entityType DesktopSummaryView(position 0) Get-HVQueryResult DesktopSummaryView .EXAMPLE + Returns query results of entityType DesktopSummaryView(position 0) with given filter(position 1) Get-HVQueryResult DesktopSummaryView (Get-HVQueryFilter data.name -Eq vmware) .EXAMPLE + Returns query results of entityType DesktopSummaryView with given filter Get-HVQueryResult -EntityType DesktopSummaryView -Filter (Get-HVQueryFilter desktopSummaryData.name -Eq vmware) .EXAMPLE - Get-HVQueryResult -EntityType DesktopSummaryView -Filter (Get-HVQueryFilter desktopSummaryData.name -Eq vmware) -SortBy desktopSummaryData.displayName - -.EXAMPLE + Returns query results of entityType DesktopSummaryView with given filter and also sorted based on dispalyName $myFilter = Get-HVQueryFilter data.name -Contains vmware Get-HVQueryResult -EntityType DesktopSummaryView -Filter $myFilter -SortBy desktopSummaryData.displayName -SortDescending $false .EXAMPLE + Returns query results of entityType DesktopSummaryView, maximum count equal to limit Get-HVQueryResult DesktopSummaryView -Limit 10 .OUTPUTS @@ -1855,12 +1919,15 @@ function New-HVFarm { Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE + Creates new linkedClone farm by using naming pattern New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" .EXAMPLE + Creates new linkedClone farm by using json file New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json .EXAMPLE + Creates new manual farm by using rdsServers names New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" .OUTPUTS @@ -2683,7 +2750,7 @@ function New-HVPool { New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json .EXAMPLE - Clone new pool from automated linked (or) full clone pool + Clones new pool by using existing pool configuration Get-HVPool -PoolName 'vmwarepool' | New-HVPool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; (OR) $vmwarepool = Get-HVPool -PoolName 'vmwarepool'; New-HVPool -ClonePool $vmwarepool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; @@ -3752,12 +3819,15 @@ function Remove-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE + Delete a given farm. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer .EXAMPLE + Deletes a given Farm object(s). For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. $farm_array | Remove-HVFarm -HvServer $hvServer .EXAMPLE + Deletes a given Farm object. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. $farm1 = Get-HVFarm -FarmName 'Farm-01' Remove-HVFarm -Farm $farm1 @@ -3866,12 +3936,15 @@ function Remove-HVPool { Logs off a session forcibly to virtual machine(s). This operation will also log off a locked session. .EXAMPLE + Deletes pool from disk with given parameters PoolName etc. Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk .EXAMPLE + Deletes specified pool from disk $pool_array | Remove-HVPool -HvServer $hvServer -DeleteFromDisk .EXAMPLE + Deletes specified pool and VM(s) associations are removed from view Manager Remove-HVPool -Pool $pool1 .OUTPUTS @@ -3956,7 +4029,7 @@ function Remove-HVPool { foreach ($item in $poolList) { if ($terminateSession) { #Terminate session - $queryResults = Get-HVQueryResults MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item.id) + $queryResults = Get-HVQueryResult MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item.id) $sessions += $queryResults.base.session if ($null -ne $sessions) { $session_service_helper = New-Object VMware.Hv.SessionService @@ -4021,18 +4094,23 @@ function Set-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Set-HVFarm -FarmName 'Farm-o1' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' + Updates farm configuration by using json file + Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' .EXAMPLE - Set-HVFarm -FarmName 'Farm-o1' -Key 'base.description' -Value 'updated description' + Updates farm configuration with given parameters key and value + Set-HVFarm -FarmName 'Farm-01' -Key 'base.description' -Value 'updated description' .EXAMPLE + Updates farm(s) configuration with given parameters key and value $farm_array | Set-HVFarm -Key 'base.description' -Value 'updated description' .EXAMPLE + Enables provisioning to specified farm Set-HVFarm -farm 'Farm2' -Start .EXAMPLE + Enables specified farm Set-HVFarm -farm 'Farm2' -Enable .OUTPUTS @@ -4218,21 +4296,27 @@ function Set-HVPool { Path of the JSON specification file containing key/value pair. .EXAMPLE + Updates pool configuration by using json file Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' .EXAMPLE + Updates pool configuration with given parameters key and value Set-HVPool -PoolName 'RDSPool' -Key 'base.description' -Value 'update description' .Example + Disables specified pool Set-HVPool -PoolName 'LnkClone' -Disable .Example + Enables specified pool Set-HVPool -PoolName 'LnkClone' -Enable .Example + Enables provisioning to specified pool Set-HVPool -PoolName 'LnkClone' -Start .Example + Disables provisioning to specified pool Set-HVPool -PoolName 'LnkClone' -Stop .OUTPUTS @@ -4422,9 +4506,11 @@ function Start-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE + Requests a recompose of RDS Servers in the specified automated farm Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' .EXAMPLE + Requests a recompose task for automated farm in specified time $myTime = Get-Date '10/03/2016 12:30:00' Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime @@ -4692,19 +4778,24 @@ function Start-HVPool { View API service object of Connect-HVServer cmdlet. .EXAMPLE + Requests a recompose of machines in the specified pool Start-HVPool -Recompose -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' .EXAMPLE + Requests a refresh of machines in the specified pool Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF .EXAMPLE + Requests a rebalance of machines in a pool with specified time $myTime = Get-Date '10/03/2016 12:30:00' Start-HVPool -Rebalance -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -StartTime $myTime .EXAMPLE + Requests an update of push image operation on the specified Instant Clone Engine sourced pool Start-HVPool -SchedulePushImage -Pool 'InstantPool' -LogoffSetting FORCE_LOGOFF -ParentVM 'InsParentVM' -SnapshotVM 'InsSnapshotVM' .EXAMPLE + Requests a cancellation of the current scheduled push image operation on the specified Instant Clone Engine sourced pool Start-HVPool -CancelPushImage -Pool 'InstantPool' .OUTPUTS @@ -5130,16 +5221,20 @@ function Get-HVMachine { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE + Queries VM(s) with given parameter poolName Get-HVDesktop -PoolName 'ManualPool' .EXAMPLE + Queries VM(s) with given parameter machineName Get-HVDesktop -MachineName 'PowerCLIVM' .EXAMPLE + Queries VM(s) with given parameter vm state Get-HVDesktop -State CUSTOMIZING .EXAMPLE - Get-HVDesktop -DnsName 'powercli-*' -State CUSTOMIZING + Queries VM(s) with given parameter dnsName with wildcard character * + Get-HVDesktop -DnsName 'powercli-*' .OUTPUTS Returns list of objects of type MachineInfo @@ -5247,16 +5342,20 @@ function Get-HVMachineSummary { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE + Queries VM(s) with given parameter poolName Get-HVDesktopSummary -PoolName 'ManualPool' .EXAMPLE + Queries VM(s) with given parameter machineName Get-HVDesktopSummary -MachineName 'PowerCLIVM' .EXAMPLE + Queries VM(s) with given parameter vm state Get-HVDesktopSummary -State CUSTOMIZING .EXAMPLE - Get-HVDesktopSummary -DnsName 'powercli-*' -State CUSTOMIZING + Queries VM(s) with given parameter dnsName with wildcard character * + Get-HVDesktopSummary -DnsName 'powercli-*' .OUTPUTS Returns list of objects of type MachineNamesView @@ -5574,7 +5673,7 @@ function Get-HVInternalName { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Decodes and returns human readable name + Decodes Horizon API Id and returns human readable name Get-HVInternalName -EntityId $entityId .OUTPUTS From 3f3bd0314e171ac7ecf6abee3e817e02ecaf3542 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Fri, 6 Jan 2017 12:54:51 -0800 Subject: [PATCH 20/90] VSAN HCL Database Script VSAN HCL Database script --- Scripts/Get-VsanHclDatabase.ps1 | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Scripts/Get-VsanHclDatabase.ps1 diff --git a/Scripts/Get-VsanHclDatabase.ps1 b/Scripts/Get-VsanHclDatabase.ps1 new file mode 100644 index 0000000..66ac72f --- /dev/null +++ b/Scripts/Get-VsanHclDatabase.ps1 @@ -0,0 +1,29 @@ +Function Get-VsanHclDatabase { +<# + .NOTES + =========================================================================== + Created by: Alan Renouf + Organization: VMware + Blog: http://virtu-al.net + Twitter: @alanrenouf + =========================================================================== + .SYNOPSIS + This function will allow you to view and download the VSAN Hardware Compatability List (HCL) Database + + .DESCRIPTION + Use this function to view or download the VSAN HCL + .EXAMPLE + View the latest online HCL Database from online source + PS C:\> Get-VsanHclDatabase | Format-Table + .EXAMPLE + Download the latest HCL Database from online source and store locally + PS C:\> Get-VsanHclDatabase -filepath ~/hcl.json +#> +param ($filepath) + $uri = "https://partnerweb.vmware.com/service/vsan/all.json" + If ($filepath) { + Invoke-WebRequest -Uri $uri -OutFile $filepath + } Else { + Invoke-WebRequest -Uri $uri | ConvertFrom-Json | Select-Object -ExpandProperty Data | Select-object -ExpandProperty Controller + } +} \ No newline at end of file From 0c5518d439e57dbc6cb89e8427779f87d2ab0015 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Sun, 8 Jan 2017 21:33:47 +0530 Subject: [PATCH 21/90] All parameters should be configured through JSON 1. Create JSON files that will help in creation of new pools based on existing pools configuration. 2. Configure all the pool attributes through JSON file. 3. Validation of the attributes passed in the JSON file. --- .../Farm}/AutomatedLinkedCloneFarm.json | 44 +- .../{New-HVFarm => Json/Farm}/ManualFarm.json | 22 +- .../{New-HVPool => Json/Pool}/FullClone.json | 44 +- .../Pool}/InstantClone.json | 39 +- .../Pool}/LinkedClone.json | 69 +- .../{New-HVPool => Json/Pool}/ManualSpec.json | 42 +- .../VMware.Hv.Helper/Json/Pool/RdsSpec.json | 27 + .../VMware.Hv.Helper/New-HVPool/RdsSpec.json | 16 - .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 1269 +++++++++++++++-- 9 files changed, 1382 insertions(+), 190 deletions(-) rename Modules/VMware.Hv.Helper/{New-HVFarm => Json/Farm}/AutomatedLinkedCloneFarm.json (84%) rename Modules/VMware.Hv.Helper/{New-HVFarm => Json/Farm}/ManualFarm.json (51%) rename Modules/VMware.Hv.Helper/{New-HVPool => Json/Pool}/FullClone.json (82%) rename Modules/VMware.Hv.Helper/{New-HVPool => Json/Pool}/InstantClone.json (88%) rename Modules/VMware.Hv.Helper/{New-HVPool => Json/Pool}/LinkedClone.json (80%) rename Modules/VMware.Hv.Helper/{New-HVPool => Json/Pool}/ManualSpec.json (54%) create mode 100644 Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json delete mode 100644 Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json diff --git a/Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json similarity index 84% rename from Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json rename to Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json index 3706374..32c3f9a 100644 --- a/Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json +++ b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json @@ -1,17 +1,31 @@ { "Type": "AUTOMATED", "Data": { - "Name": "LCFarmTest", - "DisplayName": "Ankit LC Farm Test", + "Name": "LCFarmJson", + "DisplayName": "FarmJsonTest", "AccessGroup": "Root", - "Description": "created LC Farm from PS", + "Description": "created LC Farm from PS via JSON", "Enabled": null, "Deleting": false, - "Settings": null, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, "Desktop": null, - "DisplayProtocolSettings": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, "ServerErrorThreshold": null, - "MirageConfigurationOverrides": null + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } }, "AutomatedFarmSpec": { "ProvisioningType": "VIEW_COMPOSER", @@ -19,7 +33,7 @@ "RdsServerNamingSpec": { "NamingMethod": "PATTERN", "PatternNamingSettings": { - "NamingPattern": "LCFarmVM_PS", + "NamingPattern": "LCFarmVMPS", "MaxNumberOfRDSServers": 1 } }, @@ -28,17 +42,17 @@ "StopProvisioningOnError": true, "MinReadyVMsOnVComposerMaintenance": 0, "VirtualCenterProvisioningData": { - "ParentVm": "Win_Server_2012_R2", - "Snapshot": "Snap_RDS", + "ParentVm": "RDSServer", + "Snapshot": "RDS_SNAP1", "Datacenter": null, - "VmFolder": "AnkitPoolVM", - "HostOrCluster": "cls", - "ResourcePool": "cls" + "VmFolder": "Praveen", + "HostOrCluster": "CS-1", + "ResourcePool": "CS-1" }, "VirtualCenterStorageSettings": { "Datastores": [ { - "Datastore": "datastore1 (5)", + "Datastore": "Datastore1", "StorageOvercommit": "UNBOUNDED" } ], @@ -67,7 +81,7 @@ "AdContainer": "CN=Computers", "ReusePreExistingAccounts": false, "SysprepCustomizationSettings": { - "CustomizationSpec": "RDSH_Cust2" + "CustomizationSpec": "PraveenCust" } }, "RdsServerMaxSessionsData": { @@ -76,5 +90,5 @@ } }, "ManualFarmSpec": null, - "NetBiosName" : "adankit" + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json similarity index 51% rename from Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json rename to Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json index cf674c1..3dd1678 100644 --- a/Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json +++ b/Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json @@ -7,17 +7,31 @@ "Description": "Manual PS Test", "Enabled": null, "Deleting": false, - "Settings": null, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, "Desktop": null, - "DisplayProtocolSettings": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, "ServerErrorThreshold": null, - "MirageConfigurationOverrides": null + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } }, "AutomatedFarmSpec": null, "ManualFarmSpec": { "RdsServers": [ { - "rdsServer": "WIN-ORKA1Q8B0P7" + "rdsServer": "RDSServer.adviewdev.eng.vmware.com" } ] } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/FullClone.json b/Modules/VMware.Hv.Helper/Json/Pool/FullClone.json similarity index 82% rename from Modules/VMware.Hv.Helper/New-HVPool/FullClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/FullClone.json index 2ae9539..e6d7fae 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/FullClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/FullClone.json @@ -5,7 +5,44 @@ "AccessGroup": "Root", "Description": "create full clone via JSON" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "VIRTUAL_CENTER", @@ -69,7 +106,7 @@ "NoCustomizationSettings": { "DoNotPowerOnVMsAfterCreation": false }, - "SysprepCustomizationSettings": null, + "SysprepCustomizationSettings": {"customizationSpec" : "praveencust"}, "QuickprepCustomizationSettings": null, "CloneprepCustomizationSettings": null } @@ -77,6 +114,5 @@ "ManualDesktopSpec": null, "RdsDesktopSpec": null, "GlobalEntitlementData": null, - "NetBiosName" : "adviewdev", - "SysPrepName" : "praveencust" + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json b/Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json similarity index 88% rename from Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json index a8da482..4c3c584 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json @@ -5,7 +5,44 @@ "AccessGroup": "ROOT", "Description": "create instant pool" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "ALWAYS_POWERED_ON", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "DELETE", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "INSTANT_CLONE_ENGINE", diff --git a/Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json b/Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json similarity index 80% rename from Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json index 53171a6..ee3dfac 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json @@ -5,7 +5,44 @@ "AccessGroup": "Root", "Description": "created linkedclone pool from ps" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["RDP","PCOIP", "BLAST" ], + "defaultDisplayProtocol": "PCOIP", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": null + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "VIEW_COMPOSER", @@ -33,7 +70,7 @@ "Template": null, "ParentVm": "Agent_pra", "Snapshot": "kb-hotfix", - "Datacenter": null, + "Datacenter": "Dev-Dc", "VmFolder": "Praveen", "HostOrCluster": "CS-1", "ResourcePool": "CS-1" @@ -52,7 +89,8 @@ "UseNativeSnapshots": false, "SpaceReclamationSettings": { "ReclaimVmDiskSpace": false, - "ReclamationThresholdGB": null + "ReclamationThresholdGB": null, + "BlackoutTimes" : null }, "PersistentDiskSettings": { "RedirectWindowsProfile": false, @@ -75,19 +113,31 @@ } }, "VirtualCenterNetworkingSettings": { - "Nics": null + "Nics": [ + { + "Nic": "nicName", + "NetworkLabelAssignmentSpecs": [ + { + "Enabled" : false, + "networkLabel" : null, + "maxLabelType" : null, + "maxLabel" : null + } + ] + } + ] } }, "VirtualCenterManagedCommonSettings": { "TransparentPageSharingScope": "VM" }, "CustomizationSettings": { - "CustomizationType": "QUICK_PREP", - "DomainAdministrator": null, + "CustomizationType": "SYS_PREP", + "DomainAdministrator": "administrator", "AdContainer": "CN=Computers", "ReusePreExistingAccounts": false, "NoCustomizationSettings": null, - "SysprepCustomizationSettings": null, + "SysprepCustomizationSettings": {"customizationSpec" : "praveencust"}, "QuickprepCustomizationSettings": { "PowerOffScriptName": null, "PowerOffScriptParameters": null, @@ -99,7 +149,6 @@ }, "ManualDesktopSpec": null, "RdsDesktopSpec": null, - "GlobalEntitlementData": null, - "NetBiosName" : "adviewdev", - "SysPrepName" : "praveencust" + "GlobalEntitlementData": null, + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json b/Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json similarity index 54% rename from Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json rename to Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json index b302bba..8b95389 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json @@ -5,7 +5,44 @@ "AccessGroup": "ROOT", "Description": "Manual pool creation" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "MANUAL", "AutomatedDesktopSpec": null, "ManualDesktopSpec": { @@ -16,7 +53,7 @@ "Source": "VIRTUAL_CENTER", "Machines": [ { - "Machine" : "PowerCLI-VM" + "Machine" : "Praveen_Agent" } ], "VirtualCenter": null, @@ -32,4 +69,5 @@ }, "RdsDesktopSpec": null, "GlobalEntitlementData": null + } diff --git a/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json b/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json new file mode 100644 index 0000000..bab0c67 --- /dev/null +++ b/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json @@ -0,0 +1,27 @@ +{ + "Base": { + "Name" : "RdsJson", + "DisplayName": "TestRDSPS", + "AccessGroup": "Root", + "Description": "Testing PS" + }, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": null, + "displayProtocolSettings": null, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": null + }, + "Type": "RDS", + "AutomatedDesktopSpec": null, + "ManualDesktopSpec": null, + "RdsDesktopSpec": { + "Farm": "test1" + }, + "GlobalEntitlementData": null +} diff --git a/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json b/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json deleted file mode 100644 index 86b3571..0000000 --- a/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Base": { - "Name" : "RdsJson", - "DisplayName": "TestRDSPS", - "AccessGroup": "Root", - "Description": "Testing PS" - }, - "DesktopSettings": null, - "Type": "RDS", - "AutomatedDesktopSpec": null, - "ManualDesktopSpec": null, - "RdsDesktopSpec": { - "Farm": "Farm2" - }, - "GlobalEntitlementData": null -} diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index e1ff3d6..2a0967b 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -294,7 +294,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A return } } - if ($pscmdlet.ShouldProcess($machineList)) { + if ($pscmdlet.ShouldProcess($machines)) { $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) } return $machineList @@ -412,7 +412,7 @@ function Add-HVRDSServer { try { $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($farmSpecObj) { @@ -432,7 +432,7 @@ function Add-HVRDSServer { 'MANUAL' { try { $serverList = Get-RegisteredRDSServer -services $services -serverList $rdsServers - if ($pscmdlet.ShouldProcess($serverList)) { + if ($pscmdlet.ShouldProcess($rdsServers)) { $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) } return $serverList @@ -985,8 +985,8 @@ function Get-HVFarm { } $farmList = Find-HVFarm -Param $PSBoundParameters if (! $farmList) { - Write-Host "No farm Found with given search parameters" - breakss + Write-Host "Get-HVFarm: No Farm Found with given search parameters" + break } $farm_service_helper = New-Object VMware.Hv.FarmService $queryResults = @() @@ -1089,12 +1089,7 @@ function Get-HVFarmSummary { Write-Error "Could not retrieve ViewApi services from connection object" break } - $farmList = Find-HVFarm -Param $PSBoundParameters - if (! $farmList) { - Write-Host "No farm Found with given search parameters" - break - } - return $farmList + Return Find-HVFarm -Param $PSBoundParameters } function Find-HVFarm { @@ -1282,7 +1277,7 @@ function Get-HVPool { } $poolList = Find-HVPool -Param $PSBoundParameters if (! $poolList) { - Write-Host "No Pool Found with given search parameters" + Write-Host "Get-HVPool: No Pool Found with given search parameters" break } $queryResults = @() @@ -1410,12 +1405,7 @@ function Get-HVPoolSummary { Write-Error "Could not retrieve ViewApi services from connection object" break } - $poolList = Find-HVPool -Param $psboundparameters - if (!$poolList) { - Write-Host "No Pool Found with given search parameters" - break - } - Return $poolList + Return Find-HVPool -Param $psboundparameters } function Find-HVPool { @@ -1798,6 +1788,7 @@ function Get-HVQueryResult { } } + function New-HVFarm { <# .Synopsis @@ -1984,6 +1975,71 @@ function New-HVFarm { [boolean] $Enable = $true, + #farmSpec.data.settings.disconnectedSessionTimeoutPolicy + [Parameter(Mandatory = $false)] + [ValidateSet("IMMEDIATE","NEVER","AFTER")] + [string] + $DisconnectedSessionTimeoutPolicy = "NEVER", + + #farmSpec.data.settings.disconnectedSessionTimeoutMinutes + [Parameter(Mandatory = $false)] + [ValidateRange(1,[Int]::MaxValue)] + [int] + $DisconnectedSessionTimeoutMinutes, + + #farmSpec.data.settings.emptySessionTimeoutPolicy + [Parameter(Mandatory = $false)] + [ValidateSet("NEVER","AFTER")] + [string] + $EmptySessionTimeoutPolicy = "AFTER", + + #farmSpec.data.settings.emptySessionTimeoutMinutes + [Parameter(Mandatory = $false)] + [ValidateSet(1,[Int]::MaxValue)] + [int] + $EmptySessionTimeoutMinutes = 1, + + #farmSpec.data.settings.logoffAfterTimeout + [Parameter(Mandatory = $false)] + [boolean] + $LogoffAfterTimeout = $false, + + #farmSpec.data.displayProtocolSettings.defaultDisplayProtocol + [Parameter(Mandatory = $false)] + [ValidateSet("RDP","PCOIP","BLAST")] + [string] + $DefaultDisplayProtocol = "PCOIP", + + #farmSpec.data.displayProtocolSettings.allowDisplayProtocolOverride + [Parameter(Mandatory = $false)] + [boolean] + $AllowDisplayProtocolOverride = $true, + + #farmSpec.data.displayProtocolSettings.enableHTMLAccess + [Parameter(Mandatory = $false)] + [boolean] + $EnableHTMLAccess = $false, + + #farmSpec.data.serverErrorThreshold + [Parameter(Mandatory = $false)] + [ValidateRange(0,[Int]::MaxValue)] + $ServerErrorThreshold = 0, + + #farmSpec.data.mirageConfigurationOverrides.overrideGlobalSetting + [Parameter(Mandatory = $false)] + [boolean] + $OverrideGlobalSetting = $false, + + #farmSpec.data.mirageConfigurationOverrides.enabled + [Parameter(Mandatory = $false)] + [boolean] + $MirageServerEnabled, + + #farmSpec.data.mirageConfigurationOverrides.url + [Parameter(Mandatory = $false)] + [string] + $Url, + #farmSpec.automatedfarmSpec.virtualCenter if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [string] @@ -2014,15 +2070,25 @@ function New-HVFarm { [string] $ResourcePool, + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.dataCenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string] + $dataCenter, + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [string[]] $Datastores, + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string[]] + $StorageOvercommit = $null, + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] - [string] - $UseVSAN, + [boolean] + $UseVSAN = $false, #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] @@ -2059,6 +2125,37 @@ function New-HVFarm { [int] $MaximumCount = 1, + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseSeparateDatastoresReplicaAndOSDisks = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string] + $ReplicaDiskDatastore, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useNativeSnapshots + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseNativeSnapshots = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $ReclaimVmDiskSpace = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(0,[Int]::MaxValue)] + [int] + $ReclamationThresholdGB = 1, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.blackoutTimes + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [VMware.Hv.FarmBlackoutTime[]] + $BlackoutTimes, + #farmSpec.automatedfarmSpec.customizationSettings.adContainer if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] [string] @@ -2074,12 +2171,29 @@ function New-HVFarm { [string] $DomainAdmin = $null, + #farmSpec.automatedfarmSpec.customizationSettings.reusePreExistingAccounts + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Boolean] + $ReusePreExistingAccounts = $false, + #farmSpec.automatedfarmSpec.customizationSettings.sysprepCustomizationSettings.customizationSpec if LINKED_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [string] $SysPrepName, - ##farmSpec.manualfarmSpec.rdsServers + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet("UNLIMITED", "LIMITED")] + [string] + $MaxSessionsType = "UNLIMITED", + + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1, [int]::MaxValue)] + [int] + $MaxSessions, + + #farmSpec.manualfarmSpec.rdsServers [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [string[]] $RdsServers, @@ -2127,9 +2241,9 @@ function New-HVFarm { if ($farmName) { try { - $sourceFarm = Get-HVFarm -farmName $farmName -hvServer $hvServer + $sourceFarm = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($sourceFarm) { @@ -2145,18 +2259,33 @@ function New-HVFarm { Write-Error "Json file exception, $_" break } + try { + Test-HVFarmSpec -JsonObject $jsonObject + } catch { + Write-Error "Json object validation failed, $_" + break + } if ($jsonObject.type -eq 'AUTOMATED') { $farmType = 'AUTOMATED' + $provisioningType = $jsonObject.ProvisioningType if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenter) { $vCenter = $jsonObject.AutomatedFarmSpec.VirtualCenter } $linkedClone = $true $netBiosName = $jsonObject.NetBiosName + if ($null -ne $jsonObject.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator) { + $domainAdministrator = $jsonObject.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator + } $adContainer = $jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer + $reusePreExistingAccounts = $jsonObject.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts + $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern $maximumCount = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers + $enableProvisioning = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.EnableProvisioning + $stopProvisioningOnError = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError + $minReady = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.MinReadyVMsOnVComposerMaintenance $transparentPageSharingScope = $jsonObject.AutomatedFarmSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope @@ -2170,10 +2299,36 @@ function New-HVFarm { $hostOrCluster = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.HostOrCluster $resourcePool = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ResourcePool $dataStoreList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores + foreach ($dtStore in $dataStoreList) { $datastores += $dtStore.Datastore + $storageOvercommit += $dtStore.StorageOvercommit } - $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec + $useVSan = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.UseVSan + $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks + if ($useSeparateDatastoresReplicaAndOSDisks) { + $replicaDiskDatastore = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.ReplicaDiskDatastore + } + $useNativeSnapshots = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseNativeSnapshots + $reclaimVmDiskSpace = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclaimVmDiskSpace + if ($reclaimVmDiskSpace) { + $ReclamationThresholdGB = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclamationThresholdGB + if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes) { + $blackoutTimesList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes + foreach ($blackout in $blackoutTimesList) { + $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime + $blackoutObj.Days = $blackout.Days + $blackoutObj.StartTime = $blackout.StartTime + $blackoutObj.EndTime = $blackoutObj.EndTime + $blackoutTimes += $blackoutObj + } + } + } + $maxSessionsType = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType + if ($maxSessionsType -eq "LIMITED") { + $maxSessions = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessions + } + } elseif ($jsonObject.type -eq 'MANUAL') { $manual = $true $farmType = 'MANUAL' @@ -2183,10 +2338,33 @@ function New-HVFarm { $rdsServers += $RdsServerObj.rdsServer } } - $farmDisplayName = $jsonObject.data.DisplayName - $description = $jsonObject.data.Description - $accessGroup = $jsonObject.data.AccessGroup - $farmName = $jsonObject.data.name + $farmDisplayName = $jsonObject.Data.DisplayName + $description = $jsonObject.Data.Description + $accessGroup = $jsonObject.Data.AccessGroup + $farmName = $jsonObject.Data.name + if ($null -ne $jsonObject.Data.Enabled) { + $enable = $jsonObject.Data.Enabled + } + if ($null -ne $jsonObject.Data.Settings) { + $disconnectedSessionTimeoutPolicy = $jsonObject.Data.Settings.DisconnectedSessionTimeoutPolicy + $disconnectedSessionTimeoutMinutes = $jsonObject.Data.Settings.DisconnectedSessionTimeoutMinutes + $emptySessionTimeoutPolicy = $jsonObject.Data.Settings.EmptySessionTimeoutPolicy + $emptySessionTimeoutMinutes = $jsonObject.Data.Settings.EmptySessionTimeoutMinutes + $logoffAfterTimeout = $jsonObject.Data.Settings.LogoffAfterTimeout + } + if ($null -ne $jsonObject.Data.DisplayProtocolSettings) { + $defaultDisplayProtocol = $jsonObject.Data.DisplayProtocolSettings.DefaultDisplayProtocol + $allowDisplayProtocolOverride = $jsonObject.Data.DisplayProtocolSettings.AllowDisplayProtocolOverride + $enableHTMLAccess = $jsonObject.Data.DisplayProtocolSettings.EnableHTMLAccess + } + if ($null -ne $jsonObject.Data.serverErrorThreshold) { + $serverErrorThreshold = $jsonObject.Data.serverErrorThreshold + } + if ($null -ne $jsonObject.Data.MirageConfigurationOverrides) { + $overrideGlobalSetting = $jsonObject.Data.MirageConfigurationOverrides.OverrideGlobalSetting + $mirageserverEnabled = $jsonObject.Data.MirageConfigurationOverrides.Enabled + $url = $jsonObject.Data.MirageConfigurationOverrides.url + } } if ($linkedClone) { @@ -2245,17 +2423,17 @@ function New-HVFarm { $farmVirtualCenterManagedCommonSettings = $farmSpecObj.AutomatedFarmSpec.virtualCenterManagedCommonSettings } - if (!$farmVirtualMachineNamingSpec) { + if ($farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec) { $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod = $namingMethod $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern = $namingPattern $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers = $maximumCount } else { $vmNamingSpec = New-Object VMware.Hv.FarmRDSServerNamingSpec - $vmNamingSpec.NamingMethod = 'PATTERN' - + $vmNamingSpec.NamingMethod = $namingMethod $vmNamingSpec.patternNamingSettings = New-Object VMware.Hv.FarmPatternNamingSettings $vmNamingSpec.patternNamingSettings.namingPattern = $namingPattern $vmNamingSpec.patternNamingSettings.maxNumberOfRDSServers = $maximumCount + $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec = $vmNamingSpec } # @@ -2263,8 +2441,10 @@ function New-HVFarm { # try { $farmVirtualCenterProvisioningData = Get-HVFarmProvisioningData -vc $virtualCenterID -vmObject $farmVirtualCenterProvisioningData - $hostClusterId = $farmVirtualCenterProvisioningData.HostOrCluster - $farmVirtualCenterStorageSettings = Get-HVFarmStorageObject -hostclusterID $hostClusterId -storageObject $farmVirtualCenterStorageSettings + + $HostOrCluster_helper = New-Object VMware.Hv.HostOrClusterService + $hostClusterIds = (($HostOrCluster_helper.HostOrCluster_GetHostOrClusterTree($services, $farmVirtualCenterProvisioningData.datacenter)).treeContainer.children.info).Id + $farmVirtualCenterStorageSettings = Get-HVFarmStorageObject -hostclusterIDs $hostClusterIds -storageObject $farmVirtualCenterStorageSettings $farmVirtualCenterNetworkingSettings = Get-HVFarmNetworkSetting -networkObject $farmVirtualCenterNetworkingSettings $farmCustomizationSettings = Get-HVFarmCustomizationSetting -vc $virtualCenterID -customObject $farmCustomizationSettings } catch { @@ -2273,31 +2453,20 @@ function New-HVFarm { break } - $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = "UNLIMITED" - - if (!$FarmVirtualCenterProvisioningSettings) { - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.enableProvisioning = $true - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $true - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = 0 - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings - - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings - $farmSpecObj.AutomatedFarmSpec.ProvisioningType = $provisioningType - $farmSpecObj.AutomatedFarmSpec.VirtualCenter = $virtualCenterID - } else { - $FarmVirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData - $FarmVirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings - $FarmVirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings - - $FarmAutomatedFarmSpec = New-Object VMware.Hv.FarmAutomatedFarmSpec - $FarmAutomatedFarmSpec.ProvisioningType = $provisioningType - $FarmAutomatedFarmSpec.VirtualCenter = $virtualCenterID - $FarmAutomatedFarmSpec.VirtualCenterProvisioningSettings = $farmVirtualCenterProvisioningSettings - $FarmAutomatedFarmSpec.virtualCenterManagedCommonSettings = $farmVirtualCenterManagedCommonSettings - $FarmAutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = $maxSessionsType + if ($maxSessionsType -eq "LIMITED") { + $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = $maxSessions } + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.enableProvisioning = $enableProvisioning + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $stopProvisioningOnError + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = $minReady + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings + + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.ProvisioningType = $provisioningType + $farmSpecObj.AutomatedFarmSpec.VirtualCenter = $virtualCenterID } } @@ -2313,6 +2482,29 @@ function New-HVFarm { $farmData.name = $farmName $farmData.DisplayName = $farmDisplayName $farmData.Description = $description + if ($farmData.Settings) { + $farmData.Settings.DisconnectedSessionTimeoutPolicy = $disconnectedSessionTimeoutPolicy + if ($disconnectedSessionTimeoutPolicy -eq "AFTER") { + $farmData.Settings.DisconnectedSessionTimeoutMinutes = $disconnectedSessionTimeoutMinutes + } + $farmData.Settings.EmptySessionTimeoutPolicy = $emptySessionTimeoutPolicy + if ($emptySessionTimeoutPolicy -eq "AFTER") { + $farmData.Settings.EmptySessionTimeoutMinutes = $emptySessionTimeoutMinutes + } + $logoffAfterTimeout = $farmData.Settings.logoffAfterTimeout + } + if ($farmData.DisplayProtocolSettings) { + $farmData.DisplayProtocolSettings.DefaultDisplayProtocol = $defaultDisplayProtocol + $farmData.DisplayProtocolSettings.AllowDisplayProtocolOverride = $AllowDisplayProtocolOverride + $farmData.DisplayProtocolSettings.EnableHTMLAccess = $enableHTMLAccess + } + if ($farmData.MirageConfigurationOverrides){ + $farmData.MirageConfigurationOverrides.OverrideGlobalSetting = $overrideGlobalSetting + $farmData.MirageConfigurationOverrides.Enabled = $mirageServerEnabled + if ($url) { + $farmData.MirageConfigurationOverrides.Url = $url + } + } $farmSpecObj.type = $farmType if ($FarmAutomatedFarmSpec) { @@ -2327,7 +2519,8 @@ function New-HVFarm { $myDebug = convertto-json -InputObject $farmSpecObj -depth 12 $myDebug | out-file -filepath c:\temp\copiedfarm.json #> - if ($pscmdlet.ShouldProcess($farmSpecObj)) { + + if ($pscmdlet.ShouldProcess($farmSpecObj.data.name)) { $Id = $farm_service_helper.Farm_Create($services, $farmSpecObj) } return $farmSpecObj @@ -2339,6 +2532,85 @@ function New-HVFarm { } +function Test-HVFarmSpec { + param( + [Parameter(Mandatory = $true)] + $JsonObject + ) + if ($null -eq $jsonObject.Type) { + Throw "Specify type of farm in json file" + } + $jsonFarmTypeArray = @('AUTOMATED','MANUAL') + if (! ($jsonFarmTypeArray -contains $jsonObject.Type)) { + Throw "Farm type must be AUTOMATED or MANUAL" + } + if ($null -eq $jsonObject.Data.Name) { + Throw "Specify farm name in json file" + } + if ($null -eq $jsonObject.Data.AccessGroup) { + Throw "Specify horizon access group in json file" + } + if ($jsonObject.Type -eq "AUTOMATED"){ + $jsonProvisioningType = $jsonObject.AutomatedFarmSpec.ProvisioningType + if ($null -eq $jsonProvisioningType) { + Throw "Must specify provisioningType in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.namingMethod) { + Throw "Must specify naming method to PATTERN in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings) { + Throw "Specify Naming pattern settings in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern) { + Throw "Specify specified naming pattern in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.enableProvisioning) { + Throw "Specify Whether to enable provisioning in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.stopProvisioningOnError) { + Throw "Specify Whether provisioning on all VMs stops on error in json file" + } + $jsonTemplate = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + if (!( ($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) )) ) { + Throw "Must specify Template or (ParentVm and Snapshot) names in json file" + } + if ($null -eq $jsonVmFolder) { + Throw "Must specify VM folder in json file to deploy the VMs" + } + if ($null -eq $jsonHostOrCluster) { + Throw "Must specify Host or cluster in json file to deploy the VMs" + } + if ($null -eq $resourcePool) { + Throw "Must specify Resource pool in json file to deploy the VMs" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.useVSan) { + Throw "Must specify whether to use virtual SAN or not" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.CustomizationSettings.customizationType) { + Throw "Specify Type of customization to use in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { + Throw "Specify Sysprep customization settings in json file" + } + if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType) { + Throw "Specify MaxSessionsType in json file" + } + } elseif ($jsonObject.Type -eq "MANUAL") { + if ($null -eq $jsonObject.manualFarmSpec.rdsServers) { + Throw "Specify rdsServers name in json file" + } + } +} + + function Get-HVFarmProvisioningData { param( [Parameter(Mandatory = $false)] @@ -2359,6 +2631,12 @@ function Get-HVFarmProvisioningData { } $vmObject.ParentVm = $parentVMObj.id $dataCenterID = $parentVMObj.datacenter + if ($dataCenter -and $dataCenterID) { + $baseImageVmInfo = $base_imageVm_helper.BaseImageVm_ListByDatacenter($dataCenterID) + if (! ($baseImageVmInfo.Path -like "/$dataCenter/*")) { + throw "$parentVM not exists in datacenter: [$dataCenter]" + } + } $vmObject.datacenter = $dataCenterID } if ($snapshotVM) { @@ -2411,23 +2689,30 @@ function Get-HVFarmProvisioningData { return $vmObject } + function Get-HVFarmStorageObject { param( - [Parameter(Mandatory = $false)] - [VMware.Hv.FarmVirtualCenterStorageSettings]$StorageObject, [Parameter(Mandatory = $true)] - [VMware.Hv.HostOrClusterId]$HostClusterID + [VMware.Hv.HostOrClusterId[]]$HostClusterIDs, + + [Parameter(Mandatory = $false)] + [VMware.Hv.FarmVirtualCenterStorageSettings]$StorageObject ) if (!$storageObject) { $storageObject = New-Object VMware.Hv.FarmVirtualCenterStorageSettings $FarmSpaceReclamationSettings = New-Object VMware.Hv.FarmSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $false } + if ($reclaimVmDiskSpace) { + $FarmSpaceReclamationSettings.ReclamationThresholdGB = $reclamationThresholdGB + if ($blackoutTimes) { + $FarmSpaceReclamationSettings.BlackoutTimes = $blackoutTimes + } + } $FarmViewComposerStorageSettingsList = @{ - 'useSeparateDatastoresReplicaAndOSDisks' = $false; - 'replicaDiskDatastore' = $FarmReplicaDiskDatastore - 'useNativeSnapshots' = $false; + 'useSeparateDatastoresReplicaAndOSDisks' = $UseSeparateDatastoresReplicaAndOSDisks; + 'useNativeSnapshots' = $useNativeSnapshots; 'spaceReclamationSettings' = $FarmSpaceReclamationSettings; } @@ -2435,18 +2720,33 @@ function Get-HVFarmStorageObject { } if ($datastores) { + if ($StorageOvercommit -and ($datastores.Length -ne $StorageOvercommit.Length) ) { + throw "Parameters datastores length: [$datastores.Length] and StorageOvercommit length: [$StorageOvercommit.Length] should be of same size" + } $Datastore_service_helper = New-Object VMware.Hv.DatastoreService - $datastoreList = $Datastore_service_helper.Datastore_ListDatastoresByHostOrCluster($services, $hostClusterID) + foreach ($hostClusterID in $hostClusterIDs) { + $datastoreList += $Datastore_service_helper.Datastore_ListDatastoresByHostOrCluster($services, $hostClusterID) + } $datastoresSelected = @() foreach ($ds in $datastores) { $datastoresSelected += ($datastoreList | Where-Object { $_.datastoredata.name -eq $ds }).id } + if (! $storageOvercommit) { + foreach ($ds in $datastoresSelected) { + $storageOvercommit += ,'UNBOUNDED' + } + } + $StorageOvercommitCnt = 0 foreach ($ds in $datastoresSelected) { $datastoresObj = New-Object VMware.Hv.FarmVirtualCenterDatastoreSettings $datastoresObj.Datastore = $ds - $datastoresObj.StorageOvercommit = 'UNBOUNDED' + $datastoresObj.StorageOvercommit = $storageOvercommit[$StorageOvercommitCnt] $StorageObject.Datastores += $datastoresObj } + if ($useSeparateDatastoresReplicaAndOSDisks) { + $FarmReplicaDiskDatastore = ($datastoreList | Where-Object { $_.datastoredata.name -eq $replicaDiskDatastore }).id + } + $StorageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = $FarmReplicaDiskDatastore } if ($storageObject.Datastores.Count -eq 0) { throw "No datastores found with name: [$datastores]" @@ -2455,6 +2755,7 @@ function Get-HVFarmStorageObject { return $storageObject } + function Get-HVFarmNetworkSetting { param( [Parameter(Mandatory = $false)] @@ -2466,6 +2767,7 @@ function Get-HVFarmNetworkSetting { return $networkObject } + function Get-HVFarmCustomizationSetting { param( [Parameter(Mandatory = $false)] @@ -2479,7 +2781,7 @@ function Get-HVFarmCustomizationSetting { $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) | Where-Object { $_.base.domain -match $netBiosName }) if (! [string]::IsNullOrWhitespace($domainAdmin)) { $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } else { + } elseif ($null -ne $ViewComposerDomainAdministratorID) { $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id } if ($null -eq $ViewComposerDomainAdministratorID) { @@ -2509,7 +2811,7 @@ function Get-HVFarmCustomizationSetting { $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.AdContainer = $adContainerId - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $false + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = $sysprepCustomizationSettings $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings @@ -2535,10 +2837,16 @@ function Get-FarmSpec { if ($farmType -eq 'AUTOMATED') { $farm_spec_helper.getDataObject().AutomatedFarmSpec.RdsServerNamingSpec.PatternNamingSettings = $farm_helper.getFarmPatternNamingSettingsHelper().getDataObject() $farm_spec_helper.getDataObject().AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings = $farm_helper.getFarmViewComposerStorageSettingsHelper().getDataObject() + + } + $farm_spec_helper.getDataObject().Data.Settings = $farm_helper.getFarmSessionSettingsHelper().getDataObject() + $farm_spec_helper.getDataObject().Data.DisplayProtocolSettings = $farm_helper.getFarmDisplayProtocolSettingsHelper().getDataObject() + $farm_spec_helper.getDataObject().Data.MirageConfigurationOverrides = $farm_helper.getFarmMirageConfigurationOverridesHelper( ).getDataObject() return $farm_spec_helper.getDataObject() } + function New-HVPool { <# .Synopsis @@ -2743,7 +3051,7 @@ function New-HVPool { .EXAMPLE Create new automated linked clone pool with naming method pattern - New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 1 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root + New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 0 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root .EXAMPLE Create new automated linked clone pool by using JSON spec file @@ -2850,7 +3158,6 @@ function New-HVPool { #desktopSpec.automatedDesktopSpec.desktopUserAssignment.userAssigment if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE #desktopSpec.manualDesktopSpec.desktopUserAssignment.userAssigment if MANUAL - [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -2877,6 +3184,120 @@ function New-HVPool { [string[]] $ConnectionServerRestrictions, + #desktopSpec.desktopSettings.deleting + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$Deleting = $false, + + #desktopSpec.desktopSettings.logoffSettings.powerPloicy + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('TAKE_NO_POWER_ACTION', 'ALWAYS_POWERED_ON', 'SUSPEND', 'POWER_OFF')] + [string]$PowerPolicy = 'TAKE_NO_POWER_ACTION', + + #desktopSpec.desktopSettings.logoffSettings.powerPloicy + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('IMMEDIATELY', 'NEVER', 'AFTER')] + [string]$AutomaticLogoffPolicy = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.automaticLogoffMinutes + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,120)] + [int]$AutomaticLogoffMinutes = 120, + + #desktopSpec.desktopSettings.logoffSettings.allowUsersToResetMachines + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$allowUsersToResetMachines = $false, + + #desktopSpec.desktopSettings.logoffSettings.allowMultipleSessionsPerUser + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$allowMultipleSessionsPerUser = $false, + + #desktopSpec.desktopSettings.logoffSettings.deleteOrRefreshMachineAfterLogoff + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NEVER', 'DELETE', 'AFTER')] + [string]$deleteOrRefreshMachineAfterLogoff = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.refreshOsDiskAfterLogoff + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NEVER', 'ALWAYS', 'EVERY', 'AT_SIZE')] + [string]$refreshOsDiskAfterLogoff = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [int]$refreshPeriodDaysForReplicaOsDisk = 120, + + #desktopSpec.desktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,100)] + [int]$refreshThresholdPercentageForReplicaOsDisk, + + #DesktopDisplayProtocolSettings + #desktopSpec.desktopSettings.logoffSettings.supportedDisplayProtocols + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [string[]]$supportedDisplayProtocols = @('RDP', 'PCOIP', 'BLAST'), + + #desktopSpec.desktopSettings.logoffSettings.defaultDisplayProtocol + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [string]$defaultDisplayProtocol = 'PCOIP', + + #desktopSpec.desktopSettings.logoffSettings.allowUsersToChooseProtocol + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [int]$allowUsersToChooseProtocol = $true, + + #desktopSpec.desktopSettings.logoffSettings.enableHTMLAccess + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enableHTMLAccess = $false, + + # DesktopPCoIPDisplaySettings + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.renderer3D + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('MANAGE_BY_VSPHERE_CLIENT', 'AUTOMATIC', 'SOFTWARE', 'HARDWARE', 'DISABLED')] + [string]$renderer3D = 'DISABLED', + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.enableGRIDvGPUs + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enableGRIDvGPUs = $false, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.vRamSizeMB + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(64,512)] + [int]$vRamSizeMB = 96, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.maxNumberOfMonitors + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,4)] + [int]$maxNumberOfMonitors = 2, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.maxResolutionOfAnyOneMonitor + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('WUXGA', 'WSXGA_PLUS', 'WQXGA', 'UHD')] + [string]$maxResolutionOfAnyOneMonitor = 'WUXGA', + + # flashSettings + #desktopSpec.desktopSettings.flashSettings.quality + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NO_CONTROL', 'LOW', 'MEDIUM', 'HIGH')] + [string]$quality = 'NO_CONTROL', + + #desktopSpec.desktopSettings.flashSettings.throttling + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('DISABLED', 'CONSERVATIVE', 'MODERATE', 'AGGRESSIVE')] + [string]$throttling = 'DISABLED', + + #mirageConfigurationOverrides + #desktopSpec.desktopSettings.mirageConfigurationOverrides.overrideGlobalSetting + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$overrideGlobalSetting = $false, + + #desktopSpec.desktopSettings.mirageConfigurationOverrides.enabled + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enabled = $true, + + #desktopSpec.desktopSettings.mirageConfigurationOverrides.url + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string]$url = $true, + #desktopSpec.automatedDesktopSpec.virtualCenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE #desktopSpec.manualDesktopSpec.virtualCenter if MANUAL [Parameter(Mandatory = $false,ParameterSetName = 'MANUAL')] @@ -2924,6 +3345,12 @@ function New-HVPool { [string] $ResourcePool, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.datacenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [string] + $datacenter, #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -2931,13 +3358,116 @@ function New-HVPool { [string[]] $Datastores, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [string[]] + $StorageOvercommit = $null, #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] - [string] - $UseVSAN, + [boolean] + $UseVSAN = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseSeparateDatastoresReplicaAndOSDisks = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $ReplicaDiskDatastore, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseNativeSnapshots = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $ReclaimVmDiskSpace = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(0,[Int]::MaxValue)] + [int] + $ReclamationThresholdGB = 1, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.redirectWindowsProfile if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $RedirectWindowsProfile = $true, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.useSeparateDatastoresPersistentAndOSDisks if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseSeparateDatastoresPersistentAndOSDisks = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string[]] + $PersistentDiskDatastores, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string[]] + $PersistentDiskStorageOvercommit = $null, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.diskSizeMB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(128,[Int]::MaxValue)] + [int] + $DiskSizeMB = 2048, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.diskDriveLetter if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidatePattern("^[D-Z]$")] + [string] + $DiskDriveLetter = "D", + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.redirectDisposableFiles if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $redirectDisposableFiles, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.diskSizeMB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(512,[Int]::MaxValue)] + [int] + $NonPersistentDiskSizeMB = 4096, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.diskDriveLetter if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidatePattern("^[D-Z]|Auto$")] + [string] + $NonPersistentDiskDriveLetter = "Auto", + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.useViewStorageAccelerator if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseViewStorageAccelerator = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.useViewStorageAccelerator if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string] + $ViewComposerDiskTypes = "OS_DISKS", + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.regenerateViewStorageAcceleratorDays if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,999)] + [int] + $RegenerateViewStorageAcceleratorDays = 7, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.blackoutTimes if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [VMware.Hv.DesktopBlackoutTime[]] + $BlackoutTimes, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterNetworkingSettings.nics + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [VMware.Hv.DesktopNetworkInterfaceCardSettings[]] + $Nics, #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] @@ -3042,6 +3572,8 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [string]$NetBiosName, + #desktopSpec.automatedDesktopSpec.customizationSettings.domainAdministrator + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] [string]$DomainAdmin = $null, @@ -3052,13 +3584,48 @@ function New-HVPool { [ValidateSet('CLONE_PREP','QUICK_PREP','SYS_PREP','NONE')] [string] $CustType, - + #desktopSpec.automatedDesktopSpec.customizationSettings.reusePreExistingAccounts if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Boolean] + $ReusePreExistingAccounts = $false, #desktopSpec.automatedDesktopSpec.customizationSettings.sysprepCustomizationSettings.customizationSpec if LINKED_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = "FULL_CLONE")] [string] $SysPrepName, + #desktopSpec.automatedDesktopSpec.customizationSettings.noCustomizationSettings.doNotPowerOnVMsAfterCreation if FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "FULL_CLONE")] + [boolean] + $DoNotPowerOnVMsAfterCreation = $false, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.powerOffScriptName if LINKED_CLONE, INSTANT_CLONE + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptName + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PowerOffScriptName, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.powerOffScriptParameters + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptParameters + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PowerOffScriptParameters, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.postSynchronizationScriptName + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptName + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PostSynchronizationScriptName, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.postSynchronizationScriptParameters + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptParameters + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PostSynchronizationScriptParameters, #manual desktop [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [ValidateSet('VIRTUAL_CENTER','UNMANAGED')] @@ -3160,6 +3727,13 @@ function New-HVPool { Write-Error "Json file exception, $_" break } + + try { + Test-HVDesktopSpec -JsonObject $jsonObject + } catch { + Write-Error "Json object validation failed, $_" + break + } if ($jsonObject.type -eq "AUTOMATED") { $poolType = 'AUTOMATED' if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenter) { @@ -3176,13 +3750,37 @@ function New-HVPool { $custType = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "INSTANT_CLONE_ENGINE") { $InstantClone = $true + if ($null -ne $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings) { + $domainAdmin = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator + $powerOffScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptParameters + } } else { if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "VIEW_COMPOSER") { $LinkedClone = $true - } else { + $domainAdmin = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.domainAdministrator + } elseIf($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "VIRTUAL_CENTER") { $FullClone = $true } - $sysPrepName = $jsonObject.SysPrepName + switch ($custType) { + 'SYS_PREP' { + $sysprepCustomizationSettings = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings + $sysPrepName = $sysprepCustomizationSettings.customizationSpec + $reusePreExistingAccounts = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.reusePreExistingAccounts + } + 'QUICK_PREP' { + $powerOffScriptName= $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptParameters + } + 'NONE' { + $doNotPowerOnVMsAfterCreation = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation + } + + } } $namingMethod = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod $transparentPageSharingScope = $jsonObject.AutomatedDesktopSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope @@ -3196,6 +3794,9 @@ function New-HVPool { $startInMaintenanceMode = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.SpecificNamingSpec.startMachinesInMaintenanceMode $numUnassignedMachinesKeptPoweredOn = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.SpecificNamingSpec.numUnassignedMachinesKeptPoweredOn } + $enableProvisioning = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning + $stopProvisioningOnError = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError + $minReady = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Template) { $template = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Template } @@ -3205,14 +3806,87 @@ function New-HVPool { if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Snapshot) { $snapshotVM = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Snapshot } + $dataCenter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.dataCenter $vmFolder = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.VmFolder $hostOrCluster = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.HostOrCluster $resourcePool = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ResourcePool $dataStoreList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores foreach ($dtStore in $dataStoreList) { $datastores += $dtStore.Datastore + $storageOvercommit += $dtStore.StorageOvercommit } - } elseif ($jsonObject.type -eq "MANUAL") { + $useVSan = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan + if ($LinkedClone -or $InstantClone) { + $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks + if ($useSeparateDatastoresReplicaAndOSDisks) { + $replicaDiskDatastore = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.replicaDiskDatastore + } + if ($LinkedClone) { + #For Instant clone desktops, this setting can only be set to false + $useNativeSnapshots = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.useNativeSnapshots + $reclaimVmDiskSpace = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace + if ($reclaimVmDiskSpace) { + $reclamationThresholdGB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings) { + $redirectWindowsProfile = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.RedirectWindowsProfile + if ($redirectWindowsProfile) { + $useSeparateDatastoresPersistentAndOSDisks = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.UseSeparateDatastoresPersistentAndOSDisks + } + $dataStoreList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.persistentDiskDatastores + foreach ($dtStore in $dataStoreList) { + $persistentDiskDatastores += $dtStore.Datastore + $PersistentDiskStorageOvercommit += $dtStore.StorageOvercommit + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskSizeMB) { + $diskSizeMB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskSizeMB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskDriveLetter) { + $diskDriveLetter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskDriveLetter + } + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.nonPersistentDiskSettings) { + $redirectDisposableFiles = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.RedirectDisposableFiles + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskSizeMB) { + $nonPersistentDiskSizeMB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskSizeMB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskDriveLetter) { + $nonPersistentDiskDriveLetter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskDriveLetter + } + } + } else { + $useNativeSnapshots = $false + $redirectWindowsProfile = $false + } + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings) { + $useViewStorageAccelerator = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings.UseViewStorageAccelerator + if ($useViewStorageAccelerator -and $LinkedClone) { + $viewComposerDiskTypes = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings.ViewComposerDiskTypes + } + if (! $InstantClone -and $useViewStorageAccelerator) { + $regenerateViewStorageAcceleratorDays = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings.RegenerateViewStorageAcceleratorDays + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings.blackoutTimes) { + $blackoutTimesList =$jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.viewStorageAcceleratorSettings.blackoutTimes + foreach ($blackout in $blackoutTimesList) { + $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime + $blackoutObj.Days = $blackout.Days + $blackoutObj.StartTime = $blackout.StartTime + $blackoutObj.EndTime = $blackoutObj.EndTime + $blackoutTimes += $blackoutObj + } + } + } + } + <# ToDo Nic + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.nics) { + $nicList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.nics + foreach($nicObj in $nicList) { + $nic = New-Object VMware.Hv.DesktopNetworkInterfaceCardSettings + } + } + #> + } elseIf ($jsonObject.type -eq "MANUAL") { $MANUAL = $true $poolType = 'MANUAL' $userAssignment = $jsonObject.ManualDesktopSpec.userAssignment.userAssignment @@ -3232,13 +3906,67 @@ function New-HVPool { $description = $jsonObject.base.Description $accessGroup = $jsonObject.base.AccessGroup $poolName = $jsonObject.base.name + + <# + # Populate desktop settings + #> + if ($null -ne $jsonObject.DesktopSettings) { + $Enable = $jsonObject.DesktopSettings.enabled + $deleting = $jsonObject.DesktopSettings.deleting + if ($null -ne $jsonObject.DesktopSettings.connectionServerRestrictions) { + $ConnectionServerRestrictions = $jsonObject.DesktopSettings.connectionServerRestrictions + } + if ($poolType -ne 'RDS') { + if ($null -ne $jsonObject.DesktopSettings.logoffSettings) { + $powerPolicy = $jsonObject.DesktopSettings.logoffSettings.powerPolicy + $automaticLogoffPolicy = $jsonObject.DesktopSettings.logoffSettings.automaticLogoffPolicy + if ($null -ne $jsonObject.DesktopSettings.logoffSettings.automaticLogoffMinutes) { + $automaticLogoffMinutes = $jsonObject.DesktopSettings.logoffSettings.automaticLogoffMinutes + } + $allowUsersToResetMachines = $jsonObject.DesktopSettings.logoffSettings.allowUsersToResetMachines + $allowMultipleSessionsPerUser = $jsonObject.DesktopSettings.logoffSettings.allowMultipleSessionsPerUser + $deleteOrRefreshMachineAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.deleteOrRefreshMachineAfterLogoff + $refreshOsDiskAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.refreshOsDiskAfterLogoff + $refreshPeriodDaysForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk + $refreshThresholdPercentageForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + } + + if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings) { + $supportedDisplayProtocols = $jsonObject.DesktopSettings.displayProtocolSettings.supportedDisplayProtocols + $defaultDisplayProtocol = $jsonObject.DesktopSettings.displayProtocolSettings.defaultDisplayProtocol + $allowUsersToChooseProtocol = $jsonObject.DesktopSettings.displayProtocolSettings.allowUsersToChooseProtocol + if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings) { + $renderer3D = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.renderer3D + $enableGRIDvGPUs = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.enableGRIDvGPUs + $vRamSizeMB = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB + $maxNumberOfMonitors = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxNumberOfMonitors + $maxResolutionOfAnyOneMonitor = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxResolutionOfAnyOneMonitor + } + $enableHTMLAccess = $jsonObject.DesktopSettings.displayProtocolSettings.enableHTMLAccess + } + + if ($null -ne $jsonObject.DesktopSettings.mirageConfigurationOverrides) { + $overrideGlobalSetting = $jsonObject.DesktopSettings.mirageConfigurationOverrides.overrideGlobalSetting + $enabled = $jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled + $url = $jsonObject.DesktopSettings.mirageConfigurationOverrides.url + } + } + if ($null -ne $jsonObject.DesktopSettings.flashSettings) { + $quality = $jsonObject.DesktopSettings.flashSettings.quality + $throttling = $jsonObject.DesktopSettings.flashSettings.throttling + } + #desktopsettings ends + } + if ($null -ne $jsonObject.GlobalEntitlementData) { + $globalEntitlement = $jsonObject.GlobalEntitlementData.globalEntitlement + } } if ($PSCmdlet.MyInvocation.ExpectingInput -or $clonePool) { if ($clonePool -and ($clonePool.GetType().name -eq 'DesktopSummaryView')) { $clonePool = Get-HVPool -poolName $clonePool.desktopsummarydata.name - } elseif (!($clonePool -and ($clonePool.GetType().name -eq 'DesktopInfo'))) { + } elseIf (!($clonePool -and ($clonePool.GetType().name -eq 'DesktopInfo'))) { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" return } @@ -3255,10 +3983,10 @@ function New-HVPool { $DesktopVirtualCenterProvisioningData = $DesktopVirtualCenterProvisioningSettings.VirtualCenterProvisioningData $DesktopVirtualCenterStorageSettings = $DesktopVirtualCenterProvisioningSettings.VirtualCenterStorageSettings $DesktopVirtualCenterNetworkingSettings = $DesktopVirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings - $desktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings - $desktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings + $DesktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings + $DesktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings } - elseif ($clonePool.ManualDesktopData) { + elseIf ($clonePool.ManualDesktopData) { if (! $VM) { Write-Error "ManualDesktop pool cloning requires list of machines, parameter VM is empty" break @@ -3269,7 +3997,7 @@ function New-HVPool { $desktopVirtualCenterStorageSettings = $clonePool.ManualDesktopData.viewStorageAcceleratorSettings $desktopVirtualCenterManagedCommonSettings = $clonePool.ManualDesktopData.virtualCenterManagedCommonSettings } - elseif($clonePool.RdsDesktopData) { + elseIf($clonePool.RdsDesktopData) { if (! $Farm) { Write-Error "RdsDesktop pool cloning requires farm, parameter Farm is not set" break @@ -3285,16 +4013,16 @@ function New-HVPool { $poolType = 'AUTOMATED' $provisioningType = 'INSTANT_CLONE_ENGINE' } - elseif ($LinkedClone) { + elseIf ($LinkedClone) { $poolType = 'AUTOMATED' $provisioningType = 'VIEW_COMPOSER' } - elseif ($FullClone) { + elseIf ($FullClone) { $poolType = 'AUTOMATED' $provisioningType = 'VIRTUAL_CENTER' } - elseif ($Manual) { $poolType = 'MANUAL' } - elseif ($RDS) { $poolType = 'RDS' } + elseIf ($Manual) { $poolType = 'MANUAL' } + elseIf ($RDS) { $poolType = 'RDS' } } $script:desktopSpecObj = Get-DesktopSpec -poolType $poolType -provisioningType $provisioningType -namingMethod $namingMethod @@ -3355,8 +4083,8 @@ function New-HVPool { { 'RDS' { <# - Query FarmId from Farm Name - #> + Query FarmId from Farm Name + #> $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals $QueryFilterEquals.memberName = 'data.name' $QueryFilterEquals.value = $farm @@ -3388,6 +4116,9 @@ function New-HVPool { $machineList = Get-RegisteredPhysicalMachine -services $services -machinesList $VM } $desktopSpecObj.ManualDesktopSpec.Machines = $machineList + if ($desktopUserAssignment) { + $desktopSpecObj.ManualDesktopSpec.userAssignment = $desktopUserAssignment + } } default { if (!$desktopVirtualMachineNamingSpec) { @@ -3431,7 +4162,9 @@ function New-HVPool { try { $desktopVirtualCenterProvisioningData = Get-HVPoolProvisioningData -vc $virtualCenterID -vmObject $desktopVirtualCenterProvisioningData $hostClusterId = $desktopVirtualCenterProvisioningData.HostOrCluster - $desktopVirtualCenterStorageSettings = Get-HVPoolStorageObject -hostclusterID $hostClusterId -storageObject $desktopVirtualCenterStorageSettings + $hostOrCluster_helper = New-Object VMware.Hv.HostOrClusterService + $hostClusterIds = (($hostOrCluster_helper.HostOrCluster_GetHostOrClusterTree($services, $desktopVirtualCenterProvisioningData.datacenter)).treeContainer.children.info).Id + $desktopVirtualCenterStorageSettings = Get-HVPoolStorageObject -hostClusterIds $hostClusterId -storageObject $desktopVirtualCenterStorageSettings $DesktopVirtualCenterNetworkingSettings = Get-HVPoolNetworkSetting -networkObject $DesktopVirtualCenterNetworkingSettings $desktopCustomizationSettings = Get-HVPoolCustomizationSetting -vc $virtualCenterID -customObject $desktopCustomizationSettings } catch { @@ -3440,10 +4173,10 @@ function New-HVPool { break } - if (!$DesktopVirtualCenterProvisioningSettings) { - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.enableProvisioning = $true - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $true - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = 0 + if (! $DesktopVirtualCenterProvisioningSettings) { + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.enableProvisioning = $enableProvisioning + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $stopProvisioningOnError + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = $minReady $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $desktopVirtualCenterProvisioningData $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $desktopVirtualCenterStorageSettings $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $DesktopVirtualCenterNetworkingSettings @@ -3484,8 +4217,83 @@ function New-HVPool { $desktopSpecObj.base.Description = $description $desktopSpecObj.type = $poolType - if ($desktopSettings) { $desktopSpecObj.DesktopSettings = $desktopSettings } + if (! $desktopSettings) { + $desktopSettingsService = New-Object VMware.Hv.DesktopService + $desktopSettingsHelper = $desktopSettingsService.getDesktopSettingsHelper() + $desktopSettingsHelper.setEnabled($Enable) + $desktopSettingsHelper.setConnectionServerRestrictions($ConnectionServerRestrictions) + #$desktopLogoffSettings = New-Object VMware.Hv.DesktopLogoffSettings + $desktopLogoffSettings = $desktopSettingsService.getDesktopLogoffSettingsHelper() + if ($InstantClone) { + $deleteOrRefreshMachineAfterLogoff = "DELETE" + $powerPolicy = "ALWAYS_POWERED_ON" + } + $desktopLogoffSettings.setPowerPolicy($powerPolicy) + $desktopLogoffSettings.setAutomaticLogoffPolicy($automaticLogoffPolicy) + $desktopLogoffSettings.setAutomaticLogoffMinutes($automaticLogoffMinutes) + $desktopLogoffSettings.setAllowUsersToResetMachines($allowUsersToResetMachines) + $desktopLogoffSettings.setAllowMultipleSessionsPerUser($allowMultipleSessionsPerUser) + $desktopLogoffSettings.setDeleteOrRefreshMachineAfterLogoff($deleteOrRefreshMachineAfterLogoff) + $desktopLogoffSettings.setRefreshOsDiskAfterLogoff($refreshOsDiskAfterLogoff) + $desktopLogoffSettings.setRefreshPeriodDaysForReplicaOsDisk($refreshPeriodDaysForReplicaOsDisk) + if ($refreshThresholdPercentageForReplicaOsDisk -and $refreshOsDiskAfterLogoff -eq "AT_SIZE") { + $desktopLogoffSettings.setRefreshThresholdPercentageForReplicaOsDisk($refreshThresholdPercentageForReplicaOsDisk) + } + if ($poolType -ne 'RDS') { + $desktopSettingsHelper.setLogoffSettings($desktopLogoffSettings.getDataObject()) + + $desktopDisplayProtocolSettings = $desktopSettingsService.getDesktopDisplayProtocolSettingsHelper() + #setSupportedDisplayProtocols is not exists, because this property cannot be updated. + $desktopDisplayProtocolSettings.getDataObject().SupportedDisplayProtocols = $supportedDisplayProtocols + $desktopDisplayProtocolSettings.setDefaultDisplayProtocol($defaultDisplayProtocol) + $desktopDisplayProtocolSettings.setEnableHTMLAccess($enableHTMLAccess) + $desktopDisplayProtocolSettings.setAllowUsersToChooseProtocol($allowUsersToChooseProtocol) + + $desktopPCoIPDisplaySettings = $desktopSettingsService.getDesktopPCoIPDisplaySettingsHelper() + $desktopPCoIPDisplaySettings.setRenderer3D($renderer3D) + #setEnableGRIDvGPUs is not exists, because this property cannot be updated. + $desktopPCoIPDisplaySettings.getDataObject().EnableGRIDvGPUs = $enableGRIDvGPUs + $desktopPCoIPDisplaySettings.setVRamSizeMB($vRamSizeMB) + $desktopPCoIPDisplaySettings.setMaxNumberOfMonitors($maxNumberOfMonitors) + $desktopPCoIPDisplaySettings.setMaxResolutionOfAnyOneMonitor($maxResolutionOfAnyOneMonitor) + $desktopDisplayProtocolSettings.setPcoipDisplaySettings($desktopPCoIPDisplaySettings.getDataObject()) + $desktopSettingsHelper.setDisplayProtocolSettings($desktopDisplayProtocolSettings.getDataObject()) + + $desktopMirageConfigOverrides = $desktopSettingsService.getDesktopMirageConfigurationOverridesHelper() + $desktopMirageConfigOverrides.setEnabled($enabled) + $desktopMirageConfigOverrides.setOverrideGlobalSetting($overrideGlobalSetting) + $desktopMirageConfigOverrides.setUrl($url) + $desktopSettingsHelper.setMirageConfigurationOverrides($desktopMirageConfigOverrides.getDataObject()) + $desktopSettings = $desktopSettingsHelper.getDataObject() + } + $desktopFlashSettings = $desktopSettingsService.getDesktopAdobeFlashSettingsHelper() + $desktopFlashSettings.setQuality($quality) + $desktopFlashSettings.setThrottling($throttling) + $desktopSettingsHelper.setFlashSettings($desktopFlashSettings.getDataObject()) + } + + $desktopSpecObj.DesktopSettings = $desktopSettings + + if ($globalEntitlement) { + $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals + $QueryFilterEquals.memberName = 'base.displayName' + $QueryFilterEquals.value = $globalEntitlement + $defn = New-Object VMware.Hv.QueryDefinition + $defn.queryEntityType = 'GlobalEntitlementSummaryView' + $defn.Filter = $QueryFilterEquals + $query_service_helper = New-Object VMware.Hv.QueryServiceService + try { + $queryResults = $query_service_helper.QueryService_Query($services,$defn) + $globalEntitlementid = $queryResults.Results.id + if ($globalEntitlementid.length -eq 1) { + $desktopGlobalEntitlementData = New-Object VMware.Hv.DesktopGlobalEntitlementData -Property @{'globalEntitlement'= $globalEntitlementid;} + } + } + catch { + Write-Host "GlobalEntitlement " $_ + } + } if ($desktopAutomatedDesktopSpec) { $desktopSpecObj.AutomatedDesktopSpec = $desktopAutomatedDesktopSpec } @@ -3495,11 +4303,11 @@ function New-HVPool { # Please uncomment below code, if you want save desktopSpec object to json file <# - $myDebug = convertto-json -InputObject $desktopSpecObj -depth 12 - $myDebug | out-file -filepath c:\temp\copieddesktop.json - #> + $myDebug = convertto-json -InputObject $desktopSpecObj -depth 12 + $myDebug | out-file -filepath c:\temp\copieddesktop.json + #> $desktop_helper = New-Object VMware.Hv.DesktopService - if ($pscmdlet.ShouldProcess($desktopSpecObj)) { + if ($pscmdlet.ShouldProcess($desktopSpecObj.base.name)) { $id = $desktop_helper.Desktop_create($services,$desktopSpecObj) } return $desktopSpecObj @@ -3509,7 +4317,6 @@ function New-HVPool { $desktopSpecObj = $null [System.gc]::collect() } - } function Get-HVPoolProvisioningData { @@ -3530,6 +4337,12 @@ function Get-HVPoolProvisioningData { } $vmObject.Template = $templateVM.id $dataCenterID = $templateVM.datacenter + if ($dataCenter -and $dataCenterID) { + $VmTemplateInfo = $vm_template_helper.VmTemplate_ListByDatacenter($dataCenterID) + if (! ($VmTemplateInfo.Path -like "/$dataCenter/*")) { + throw "$template not exists in datacenter: [$dataCenter]" + } + } $vmObject.datacenter = $dataCenterID } if ($parentVM) { @@ -3541,6 +4354,12 @@ function Get-HVPoolProvisioningData { } $vmObject.ParentVm = $parentVmObj.id $dataCenterID = $parentVmObj.datacenter + if ($dataCenter -and $dataCenterID) { + $baseImageVmInfo = $base_imageVm_helper.BaseImageVm_ListByDatacenter($services,$dataCenterID) + if (! ($baseImageVmInfo.Path -like "/$dataCenter/*")) { + throw "$parentVM not exists in datacenter: [$dataCenter]" + } + } $vmObject.datacenter = $dataCenterID } if ($snapshotVM) { @@ -3559,7 +4378,9 @@ function Get-HVPoolProvisioningData { $folderList += $folders while ($folderList.Length -gt 0) { $item = $folderList[0] - if ($item -and !$_.folderdata.incompatiblereasons.inuse -and !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and (($item.folderdata.path -eq $vmFolder) -or ($item.folderdata.name -eq $vmFolder))) { + if ($item -and !$_.folderdata.incompatiblereasons.inuse -and ` + !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and ` + (($item.folderdata.path -eq $vmFolder) -or ($item.folderdata.name -eq $vmFolder))) { $vmObject.VmFolder = $item.id break } @@ -3595,26 +4416,54 @@ function Get-HVPoolProvisioningData { function Get-HVPoolStorageObject { param( - [Parameter(Mandatory = $false)] - [VMware.Hv.DesktopVirtualCenterStorageSettings]$StorageObject, - [Parameter(Mandatory = $true)] - [VMware.Hv.HostOrClusterId]$HostClusterID + [VMware.Hv.HostOrClusterId[]]$HostClusterIDs, + + [Parameter(Mandatory = $false)] + [VMware.Hv.DesktopVirtualCenterStorageSettings]$StorageObject ) + $datastoreList = $null if (!$storageObject) { + $datastore_helper = New-Object VMware.Hv.DatastoreService + foreach ($hostClusterID in $hostClusterIDs){ + $datastoreList += $datastore_helper.Datastore_ListDatastoresByHostOrCluster($services,$hostClusterID) + } $storageObject = New-Object VMware.Hv.DesktopVirtualCenterStorageSettings $storageAcceleratorList = @{ - 'useViewStorageAccelerator' = $false + 'useViewStorageAccelerator' = $useViewStorageAccelerator } $desktopViewStorageAcceleratorSettings = New-Object VMware.Hv.DesktopViewStorageAcceleratorSettings -Property $storageAcceleratorList $storageObject.viewStorageAcceleratorSettings = $desktopViewStorageAcceleratorSettings - $desktopSpaceReclamationSettings = New-Object VMware.Hv.DesktopSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $false } + $desktopSpaceReclamationSettings = New-Object VMware.Hv.DesktopSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $reclaimVmDiskSpace; 'reclamationThresholdGB' = $reclamationThresholdGB} $desktopPersistentDiskSettings = New-Object VMware.Hv.DesktopPersistentDiskSettings -Property @{ 'redirectWindowsProfile' = $false } $desktopNonPersistentDiskSettings = New-Object VMware.Hv.DesktopNonPersistentDiskSettings -Property @{ 'redirectDisposableFiles' = $false } + if ($LinkedClone) { + if ($blackoutTimes) { + $storageObject.viewStorageAcceleratorSettings.BlackoutTimes = $blackoutTimes + } + if ($useViewStorageAccelerator) { + $storageObject.viewStorageAcceleratorSettings.ViewComposerDiskTypes = $viewComposerDiskTypes + $storageObject.viewStorageAcceleratorSettings.RegenerateViewStorageAcceleratorDays = $regenerateViewStorageAcceleratorDays + } + $desktopPersistentDiskSettings.RedirectWindowsProfile = $redirectWindowsProfile + if ($redirectWindowsProfile) { + $desktopPersistentDiskSettings.UseSeparateDatastoresPersistentAndOSDisks = $useSeparateDatastoresPersistentAndOSDisks + $desktopPersistentDiskSettings.DiskSizeMB = $diskSizeMB + $desktopPersistentDiskSettings.DiskDriveLetter = $diskDriveLetter + } + if ($useSeparateDatastoresPersistentAndOSDisks) { + if ($persistentDiskStorageOvercommit -and ($persistentDiskDatastores.Length -ne $persistentDiskStorageOvercommit.Length) ) { + throw "Parameters persistentDiskDatastores length: [$persistentDiskDatastores.Length] and persistentDiskStorageOvercommit length: [$persistentDiskStorageOvercommit.Length] should be of same size" + } + $desktopPersistentDiskSettings.PersistentDiskDatastores = Get_Datastore -DatastoreInfoList $datastoreList -DatastoreNames $PersistentDiskDatastores -DsStorageOvercommit $persistentDiskStorageOvercommit + } + $desktopNonPersistentDiskSettings.RedirectDisposableFiles = $redirectDisposableFiles + $desktopNonPersistentDiskSettings.DiskSizeMB = $nonPersistentDiskSizeMB + $desktopNonPersistentDiskSettings.DiskDriveLetter = $nonPersistentDiskDriveLetter + } $desktopViewComposerStorageSettingsList = @{ - 'useSeparateDatastoresReplicaAndOSDisks' = $false; - 'useNativeSnapshots' = $false; + 'useNativeSnapshots' = $useNativeSnapshots; 'spaceReclamationSettings' = $desktopSpaceReclamationSettings; 'persistentDiskSettings' = $desktopPersistentDiskSettings; 'nonPersistentDiskSettings' = $desktopNonPersistentDiskSettings @@ -3624,17 +4473,13 @@ function Get-HVPoolStorageObject { } } if ($datastores) { - $datastore_helper = New-Object VMware.Hv.DatastoreService - $datastoreList = $datastore_helper.Datastore_ListDatastoresByHostOrCluster($services,$hostClusterID) - $datastoresSelected = @() - foreach ($ds in $datastores) { - $datastoresSelected += ($datastoreList | Where-Object { ($_.DatastoreData.Path -eq $ds) -or ($_.datastoredata.name -eq $ds) }).id + if ($StorageOvercommit -and ($datastores.Length -ne $StorageOvercommit.Length) ) { + throw "Parameters datastores length: [$datastores.Length] and StorageOvercommit length: [$StorageOvercommit.Length] should be of same size" } - foreach ($ds in $datastoresSelected) { - $myDatastores = New-Object VMware.Hv.DesktopVirtualCenterDatastoreSettings - $myDatastores.Datastore = $ds - $mydatastores.StorageOvercommit = 'UNBOUNDED' - $storageObject.Datastores += $myDatastores + $storageObject.Datastores = Get-HVDatastore -DatastoreInfoList $datastoreList -DatastoreNames $datastores -DsStorageOvercommit $StorageOvercommit + if ($useSeparateDatastoresReplicaAndOSDisks) { + $storageObject.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks = $UseSeparateDatastoresReplicaAndOSDisks + $storageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = ($datastoreInfoList | Where-Object { ($_.datastoredata.name -eq $replicaDiskDatastore) -or ($_.datastoredata.path -eq $replicaDiskDatastore)}).id } } if ($storageObject.Datastores.Count -eq 0) { @@ -3644,6 +4489,40 @@ function Get-HVPoolStorageObject { return $storageObject } +function Get-HVDatastore { + param( + [Parameter(Mandatory = $true)] + [VMware.Hv.DatastoreInfo[]] + $DatastoreInfoList, + + [Parameter(Mandatory = $true)] + [string[]] + $DatastoreNames, + + [Parameter(Mandatory = $false)] + [string[]] + $DsStorageOvercommit + + ) + $datastoresSelected = @() + foreach ($ds in $datastoreNames) { + $datastoresSelected += ($datastoreInfoList | Where-Object { ($_.DatastoreData.Path -eq $ds) -or ($_.datastoredata.name -eq $ds) }).id + } + $Datastores = $null + if (! $DsStorageOvercommit) { + $DsStorageOvercommit += 'UNBOUNDED' + } + $StorageOvercommitCnt = 0 + foreach ($ds in $datastoresSelected) { + $myDatastores = New-Object VMware.Hv.DesktopVirtualCenterDatastoreSettings + $myDatastores.Datastore = $ds + $mydatastores.StorageOvercommit = $DsStorageOvercommit[$StorageOvercommitCnt] + $Datastores += $myDatastores + $StorageOvercommitCnt++ + } + return $Datastores +} + function Get-HVPoolNetworkSetting { param( [Parameter(Mandatory = $false)] @@ -3684,7 +4563,7 @@ function Get-HVPoolCustomizationSetting { $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) | Where-Object { $_.namesData.dnsName -match $netBiosName }) if (![string]::IsNullOrWhitespace($domainAdmin)) { $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id - } else { + } elseIf ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id } if ($null -eq $instantCloneEngineDomainAdministrator) { @@ -3699,7 +4578,7 @@ function Get-HVPoolCustomizationSetting { $ViewComposerDomainAdministratorID = ($viewComposerDomainAdministrator_helper.ViewComposerDomainAdministrator_List($services,$vcID) | Where-Object { $_.base.domain -match $netBiosName }) if (![string]::IsNullOrWhitespace($domainAdmin)) { $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } else { + } elseIf ($null -ne $ViewComposerDomainAdministratorID) { $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id } if ($null -eq $ViewComposerDomainAdministratorID) { @@ -3716,14 +4595,15 @@ function Get-HVPoolCustomizationSetting { throw "No Sysprep Customization Spec found with Name: [$sysPrepName]" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - } elseif ($custType -eq 'QUICK_PREP') { + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts + } elseIf ($custType -eq 'QUICK_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'QUICK_PREP' $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings = Get-CustomizationObject } else { throw "The customization type: [$custType] is not supported for LinkedClone Pool" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID - } elseif ($FullClone) { + } elseIf ($FullClone) { if ($custType -eq 'SYS_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings = Get-CustomizationObject @@ -3734,7 +4614,7 @@ function Get-HVPoolCustomizationSetting { throw "No Sysprep Customization Spec found with Name: [$sysPrepName]" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - } elseif ($custType -eq 'NONE') { + } elseIf ($custType -eq 'NONE') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings = Get-CustomizationObject $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation = $false $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = "NONE" @@ -3751,7 +4631,7 @@ function Get-HVPoolCustomizationSetting { function Get-CustomizationObject { if ($InstantClone) { return New-Object VMware.Hv.DesktopCloneprepCustomizationSettings - } elseif ($LinkedClone) { + } elseIf ($LinkedClone) { if ($custType -eq 'QUICK_PREP') { return New-Object VMware.Hv.DesktopQuickPrepCustomizationSettings } else { @@ -3790,7 +4670,7 @@ function Get-DesktopSpec { if ($provisioningType -ne 'VIRTUAL_CENTER') { $desktop_spec_helper.getDataObject().AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings = $desktop_helper.getDesktopViewComposerStorageSettingsHelper().getDataObject() } - } elseif ($poolType -eq 'MANUAL') { + } elseIf ($poolType -eq 'MANUAL') { $desktop_spec_helper.getDataObject().ManualDesktopSpec.userAssignment = $desktop_helper.getDesktopUserAssignmentHelper().getDataObject() $desktop_spec_helper.getDataObject().ManualDesktopSpec.viewStorageAcceleratorSettings = $desktop_helper.getDesktopViewStorageAcceleratorSettingsHelper().getDataObject() $desktop_spec_helper.getDataObject().ManualDesktopSpec.virtualCenterManagedCommonSettings = $desktop_helper.getDesktopVirtualCenterManagedCommonSettingsHelper().getDataObject() @@ -3801,6 +4681,119 @@ function Get-DesktopSpec { } +function Test-HVDesktopSpec { + param( + [Parameter(Mandatory = $true)] + $JsonObject + ) + if ($null -eq $jsonObject.type) { + Throw "Pool type is empty, need to be configure in json file" + } + if ($null -eq $jsonObject.Base.Name) { + Throw "Pool name is empty, need to be configure in json file" + } + if ($null -eq $jsonObject.Base.AccessGroup) { + Throw "AccessGroup of pool is empty, need to be configure in json file" + } + if ($jsonObject.type -eq "AUTOMATED") { + if (! (($jsonObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "FLOATING") -or ($jsonObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "DEDICATED")) ) { + Throw "UserAssignment must be FLOATING or DEDICATED" + } + if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq $null) { + Throw "Pool Provisioning type is empty, need to be configure in json file" + } + $provisionTypeArray = @('VIRTUAL_CENTER', 'VIEW_COMPOSER', 'INSTANT_CLONE_ENGINE') + if (! ($provisionTypeArray -contains $jsonObject.AutomatedDesktopSpec.provisioningType)) { + Throw "ProvisioningType of pool is invalid" + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning) { + Throw "Whether to enable provisioning immediately or not, need to configure in json file" + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError) { + Throw "Whether to stop provisioning immediately or not on error, need to configure in json file" + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + Throw "Determines how the VMs in the desktop are named, need to configure in json file" + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + $namingMethodArray = @('PATTERN','SPECIFIED') + if (! ($namingMethodArray -contains $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod)) { + Throw "NamingMethod property must to be one of these SPECIFIED or PATTERN" + } + if (($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings) -and ($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.specificNamingSpec)) { + Throw "Naming pattern (or) Specified name settings need to be configure in json file" + } + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan) { + Throw "Must specify whether to use virtual SAN or not" + } + $jsonTemplate = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + if (! (($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) ))) { + Throw "Must specify Template or (ParentVm and Snapshot) names in json file" + } + if ($null -eq $jsonVmFolder) { + Throw "Must specify VM folder in json file to deploy the VMs" + } + if ($null -eq $jsonHostOrCluster) { + Throw "Must specify Host or cluster in json file to deploy the VMs" + } + if ($null -eq $resourcePool) { + Throw "Must specify Resource pool in json file to deploy the VMs" + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names in json file" + } + if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterManagedCommonSettings.transparentPageSharingScope) { + Throw "Must specify transparent page sharing scope in json file" + } + $jsonCustomizationType = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType + switch ($jsonCustomizationType) { + "NONE" { + if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.noCustomizationSettings) { + Throw "Specify noCustomization Settings in json file" + } + } + "QUICK_PREP" { + if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.quickprepCustomizationSettings) { + Throw "Specify quickprep CustomizationSettings in json file" + } + } + "SYS_PREP" { + if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.sysprepCustomizationSettings) { + Throw "Specify sysprep CustomizationSettings in json file" + } + } + "CLONE_PREP" { + if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.cloneprepCustomizationSettings) { + Throw "Specify ClonePrep CustomizationSettings in json file" + } + } + } + } elseIf ($jsonObject.Type -eq "MANUAL") { + $jsonUserAssignment = $jsonObject.ManualDesktopSpec.UserAssignment.UserAssignment + if (! (($jsonUserAssignment -eq "FLOATING") -or ($jsonUserAssignment -eq "DEDICATED")) ) { + Throw "UserAssignment must be FLOATING or DEDICATED" + } + $jsonSource = @('VIRTUAL_CENTER','UNMANAGED') + if (! ($jsonSource -contains $jsonObject.ManualDesktopSpec.Source)) { + Throw "The Source of machines must be VIRTUAL_CENTER or UNMANAGED" + } + if ($null -eq $jsonObject.ManualDesktopSpec.Machines) { + Throw "Specify list of machines to add to this desktop during creation" + } + } + elseIf ($jsonObject.type -eq "RDS") { + if ($null -eq $jsonObject.RdsDesktopSpec.Farm) { + Throw "Specify Farm needed to create RDS Desktop" + } + } +} + function Remove-HVFarm { <# .SYNOPSIS @@ -3900,7 +4893,7 @@ function Remove-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList) { - if ($pscmdlet.ShouldProcess($item)) { + if ($pscmdlet.ShouldProcess($item.Name)) { $farm_service_helper.Farm_Delete($services, $item.id) } Write-Host "Farm Deleted: " $item.Name @@ -4044,7 +5037,7 @@ function Remove-HVPool { } } Write-Host "Deleting Pool: " $item.Name - if ($pscmdlet.ShouldProcess($deleteSpec)) { + if ($pscmdlet.ShouldProcess($item.Name)) { $desktop_service_helper.Desktop_Delete($services,$item.id,$deleteSpec) } } @@ -4177,7 +5170,7 @@ function Set-HVFarm { try { $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($farmSpecObj) { @@ -4651,7 +5644,7 @@ function Start-HVFarm { $updates = @() $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_Update($services,$item,$updates) $farm_service_helper.Farm_Recompose($services,$item,$spec) } @@ -4939,20 +5932,20 @@ function Start-HVPool { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRebalanceSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Rebalance($services,$item,$spec) } - Write-Host "Performed rebalance task on Pool: $PoolList.item" + Write-Host "Performed rebalance task on Pool: $PoolList.$item" } } 'REFRESH' { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRefreshSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Refresh($services,$item,$spec) } - Write-Host "Performed refresh task on Pool: $PoolList.item" + Write-Host "Performed refresh task on Pool: $PoolList.$item" } } 'RECOMPOSE' { @@ -4968,10 +5961,10 @@ function Start-HVPool { $updates = @() $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } - Write-Host "Performed recompose task on Pool: $PoolList.item" + Write-Host "Performed recompose task on Pool: $PoolList.$item" } } 'PUSH_IMAGE' { @@ -4986,10 +5979,10 @@ function Start-HVPool { $spec.Settings.LogoffSetting = $logoffSetting $spec.Settings.StopOnFirstError = $stopOnFirstError if ($startTime) { $spec.Settings.startTime = $startTime } - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) } - Write-Host "Performed push_image task on Pool: $PoolList.item" + Write-Host "Performed push_image task on Pool: $PoolList.$item" } } 'CANCEL_PUSH_IMAGE' { @@ -4997,10 +5990,10 @@ function Start-HVPool { Write-Error "$poolList.$item is not a INSTANT CLONE pool" break } else { - if ($pscmdlet.ShouldProcess($spec)) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) } - Write-Host "Performed cancel_push_image task on Pool: $PoolList.item" + Write-Host "Performed cancel_push_image task on Pool: $PoolList.$item" } } } From a30d1449742c2fbb8361b7d9e02de0f3e97f3daf Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Mon, 9 Jan 2017 21:11:54 +0530 Subject: [PATCH 22/90] Enhancements to examples, validation and netBiosName 1) Renamed Get-HVDesktopSpec to Get-HVPoolSpec. 2) Enhancements to examples 3) Enhancements to validation for farm and pool 4) ADDomainId handling for farm and pool --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 333 ++++++++++-------- 1 file changed, 184 insertions(+), 149 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 2a0967b..d388441 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -176,7 +176,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A .EXAMPLE Add managed manual VMs to existing manual pool - Add-HVDesktop -PoolName 'ManualPool' -Machines 'manualPool1', 'manualPool2'. + Add-HVDesktop -PoolName 'ManualPool' -Machines 'manualPool1', 'manualPool2' -Confirm:$false .EXAMPLE Add virtual machines to automated specific named dedicated pool @@ -368,7 +368,7 @@ function Add-HVRDSServer { .EXAMPLE Add RDSServers to manual farm - Add-HVRDSServer -Farm "manualFarmTest" -RdsServers "vm-for-rds","vm-for-rds-2" + Add-HVRDSServer -Farm "manualFarmTest" -RdsServers "vm-for-rds","vm-for-rds-2" -Confirm:$false .OUTPUTS None @@ -1915,11 +1915,11 @@ function New-HVFarm { .EXAMPLE Creates new linkedClone farm by using json file - New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json + New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false .EXAMPLE Creates new manual farm by using rdsServers names - New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" + New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" -Confirm:$false .OUTPUTS None @@ -2260,7 +2260,7 @@ function New-HVFarm { break } try { - Test-HVFarmSpec -JsonObject $jsonObject + Test-HVFarmSpec -PoolObject $jsonObject } catch { Write-Error "Json object validation failed, $_" break @@ -2522,7 +2522,14 @@ function New-HVFarm { if ($pscmdlet.ShouldProcess($farmSpecObj.data.name)) { $Id = $farm_service_helper.Farm_Create($services, $farmSpecObj) - } + } else { + try { + Test-HVFarmSpec -PoolObject $farmSpecObj + } catch { + Write-Error "FarmSpec object validation failed, $_" + break + } + } return $farmSpecObj } @@ -2535,77 +2542,77 @@ function New-HVFarm { function Test-HVFarmSpec { param( [Parameter(Mandatory = $true)] - $JsonObject + $PoolObject ) - if ($null -eq $jsonObject.Type) { - Throw "Specify type of farm in json file" + if ($null -eq $PoolObject.Type) { + Throw "Specify type of farm" } $jsonFarmTypeArray = @('AUTOMATED','MANUAL') - if (! ($jsonFarmTypeArray -contains $jsonObject.Type)) { + if (! ($jsonFarmTypeArray -contains $PoolObject.Type)) { Throw "Farm type must be AUTOMATED or MANUAL" } - if ($null -eq $jsonObject.Data.Name) { - Throw "Specify farm name in json file" + if ($null -eq $PoolObject.Data.Name) { + Throw "Specify farm name" } - if ($null -eq $jsonObject.Data.AccessGroup) { - Throw "Specify horizon access group in json file" + if ($null -eq $PoolObject.Data.AccessGroup) { + Throw "Specify horizon access group" } - if ($jsonObject.Type -eq "AUTOMATED"){ - $jsonProvisioningType = $jsonObject.AutomatedFarmSpec.ProvisioningType + if ($PoolObject.Type -eq "AUTOMATED"){ + $jsonProvisioningType = $PoolObject.AutomatedFarmSpec.ProvisioningType if ($null -eq $jsonProvisioningType) { - Throw "Must specify provisioningType in json file" + Throw "Must specify provisioningType" } - if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.namingMethod) { - Throw "Must specify naming method to PATTERN in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.namingMethod) { + Throw "Must specify naming method to PATTERN" } - if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings) { - Throw "Specify Naming pattern settings in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings) { + Throw "Specify Naming pattern settings" } - if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern) { - Throw "Specify specified naming pattern in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern) { + Throw "Specify specified naming pattern" } - if ($null -eq $jsonObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.enableProvisioning) { - Throw "Specify Whether to enable provisioning in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.enableProvisioning) { + Throw "Specify Whether to enable provisioning or not" } - if ($null -eq $jsonObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.stopProvisioningOnError) { - Throw "Specify Whether provisioning on all VMs stops on error in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.stopProvisioningOnError) { + Throw "Specify Whether provisioning on all VMs stops on error" } - $jsonTemplate = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template - $jsonParentVm = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm - $jsonSnapshot = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot - $jsonVmFolder = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder - $jsonHostOrCluster = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster - $ResourcePool = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + $jsonTemplate = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool if (!( ($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) )) ) { - Throw "Must specify Template or (ParentVm and Snapshot) names in json file" + Throw "Must specify Template or (ParentVm and Snapshot) names" } if ($null -eq $jsonVmFolder) { - Throw "Must specify VM folder in json file to deploy the VMs" + Throw "Must specify VM folder to deploy the VMs" } if ($null -eq $jsonHostOrCluster) { - Throw "Must specify Host or cluster in json file to deploy the VMs" + Throw "Must specify Host or cluster to deploy the VMs" } if ($null -eq $resourcePool) { - Throw "Must specify Resource pool in json file to deploy the VMs" + Throw "Must specify Resource pool to deploy the VMs" } - if ($null -eq $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { - Throw "Must specify datastores names in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names" } - if ($null -eq $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.useVSan) { + if ($null -eq $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.useVSan) { Throw "Must specify whether to use virtual SAN or not" } - if ($null -eq $jsonObject.AutomatedFarmSpec.CustomizationSettings.customizationType) { - Throw "Specify Type of customization to use in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.customizationType) { + Throw "Specify customization type" } - if ($null -eq $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { - Throw "Specify Sysprep customization settings in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { + Throw "Specify sysPrep customization settings" } - if ($null -eq $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType) { - Throw "Specify MaxSessionsType in json file" + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType) { + Throw "Specify MaxSessionsType" } - } elseif ($jsonObject.Type -eq "MANUAL") { - if ($null -eq $jsonObject.manualFarmSpec.rdsServers) { - Throw "Specify rdsServers name in json file" + } elseif ($PoolObject.Type -eq "MANUAL") { + if ($null -eq $PoolObject.manualFarmSpec.rdsServers) { + Throw "Specify rdsServers name" } } } @@ -2788,9 +2795,17 @@ function Get-HVFarmCustomizationSetting { throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" } $ADDomain_service_helper = New-Object VMware.Hv.ADDomainService - $adDomianId = ($ADDomain_service_helper.ADDomain_List($services) | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain found with netBiosName: [$netBiosName]" + $ADDomains = $ADDomain_service_helper.ADDomain_List($services) + if ($netBiosName) { + $adDomianId = ( $ADDomains| Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain found with netBiosName: [$netBiosName]" + } + } else { + $adDomianId = ( $ADDomains[0] | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain configured in view administrator UI" + } } $ad_containder_service_helper = New-Object VMware.Hv.AdContainerService $adContainerId = ($ad_containder_service_helper.ADContainer_ListByDomain($services, $adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id @@ -3055,7 +3070,7 @@ function New-HVPool { .EXAMPLE Create new automated linked clone pool by using JSON spec file - New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json + New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false .EXAMPLE Clones new pool by using existing pool configuration @@ -3729,7 +3744,8 @@ function New-HVPool { } try { - Test-HVDesktopSpec -JsonObject $jsonObject + #Json object validation + Test-HVPoolSpec -PoolObject $jsonObject } catch { Write-Error "Json object validation failed, $_" break @@ -3765,21 +3781,20 @@ function New-HVPool { $FullClone = $true } switch ($custType) { - 'SYS_PREP' { - $sysprepCustomizationSettings = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings - $sysPrepName = $sysprepCustomizationSettings.customizationSpec - $reusePreExistingAccounts = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.reusePreExistingAccounts - } - 'QUICK_PREP' { - $powerOffScriptName= $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName - $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptParameters - $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptName - $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptParameters - } - 'NONE' { - $doNotPowerOnVMsAfterCreation = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation - } - + 'SYS_PREP' { + $sysprepCustomizationSettings = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings + $sysPrepName = $sysprepCustomizationSettings.customizationSpec + $reusePreExistingAccounts = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.reusePreExistingAccounts + } + 'QUICK_PREP' { + $powerOffScriptName= $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptParameters + } + 'NONE' { + $doNotPowerOnVMsAfterCreation = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation + } } } $namingMethod = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod @@ -3927,8 +3942,12 @@ function New-HVPool { $allowMultipleSessionsPerUser = $jsonObject.DesktopSettings.logoffSettings.allowMultipleSessionsPerUser $deleteOrRefreshMachineAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.deleteOrRefreshMachineAfterLogoff $refreshOsDiskAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.refreshOsDiskAfterLogoff - $refreshPeriodDaysForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk - $refreshThresholdPercentageForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + if ($jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk) { + $refreshPeriodDaysForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk + } + if ($jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk) { + $refreshThresholdPercentageForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + } } if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings) { @@ -3938,7 +3957,9 @@ function New-HVPool { if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings) { $renderer3D = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.renderer3D $enableGRIDvGPUs = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.enableGRIDvGPUs - $vRamSizeMB = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB + if ($jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB) { + $vRamSizeMB = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB + } $maxNumberOfMonitors = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxNumberOfMonitors $maxResolutionOfAnyOneMonitor = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxResolutionOfAnyOneMonitor } @@ -3947,8 +3968,12 @@ function New-HVPool { if ($null -ne $jsonObject.DesktopSettings.mirageConfigurationOverrides) { $overrideGlobalSetting = $jsonObject.DesktopSettings.mirageConfigurationOverrides.overrideGlobalSetting - $enabled = $jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled - $url = $jsonObject.DesktopSettings.mirageConfigurationOverrides.url + if ($jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled) { + $enabled = $jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled + } + if ($jsonObject.DesktopSettings.mirageConfigurationOverrides.url) { + $url = $jsonObject.DesktopSettings.mirageConfigurationOverrides.url + } } } if ($null -ne $jsonObject.DesktopSettings.flashSettings) { @@ -4309,7 +4334,15 @@ function New-HVPool { $desktop_helper = New-Object VMware.Hv.DesktopService if ($pscmdlet.ShouldProcess($desktopSpecObj.base.name)) { $id = $desktop_helper.Desktop_create($services,$desktopSpecObj) - } + } else { + try { + #DesktopSpec validation + Test-HVPoolSpec -PoolObject $desktopSpecObj + } catch { + Write-Error "DesktopSpec object validation failed, $_" + break + } + } return $desktopSpecObj } @@ -4354,12 +4387,6 @@ function Get-HVPoolProvisioningData { } $vmObject.ParentVm = $parentVmObj.id $dataCenterID = $parentVmObj.datacenter - if ($dataCenter -and $dataCenterID) { - $baseImageVmInfo = $base_imageVm_helper.BaseImageVm_ListByDatacenter($services,$dataCenterID) - if (! ($baseImageVmInfo.Path -like "/$dataCenter/*")) { - throw "$parentVM not exists in datacenter: [$dataCenter]" - } - } $vmObject.datacenter = $dataCenterID } if ($snapshotVM) { @@ -4546,9 +4573,17 @@ function Get-HVPoolCustomizationSetting { # View Composer and Instant Clone Engine Active Directory container for QuickPrep and ClonePrep. This must be set for Instant Clone Engine or SVI sourced desktops. if ($InstantClone -or $LinkedClone) { $ad_domain_helper = New-Object VMware.Hv.ADDomainService - $adDomianId = ($ad_domain_helper.ADDomain_List($services) | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain found with netBiosName: [$netBiosName]" + $ADDomains = $ad_domain_helper.ADDomain_List($services) + if ($netBiosName) { + $adDomianId = ($ADDomains | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain found with netBiosName: [$netBiosName]" + } + } else { + $adDomianId = ($ADDomains[0] | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain configured in view administrator UI" + } } $ad_container_helper = New-Object VMware.Hv.AdContainerService $adContainerId = ($ad_container_helper.ADContainer_ListByDomain($services,$adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id @@ -4681,115 +4716,115 @@ function Get-DesktopSpec { } -function Test-HVDesktopSpec { +function Test-HVPoolSpec { param( [Parameter(Mandatory = $true)] - $JsonObject + $PoolObject ) - if ($null -eq $jsonObject.type) { - Throw "Pool type is empty, need to be configure in json file" + if ($null -eq $PoolObject.type) { + Throw "Pool type is empty, need to be configured" } - if ($null -eq $jsonObject.Base.Name) { - Throw "Pool name is empty, need to be configure in json file" + if ($null -eq $PoolObject.Base.Name) { + Throw "Pool name is empty, need to be configured" } - if ($null -eq $jsonObject.Base.AccessGroup) { - Throw "AccessGroup of pool is empty, need to be configure in json file" + if ($null -eq $PoolObject.Base.AccessGroup) { + Throw "AccessGroup of pool is empty, need to be configured" } - if ($jsonObject.type -eq "AUTOMATED") { - if (! (($jsonObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "FLOATING") -or ($jsonObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "DEDICATED")) ) { + if ($PoolObject.type -eq "AUTOMATED") { + if (! (($PoolObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "FLOATING") -or ($PoolObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "DEDICATED")) ) { Throw "UserAssignment must be FLOATING or DEDICATED" } - if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq $null) { - Throw "Pool Provisioning type is empty, need to be configure in json file" + if ($PoolObject.AutomatedDesktopSpec.ProvisioningType -eq $null) { + Throw "Pool Provisioning type is empty, need to be configured" } $provisionTypeArray = @('VIRTUAL_CENTER', 'VIEW_COMPOSER', 'INSTANT_CLONE_ENGINE') - if (! ($provisionTypeArray -contains $jsonObject.AutomatedDesktopSpec.provisioningType)) { + if (! ($provisionTypeArray -contains $PoolObject.AutomatedDesktopSpec.provisioningType)) { Throw "ProvisioningType of pool is invalid" } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning) { - Throw "Whether to enable provisioning immediately or not, need to configure in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning) { + Throw "Whether to enable provisioning immediately or not, need to be configured" } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError) { - Throw "Whether to stop provisioning immediately or not on error, need to configure in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError) { + Throw "Whether to stop provisioning immediately or not on error, need to be configured" } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { - Throw "Determines how the VMs in the desktop are named, need to configure in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + Throw "Determines how the VMs in the desktop are named, need to be configured" } - if ($null -ne $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + if ($null -ne $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { $namingMethodArray = @('PATTERN','SPECIFIED') - if (! ($namingMethodArray -contains $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod)) { + if (! ($namingMethodArray -contains $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod)) { Throw "NamingMethod property must to be one of these SPECIFIED or PATTERN" } - if (($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings) -and ($null -eq $jsonObject.AutomatedDesktopSpec.VmNamingSpec.specificNamingSpec)) { - Throw "Naming pattern (or) Specified name settings need to be configure in json file" + if (($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings) -and ($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.specificNamingSpec)) { + Throw "Naming pattern (or) Specified name settings need to be configured" } } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan) { + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan) { Throw "Must specify whether to use virtual SAN or not" } - $jsonTemplate = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template - $jsonParentVm = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm - $jsonSnapshot = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot - $jsonVmFolder = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder - $jsonHostOrCluster = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster - $ResourcePool = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + $jsonTemplate = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool if (! (($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) ))) { - Throw "Must specify Template or (ParentVm and Snapshot) names in json file" + Throw "Must specify Template or (ParentVm and Snapshot) names" } if ($null -eq $jsonVmFolder) { - Throw "Must specify VM folder in json file to deploy the VMs" + Throw "Must specify VM folder to deploy the VMs" } if ($null -eq $jsonHostOrCluster) { - Throw "Must specify Host or cluster in json file to deploy the VMs" + Throw "Must specify HostOrCluster to deploy the VMs" } if ($null -eq $resourcePool) { - Throw "Must specify Resource pool in json file to deploy the VMs" + Throw "Must specify Resource pool to deploy the VMs" } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { - Throw "Must specify datastores names in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names" } - if ($null -eq $jsonObject.AutomatedDesktopSpec.VirtualCenterManagedCommonSettings.transparentPageSharingScope) { - Throw "Must specify transparent page sharing scope in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterManagedCommonSettings.transparentPageSharingScope) { + Throw "Must specify transparent page sharing scope" } - $jsonCustomizationType = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType + $jsonCustomizationType = $PoolObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType switch ($jsonCustomizationType) { "NONE" { - if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.noCustomizationSettings) { - Throw "Specify noCustomization Settings in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.noCustomizationSettings) { + Throw "Specify noCustomization Settings" } } "QUICK_PREP" { - if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.quickprepCustomizationSettings) { - Throw "Specify quickprep CustomizationSettings in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.quickprepCustomizationSettings) { + Throw "Specify quickPrep customizationSettings" } } "SYS_PREP" { - if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.sysprepCustomizationSettings) { - Throw "Specify sysprep CustomizationSettings in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.sysprepCustomizationSettings) { + Throw "Specify sysPrep customizationSettings" } } "CLONE_PREP" { - if ($null -eq $jsonObject.AutomatedDesktopSpec.CustomizationSettings.cloneprepCustomizationSettings) { - Throw "Specify ClonePrep CustomizationSettings in json file" + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.cloneprepCustomizationSettings) { + Throw "Specify clonePrep customizationSettings" } } } - } elseIf ($jsonObject.Type -eq "MANUAL") { - $jsonUserAssignment = $jsonObject.ManualDesktopSpec.UserAssignment.UserAssignment + } elseIf ($PoolObject.Type -eq "MANUAL") { + $jsonUserAssignment = $PoolObject.ManualDesktopSpec.UserAssignment.UserAssignment if (! (($jsonUserAssignment -eq "FLOATING") -or ($jsonUserAssignment -eq "DEDICATED")) ) { Throw "UserAssignment must be FLOATING or DEDICATED" } $jsonSource = @('VIRTUAL_CENTER','UNMANAGED') - if (! ($jsonSource -contains $jsonObject.ManualDesktopSpec.Source)) { + if (! ($jsonSource -contains $PoolObject.ManualDesktopSpec.Source)) { Throw "The Source of machines must be VIRTUAL_CENTER or UNMANAGED" } - if ($null -eq $jsonObject.ManualDesktopSpec.Machines) { - Throw "Specify list of machines to add to this desktop during creation" + if ($null -eq $PoolObject.ManualDesktopSpec.Machines) { + Throw "Specify list of virtual machines to be added to this pool" } } - elseIf ($jsonObject.type -eq "RDS") { - if ($null -eq $jsonObject.RdsDesktopSpec.Farm) { - Throw "Specify Farm needed to create RDS Desktop" + elseIf ($PoolObject.type -eq "RDS") { + if ($null -eq $PoolObject.RdsDesktopSpec.Farm) { + Throw "Specify farm needed to create RDS desktop" } } } @@ -4813,7 +4848,7 @@ function Remove-HVFarm { .EXAMPLE Delete a given farm. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. - Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer + Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer -Confirm:$false .EXAMPLE Deletes a given Farm object(s). For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. @@ -4930,7 +4965,7 @@ function Remove-HVPool { .EXAMPLE Deletes pool from disk with given parameters PoolName etc. - Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk + Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk -Confirm:$false .EXAMPLE Deletes specified pool from disk @@ -5088,7 +5123,7 @@ function Set-HVFarm { .EXAMPLE Updates farm configuration by using json file - Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' + Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' -Confirm:$false .EXAMPLE Updates farm configuration with given parameters key and value @@ -5290,7 +5325,7 @@ function Set-HVPool { .EXAMPLE Updates pool configuration by using json file - Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' + Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' -Confirm:$false .EXAMPLE Updates pool configuration with given parameters key and value @@ -5500,7 +5535,7 @@ function Start-HVFarm { .EXAMPLE Requests a recompose of RDS Servers in the specified automated farm - Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' + Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' -Confirm:$false .EXAMPLE Requests a recompose task for automated farm in specified time @@ -5776,7 +5811,7 @@ function Start-HVPool { .EXAMPLE Requests a refresh of machines in the specified pool - Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF + Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -Confirm:$false .EXAMPLE Requests a rebalance of machines in a pool with specified time @@ -6414,7 +6449,7 @@ function Get-HVMachineSummary { return $machineList } -function Get-HVDesktopSpec { +function Get-HVPoolSpec { <# .Synopsis Gets desktop specification @@ -6431,11 +6466,11 @@ function Get-HVDesktopSpec { .EXAMPLE Converts DesktopInfo to DesktopSpec - Get-HVDesktopSpec -DesktopInfo $DesktopInfoObj + Get-HVPoolSpec -DesktopInfo $DesktopInfoObj .EXAMPLE Converts DesktopInfo to DesktopSpec and also dumps json object - Get-HVPool -PoolName 'LnkClnJson' | Get-HVDesktopSpec -FilePath "C:\temp\LnkClnJson.json" + Get-HVPool -PoolName 'LnkClnJson' | Get-HVPoolSpec -FilePath "C:\temp\LnkClnJson.json" .OUTPUTS Returns desktop specification @@ -6763,4 +6798,4 @@ function Get-HVInternalName { } } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVDesktopSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool From c3d82c2119a0d118eadab90d79d3460c0dd0bbc3 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 10 Jan 2017 16:27:48 +0530 Subject: [PATCH 23/90] Update task info Task information is not displaying properly, now fixed it. --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index d388441..f04dfef 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5683,7 +5683,7 @@ function Start-HVFarm { $farm_service_helper.Farm_Update($services,$item,$updates) $farm_service_helper.Farm_Recompose($services,$item,$spec) } - Write-Host "Performed recompose task on farm: $farmList.item" + Write-Host "Performed recompose task on farm: " $farmList.$item } } } @@ -5970,7 +5970,7 @@ function Start-HVPool { if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Rebalance($services,$item,$spec) } - Write-Host "Performed rebalance task on Pool: $PoolList.$item" + Write-Host "Performed rebalance task on Pool: " $PoolList.$item } } 'REFRESH' { @@ -5980,7 +5980,7 @@ function Start-HVPool { if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Refresh($services,$item,$spec) } - Write-Host "Performed refresh task on Pool: $PoolList.$item" + Write-Host "Performed refresh task on Pool: " $PoolList.$item } } 'RECOMPOSE' { @@ -5999,7 +5999,7 @@ function Start-HVPool { if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } - Write-Host "Performed recompose task on Pool: $PoolList.$item" + Write-Host "Performed recompose task on Pool: " $PoolList.$item } } 'PUSH_IMAGE' { @@ -6017,7 +6017,7 @@ function Start-HVPool { if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) } - Write-Host "Performed push_image task on Pool: $PoolList.$item" + Write-Host "Performed push_image task on Pool: " $PoolList.$item } } 'CANCEL_PUSH_IMAGE' { @@ -6028,7 +6028,7 @@ function Start-HVPool { if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) } - Write-Host "Performed cancel_push_image task on Pool: $PoolList.$item" + Write-Host "Performed cancel_push_image task on Pool: " $PoolList.$item } } } From 6ede0e5aae94f0274e91d980bdccb21623db2401 Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Wed, 18 Jan 2017 13:45:56 -0500 Subject: [PATCH 24/90] Get-HVMachine Summary - Remove Informational Message Get-HVMachine Summary - Remove informational nessage and associated break --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 583933d..7c06a99 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5253,10 +5253,6 @@ function Get-HVMachineSummary { } $machineList = Find-HVMachine -Param $PSBoundParameters - if (!$machineList) { - Write-Host "No Virtual Machine(s) Found with given search parameters" - break - } return $machineList } From ea23bb0568b2823b78afff58e48ffa214a1d3822 Mon Sep 17 00:00:00 2001 From: Mike Foley Date: Wed, 18 Jan 2017 15:33:50 -0500 Subject: [PATCH 25/90] Update VMware.VMEncryption.psm1 Added missing parenthesis at line 277 position 35. --- Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index 5252f96..c350955 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -274,7 +274,7 @@ Function Set-vMotionEncryptionConfig { [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$VM, - [Parameter(Mandatory=$True] + [Parameter(Mandatory=$True)] [ValidateSet("disabled", "opportunistic", "required")] [String]$Encryption ) From fa6c9f491f4c83ca34fbbf78ca973e923428d73b Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Thu, 19 Jan 2017 17:44:57 -0800 Subject: [PATCH 26/90] Added NVME Info Script Added NVME Info Script --- Scripts/NVME Info.ps1 | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Scripts/NVME Info.ps1 diff --git a/Scripts/NVME Info.ps1 b/Scripts/NVME Info.ps1 new file mode 100644 index 0000000..415b298 --- /dev/null +++ b/Scripts/NVME Info.ps1 @@ -0,0 +1,27 @@ +<# + .NOTES + =========================================================================== + Created by: Alan Renouf + Organization: VMware + Blog: http://virtu-al.net + Twitter: @alanrenouf + =========================================================================== +#> + +Foreach ($vmhost in Get-VMHost) { + $esxcli = get-esxcli -V2 -vmhost $vmhost + Write-Host "Host: $($vmhost.name)" -ForegroundColor Green + $devices = $esxcli.nvme.device.list.Invoke() + Foreach ($device in $devices) { + $nvmedevice = $esxcli.nvme.device.get.CreateArgs() + $nvmedevice.adapter = $device.HBAName + $esxcli.nvme.device.get.invoke($nvmedevice) | Select-Object ModelNumber, FirmwareRevision + $features = $esxcli.nvme.device.feature.ChildElements | Select-object -ExpandProperty name + ForEach ($feature in $features){ + Write-Host "Feature: $feature" -ForegroundColor Yellow + $currentfeature = $esxcli.nvme.device.feature.$feature.get.CreateArgs() + $currentfeature.adapter = $device.HBAName + $esxcli.nvme.device.feature.$feature.get.Invoke($currentfeature) + } + } +} \ No newline at end of file From dd8906d65fab1d7ea2cd217dfec4316f5ddd9289 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 20 Jan 2017 19:27:31 +0530 Subject: [PATCH 27/90] Adding of new AFs New-HVEntitlement, Get-HVEntitlement and Remove-HVEntitlement 1) This represents a simple association between a single user/group and a resource that they can be assigned to. Examples of associated resources are Desktops, Applications, GlobalEntitlements,GlobalApplicationEntitlements,or URLRedirection Settings. Individual users/groups and resources may be associated with multiple user entitlements. 2) Adding Get-Help changes for Pool 3) Configuring Clone Prep and Sys Prep script parameters to pool --- .../VMware.HV.Helper.format.ps1xml | 35 +- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 920 +++++++++++++++++- 2 files changed, 933 insertions(+), 22 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml index 62ce001..7f8a6aa 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml @@ -27,6 +27,10 @@ 16 + + + 8 + 7 @@ -56,10 +60,23 @@ $_.desktopSummaryData.userAssignment - + + + $filterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + $GlobalfilterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + Try { + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filterContains + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $GlobalfilterContains + } Catch { + #Do nothing + } + $results.length + + + $_.desktopSummaryData.enabled - + $_.desktopSummaryData.numSessions @@ -97,6 +114,20 @@ $_.desktopSummaryData.userAssignment + + + $filterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + $GlobalfilterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + Try { + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filterContains + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $GlobalfilterContains + } Catch { + #Do nothing + } + $results.length + + + $_.desktopSummaryData.enabled diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index f04dfef..053d1f6 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -2920,6 +2920,78 @@ function New-HVPool { This is a list of tags that access to the desktop is restricted to. No list means that the desktop can be accessed from any connection server. +.PARAMETER PowerPolicy + Power policy for the machines in the desktop after logoff. + This setting is only relevant for managed machines + +.PARAMETER AutomaticLogoffPolicy + Automatically log-off policy after disconnect. + This property has a default value of "NEVER". + +.PARAMETER AutomaticLogoffMinutes + The timeout in minutes for automatic log-off after disconnect. + This property is required if automaticLogoffPolicy is set to "AFTER". + +.PARAMETER AllowUsersToResetMachines + Whether users are allowed to reset/restart their machines. + +.PARAMETER AllowMultipleSessionsPerUser + Whether multiple sessions are allowed per user in case of Floating User Assignment. + +.PARAMETER DeleteOrRefreshMachineAfterLogoff + Whether machines are to be deleted or refreshed after logoff in case of Floating User Assignment. + +.PARAMETER RefreshOsDiskAfterLogoff + Whether and when to refresh the OS disks for dedicated-assignment, linked-clone machines. + +.PARAMETER RefreshPeriodDaysForReplicaOsDisk + Regular interval at which to refresh the OS disk. + +.PARAMETER RefreshThresholdPercentageForReplicaOsDisk + With the 'AT_SIZE' option for refreshOsDiskAfterLogoff, the size of the linked clone's OS disk in the datastore is compared to its maximum allowable size. + +.PARAMETER SupportedDisplayProtocols + The list of supported display protocols for the desktop. + +.PARAMETER DefaultDisplayProtocol + The default display protocol for the desktop. For a managed desktop, this will default to "PCOIP". For an unmanaged desktop, this will default to "RDP". + +.PARAMETER AllowUsersToChooseProtocol + Whether the users can choose the protocol. + +.PARAMETER Renderer3D + Specify 3D rendering dependent types hardware, software, vsphere client etc. + +.PARAMETER EnableGRIDvGPUs + Whether GRIDvGPUs enabled or not + +.PARAMETER VRamSizeMB + VRAM size for View managed 3D rendering. More VRAM can improve 3D performance. + +.PARAMETER MaxNumberOfMonitors + The greater these values are, the more memory will be consumed on the associated ESX hosts + +.PARAMETER MaxResolutionOfAnyOneMonitor + The greater these values are, the more memory will be consumed on the associated ESX hosts. + +.PARAMETER EnableHTMLAccess + HTML Access, enabled by VMware Blast technology, allows users to connect to View machines from Web browsers. + +.PARAMETER Quality + This setting determines the image quality that the flash movie will render. Lower quality results in less bandwidth usage. + +.PARAMETER Throttling + This setting affects the frame rate of the flash movie. If enabled, the frames per second will be reduced based on the aggressiveness level. + +.PARAMETER OverrideGlobalSetting + Mirage configuration specified here will be used for this Desktop + +.PARAMETER Enabled + Whether a Mirage server is enabled. + +.PARAMETER Url + The URL of the Mirage server. This should be in the form "<(DNS name)|(IPv4)|(IPv6)><:(port)>". IPv6 addresses must be enclosed in square brackets. + .PARAMETER Vcenter Virtual Center server-address (IP or FQDN) where the pool virtual machines are located. This should be same as provided to the Connection Server while adding the vCenter server. @@ -2954,6 +3026,60 @@ function New-HVPool { Whether to use vSphere VSAN. This is applicable for vSphere 5.5 or later. Applicable to Full, Linked, Instant Clone Pools. +.PARAMETER UseSeparateDatastoresReplicaAndOSDisks + Whether to use separate datastores for replica and OS disks. + +.PARAMETER ReplicaDiskDatastore + Datastore to store replica disks for View Composer and Instant clone engine sourced machines. + +.PARAMETER UseNativeSnapshots + Native NFS Snapshots is a hardware feature, specify whether to use or not + +.PARAMETER ReclaimVmDiskSpace + virtual machines can be configured to use a space efficient disk format that supports reclamation of unused disk space. + +.PARAMETER ReclamationThresholdGB + Initiate reclamation when unused space on VM exceeds the threshold. + +.PARAMETER RedirectWindowsProfile + Windows profiles will be redirected to persistent disks, which are not affected by View Composer operations such as refresh, recompose and rebalance. + +.PARAMETER UseSeparateDatastoresPersistentAndOSDisks + Whether to use separate datastores for persistent and OS disks. This must be false if redirectWindowsProfile is false. + +.PARAMETER PersistentDiskDatastores + Name of the Persistent disk datastore + +.PARAMETER PersistentDiskStorageOvercommit + Storage overcommit determines how view places new VMs on the selected datastores. + +.PARAMETER DiskSizeMB + Size of the persistent disk in MB. + +.PARAMETER DiskDriveLetter + Persistent disk drive letter. + +.PARAMETER RedirectDisposableFiles + Redirect disposable files to a non-persistent disk that will be deleted automatically when a user's session ends. + +.PARAMETER NonPersistentDiskSizeMB + Size of the non persistent disk in MB. + +.PARAMETER NonPersistentDiskDriveLetter + Non persistent disk drive letter. + +.PARAMETER UseViewStorageAccelerator + Whether to use View Storage Accelerator. + +.PARAMETER ViewComposerDiskTypes + Disk types to enable for the View Storage Accelerator feature. + +.PARAMETER RegenerateViewStorageAcceleratorDays + How often to regenerate the View Storage Accelerator cache. + +.PARAMETER BlackoutTimes + A list of blackout times. + .PARAMETER StopOnProvisioningError Set to true to stop provisioning of all VMs on error. Applicable to Full, Linked, Instant Clone Pools. @@ -3039,6 +3165,21 @@ function New-HVPool { The customization spec to use. Applicable to Full, Linked Clone Pools. +.PARAMETER PowerOffScriptName + Power off script. ClonePrep/QuickPrep can run a customization script on instant/linked clone machines before they are powered off. Provide the path to the script on the parent virtual machine. + Applicable to Linked, Instant Clone pools. + +.PARAMETER PowerOffScriptParameters + Power off script parameters. Example: p1 p2 p3 + Applicable to Linked, Instant Clone pools. + +.PARAMETER PostSynchronizationScriptName + Post synchronization script. ClonePrep/QuickPrep can run a customization script on instant/linked clone machines after they are created or recovered or a new image is pushed. Provide the path to the script on the parent virtual machine. + Applicable to Linked, Instant Clone pools. + +.PARAMETER PostSynchronizationScriptParameters + Post synchronization script parameters. Example: p1 p2 p3 + Applicable to Linked, Instant Clone pools. .PARAMETER Source Source of the Virtual machines for manual pool. Supported values are 'VIRTUAL_CENTER','UNMANAGED'. @@ -3366,6 +3507,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [string] $datacenter, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -3379,12 +3521,14 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [string[]] $StorageOvercommit = $null, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [boolean] $UseVSAN = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] @@ -3397,21 +3541,24 @@ function New-HVPool { [string] $ReplicaDiskDatastore, - #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore if LINKED_CLONE, INSTANT_CLONE + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.UseNativeSnapshots if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $UseNativeSnapshots = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $ReclaimVmDiskSpace = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [ValidateRange(0,[Int]::MaxValue)] [int] $ReclamationThresholdGB = 1, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.redirectWindowsProfile if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] @@ -3435,11 +3582,13 @@ function New-HVPool { [ValidateRange(128,[Int]::MaxValue)] [int] $DiskSizeMB = 2048, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.diskDriveLetter if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [ValidatePattern("^[D-Z]$")] [string] $DiskDriveLetter = "D", + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.redirectDisposableFiles if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [boolean] @@ -3450,6 +3599,7 @@ function New-HVPool { [ValidateRange(512,[Int]::MaxValue)] [int] $NonPersistentDiskSizeMB = 4096, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.diskDriveLetter if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [ValidatePattern("^[D-Z]|Auto$")] @@ -3483,6 +3633,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [VMware.Hv.DesktopNetworkInterfaceCardSettings[]] $Nics, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] @@ -3599,10 +3750,12 @@ function New-HVPool { [ValidateSet('CLONE_PREP','QUICK_PREP','SYS_PREP','NONE')] [string] $CustType, + #desktopSpec.automatedDesktopSpec.customizationSettings.reusePreExistingAccounts if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] [Boolean] $ReusePreExistingAccounts = $false, + #desktopSpec.automatedDesktopSpec.customizationSettings.sysprepCustomizationSettings.customizationSpec if LINKED_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = "FULL_CLONE")] @@ -3641,6 +3794,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] [string] $PostSynchronizationScriptParameters, + #manual desktop [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [ValidateSet('VIRTUAL_CENTER','UNMANAGED')] @@ -3787,7 +3941,7 @@ function New-HVPool { $reusePreExistingAccounts = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.reusePreExistingAccounts } 'QUICK_PREP' { - $powerOffScriptName= $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName + $powerOffScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptParameters $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptName $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptParameters @@ -4299,8 +4453,8 @@ function New-HVPool { } $desktopSpecObj.DesktopSettings = $desktopSettings - - if ($globalEntitlement) { + $info = $services.PodFederation.PodFederation_get() + if ($globalEntitlement -and ("ENABLED" -eq $info.localPodStatus.status)) { $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals $QueryFilterEquals.memberName = 'base.displayName' $QueryFilterEquals.value = $globalEntitlement @@ -4606,6 +4760,10 @@ function Get-HVPoolCustomizationSetting { } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings = Get-CustomizationObject $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters } else { if ($LinkedClone) { @@ -4634,6 +4792,10 @@ function Get-HVPoolCustomizationSetting { } elseIf ($custType -eq 'QUICK_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'QUICK_PREP' $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings = Get-CustomizationObject + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters } else { throw "The customization type: [$custType] is not supported for LinkedClone Pool" } @@ -5200,7 +5362,7 @@ function Set-HVFarm { } process { - $farmList = @() + $farmList = @{} if ($farmName) { try { $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer @@ -5214,7 +5376,7 @@ function Set-HVFarm { Write-Error "Start/Stop operation is not supported for farm with name : [$farmObj.Data.Name]" return } - $farmList += $farmObj.id + $farmList.add($farmObj.id, $farmObj.data.name) } } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farmName]" @@ -5227,14 +5389,14 @@ function Set-HVFarm { Write-Error "Start/Stop operation is not supported for farm with name : [$item.Data.Name]" return } - $farmList += $item.id + $farmList.add($item.id, $item.data.name) } elseif ($item.GetType().name -eq 'FarmInfo') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.Type)) { Write-Error "Start/Stop operation is not supported for farm with name : [$item.Data.Name]" return } - $farmList += $item.id + $farmList.add($item.id, $item.data.name) } else { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" @@ -5271,11 +5433,11 @@ function Set-HVFarm { -value $false } $farm_service_helper = New-Object VMware.Hv.FarmService - foreach ($item in $farmList) { - if ($pscmdlet.ShouldProcess($updates)) { + foreach ($item in $farmList.Keys) { + if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_Update($services,$item,$updates) } - Write-Host "Updated Farm Member $updates.Key with value $updates.value" + Write-Host "Update successful for farm: " $farmList.$item } } @@ -5408,7 +5570,7 @@ function Set-HVPool { } process { - $poolList = @() + $poolList = @{} if ($poolName) { try { $desktopPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer @@ -5422,24 +5584,24 @@ function Set-HVPool { Write-Error "Start/Stop operation is not supported for Poll with name : [$item.DesktopSummaryData.Name]" return } - $poolList += $desktopObj.id + $poolList.add($desktopObj.id, $desktopObj.DesktopSummaryData.Name) } } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Pool) { foreach ($item in $pool) { if ($item.GetType().name -eq 'DesktopInfo') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.Type)) { Write-Error "Start/Stop operation is not supported for Pool with name : [$item.Base.Name]" return } - $poolList += $item.id + $poolList.add($item.id, $item.Base.Name) } elseif ($item.GetType().name -eq 'DesktopSummaryView') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.DesktopSummaryData.Type)) { Write-Error "Start/Stop operation is not supported for Poll with name : [$item.DesktopSummaryData.Name]" return } - $poolList += $item.id + $poolList.add($item.id, $item.DesktopSummaryData.Name) } else { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" @@ -5480,12 +5642,12 @@ function Set-HVPool { -value $false } $desktop_helper = New-Object VMware.Hv.DesktopService - foreach ($item in $poolList) { - if ($pscmdlet.ShouldProcess($updates)) { + foreach ($item in $poolList.Keys) { + if ($pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } - Write-Host "Updated Pool member $updates.key with value $updates.value" } + Write-Host "Update successful for Pool: " $poolList.$item } end { @@ -6798,4 +6960,722 @@ function Get-HVInternalName { } } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool + +function Get-UserInfo { + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $UserName + ) + + if ($UserName -match '^.+?[@].+?$') { + $info = $UserName -split "@" + $Domain = $info[1] + $Name = $Info[0] + } else { + $info = $UserName -split "\\" + $Domain = $info[0] + $Name = $Info[1] + } + return @{'Name' = $Name; 'Domain' = $Domain} +} + +function New-HVEntitlement { +<# +.Synopsis + Associates a user/group with a resource + +.DESCRIPTION + This represents a simple association between a single user/group and a resource that they can be assigned. + +.PARAMETER User + User prinicipal name of user or group + +.PARAMETER ResourceName + The resource(Application, Pool etc.) name + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Associate a user/group with a pool + New-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'InsClnPol' -Confirm:$false + +.EXAMPLE + Associate a user/group with a application + New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'Calculator' -ResourceType Application + +.EXAMPLE + Associate a user/group with a URLRedirection settings + New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + +.EXAMPLE + Associate a user/group with a desktop entitlement + New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'GE1' -ResourceType GlobalEntitlement + +.EXAMPLE + Associate a user/group with a application entitlement + New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + +.EXAMPLE + Associate a user/group with list of pools + $pools = Get-HVPool; $pools | New-HVEntitlement -User 'adviewdev\administrator' -Confirm:$false + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $true,ParameterSetName ='Default')] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $true,ValueFromPipeline = $true,ParameterSetName ='PipeLine')] + $Resource, + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $IsGroup = ($Type -eq 'Group') + $filter1 = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $filter2 = Get-HVQueryFilter 'base.domain' -Eq $Domain + $filter3 = Get-HVQueryFilter 'base.group' -Eq $IsGroup + $andFilter = Get-HVQueryFilter -And -Filters @($filter1, $filter2, $filter3) + $results = Get-HVQueryResult -EntityType ADUserOrGroupSummaryView -Filter $andFilter -HvServer $HvServer + if ($results.length -ne 1) { + Write-Host "Unable to find specific user or group with given search parameters" + return + } + $ResourceObjs = $null + $info = $services.PodFederation.PodFederation_get() + switch($ResourceType){ + "Desktop" { + if ($ResourceName) { + $ResourceObjs = Get-HVPool -PoolName $ResourceName + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'DesktopInfo') { + $ResourceObjs += ,$item + } + elseif ($item.GetType().name -eq 'DesktopSummaryView') { + $ResourceObjs += ,$item + } + else { + Write-Error "In pipeline didn't received object(s) of expected type DesktopSummaryView/DesktopInfo" + return + } + } + } + } + "Application" { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'ApplicationInfo') { + $ResourceObjs += ,$item + + } else { + Write-Error "In pipeline didn't received object(s) of expected type ApplicationInfo" + return + } + } + } + } + "URLRedirection" { + if ($ResourceName) { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'URLRedirectionInfo') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type URLRedirectionInfo" + return + } + } + } + } + "GlobalApplicationEntitlement" { + if ("ENABLED" -eq $info.localPodStatus.status) { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'GlobalApplicationEntitlementInfo') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type globalApplicationEntitlementInfo" + return + } + } + } + } + } else { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + } + "GlobalEntitlement" { + if ("ENABLED" -eq $info.localPodStatus.status) { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'GlobalEntitlementSummaryView') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type GlobalEntitlementSummaryView" + return + } + } + } + } + } else { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + } + } + $base = New-Object VMware.HV.UserEntitlementBase + $base.UserOrGroup = $results.id + foreach ($ResourceObj in $ResourceObjs) { + $base.Resource = $ResourceObj.id + if ($pscmdlet.ShouldProcess($User)) { + $id = $services.UserEntitlement.UserEntitlement_Create($base) + } + } + Write-host $ResourceObjs.Length " resource(s) entitled with User or group: " $User + } + end { + [System.gc]::collect() + } +} + + +function Get-HVEntitlement { +<# +.Synopsis + Gets association data between a user/group and a resource + +.DESCRIPTION + Provides entitlement Info between a single user/group and a resource that they can be assigned. + +.PARAMETER User + User prinicipal name of user or group + +.PARAMETER ResourceName + The resource(Application, Pool etc.) name + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Gets all the entitlements related to application pool + Get-HVEntitlement -ResourceType Application + +.EXAMPLE + Gets entitlements specific to user or group name and application resource + Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'calculator' -ResourceType Application + +.EXAMPLE + Gets entitlements specific to user or group and URLRedirection resource + Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + +.EXAMPLE + Gets entitlements specific to user or group and GlobalEntitlement resource + Get-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'GE1' -ResourceType GlobalEntitlement + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $false)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $AndFilter = @() + $results = @() + $ResourceObjs = $null + if ($User) { + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $nameFilter = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $AndFilter += $nameFilter + $doaminFilter = Get-HVQueryFilter 'base.domain' -Eq $Domain + $AndFilter += $doaminFilter + } + $IsGroup = ($Type -eq 'Group') + $groupFilter = Get-HVQueryFilter 'base.group' -Eq $IsGroup + $AndFilter += $groupFilter + $info = $services.PodFederation.PodFederation_get() + $cpaEnabled = ("ENABLED" -eq $info.localPodStatus.status) + switch($ResourceType) { + "Desktop" { + if ($ResourceName) { + $ResourceObjs = Get-HVPool -PoolName $ResourceName -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.desktops' -Contains ([VMware.Hv.DesktopId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.localData.desktops -ne $null} + } + "Application" { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.applications' -Contains ([VMware.Hv.ApplicationId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.localData.applications -ne $null} + } + "URLRedirection" { + $localFilter = @() + $globalFilter = @() + $localFilter += $AndFilter + $globalFilter += $AndFilter + if ($ResourceName) { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + $localFilter += Get-HVQueryFilter 'localData.urlRedirectionSettings' -Contains ([VMware.Hv.URLRedirectionId[]]$ResourceObjs.Id) + if ($cpaEnabled) { + $globalFilter += Get-HVQueryFilter 'globalData.urlRedirectionSettings' -Contains ([VMware.Hv.URLRedirectionId[]]$ResourceObjs.Id) + } + } + $localFilter = Get-HVQueryFilter -And -Filters $localFilter + $localResults = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $localFilter -HvServer $HvServer + $results += ($localResults | where {$_.localData.urlRedirectionSettings -ne $null}) + if ($cpaEnabled) { + $globalFilter = Get-HVQueryFilter -And -Filters $globalFilter + $globalResults = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $globalFilter -HvServer $HvServer + $globalResults = $globalResults | where {$_.globalData.urlRedirectionSettings -ne $null} + $results += $globalResults + } + } + "GlobalApplicationEntitlement" { + if (! $cpaEnabled) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalApplicationEntitlements' -Contains ([VMware.Hv.GlobalApplicationEntitlementId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results| where {$_.globalData.globalApplicationEntitlements -ne $null} + } + "GlobalEntitlement" { + if (! $cpaEnabled) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalEntitlements' -Contains ([VMware.Hv.GlobalEntitlementId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.globalData.globalEntitlements -ne $null} + } + } + if (! $results) { + Write-Host "Get-HVEntitlement: No entitlements found with given search parameters" + break + } + return $results + } + end { + [System.gc]::collect() + } +} + +function Remove-HVEntitlement { +<# +.Synopsis + Deletes association data between a user/group and a resource + +.DESCRIPTION + Removes entitlement between a single user/group and a resource that already been assigned. + +.PARAMETER User + User prinicipal name of user or group + +.PARAMETER ResourceName + The resource(Application, Pool etc.) name + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Deletes entitlement between a user/group and a pool resource + Remove-HVEntitlement -User 'administrator@adviewdev' -ResourceName LnkClnJSon -Confirm:$false + +.EXAMPLE + Deletes entitlement between a user/group and a Application resource + Remove-HVEntitlement -User 'adviewdev\puser2' -ResourceName 'calculator' -ResourceType Application + +.EXAMPLE + Deletes entitlement between a user/group and a GlobalApplicationEntitlement resource + Remove-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $AndFilter = @() + $results = $null + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $nameFilter = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $doaminFilter = Get-HVQueryFilter 'base.domain' -Eq $Domain + $IsGroup = ($Type -eq 'Group') + $groupFilter = Get-HVQueryFilter 'base.group' -Eq $IsGroup + [VMware.Hv.UserEntitlementId[]] $userEntitlements = $null + if ($ResourceName) { + $info = $services.PodFederation.PodFederation_get() + switch($ResourceType) { + "Desktop" { + $ResourceObjs = Get-HVPool -PoolName $ResourceName -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.desktops' -Contains ([VMware.HV.DesktopId[]] $ResourceObjs.Id) + $filters = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filters -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.localData.desktopUserEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + Write-Host $userEntitlements.Length " desktopUserEntitlement(s) are removed for UserOrGroup " $user + } + } + } + "Application" { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.applications' -Contains ([VMware.HV.ApplicationId[]] $ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.localData.applicationUserEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + Write-Host $userEntitlements.Length " applicationUserEntitlement(s) are removed for UserOrGroup " $user + } + } + } + "URLRedirection" { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + $localFilter = @() + $localFilter += $AndFilter + $localFilter += (Get-HVQueryFilter 'localData.urlRedirectionSettings' -Contains ([VMware.HV.URLRedirectionId[]]$ResourceObjs.Id)) + $localFilter = Get-HVQueryFilter -And -Filters $localFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $localFilter -HvServer $HvServer + if ("ENABLED" -eq $info.localPodStatus.status) { + $globalFilter = @() + $globalFilter += $AndFilter + $globalFilter += Get-HVQueryFilter 'globalData.urlRedirectionSettings' -Contains ([VMware.HV.URLRedirectionId[]]$ResourceObjs.Id) + $globalFilter = Get-HVQueryFilter -And -Filters $globalFilter + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $globalFilter -HvServer $HvServer + } + if ($results) { + foreach ($result in $Results) { + if ($result.GetType().Name -eq 'EntitledUserOrGroupLocalSummaryView') { + $userEntitlements = $result.localData.urlRedirectionUserEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } else { + $userEntitlements = $result.globalData.urlRedirectionUserEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } + Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) are removed for UserOrGroup " $user + } + } + } + "GlobalApplicationEntitlement" { + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalApplicationEntitlements' -Contains ([VMware.Hv.GlobalApplicationEntitlementId[]]$ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.globalData.globalUserApplicationEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + Write-Host $userEntitlements.Length " GlobalApplicationEntitlement(s) are removed for UserOrGroup " $user + } + } + } + "GlobalEntitlement" { + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalEntitlements' -Contains ([VMware.Hv.GlobalEntitlementId[]]$ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.globalData.globalUserEntitlements + if ($pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + Write-Host $userEntitlements.Length " GlobalEntitlement(s) are removed for UserOrGroup " $user + } + + } + } + } + } + if (! $results) { + Write-Host "Remove-HVEntitlement: No entitlements found with given search parameters" + return + } + } + end { + [System.gc]::collect() + } +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement From 42d33acb4c10adc4e8e257b5eccc6b49c5cfae09 Mon Sep 17 00:00:00 2001 From: vkg1215 Date: Mon, 23 Jan 2017 12:04:48 +0530 Subject: [PATCH 28/90] Code for Instant clone farm and farm maintenance Code for Instant clone farm and farm maintenance --- .../Json/Farm/AutomatedInstantCloneFarm.json | 98 ++++ .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 504 ++++++++++++++---- 2 files changed, 489 insertions(+), 113 deletions(-) create mode 100644 Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json diff --git a/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json new file mode 100644 index 0000000..605e6a5 --- /dev/null +++ b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json @@ -0,0 +1,98 @@ +{ + "Type": "AUTOMATED", + "Data": { + "Name": "ICFarmJson", + "DisplayName": "FarmJsonTest", + "AccessGroup": "Root", + "Description": "created IC Farm from PS via JSON", + "Enabled": null, + "Deleting": false, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, + "Desktop": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, + "ServerErrorThreshold": null, + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } + }, + "AutomatedFarmSpec": { + "ProvisioningType": "INSTANT_CLONE_ENGINE", + "VirtualCenter": null, + "RdsServerNamingSpec": { + "NamingMethod": "PATTERN", + "PatternNamingSettings": { + "NamingPattern": "ICFarmVMPS", + "MaxNumberOfRDSServers": 1 + } + }, + "VirtualCenterProvisioningSettings": { + "EnableProvisioning": true, + "StopProvisioningOnError": true, + "MinReadyVMsOnVComposerMaintenance": 0, + "VirtualCenterProvisioningData": { + "ParentVm": "vm-rdsh-ic", + "Snapshot": "snap_5", + "Datacenter": null, + "VmFolder": "Instant_Clone_VMs", + "HostOrCluster": "vimal-cluster", + "ResourcePool": "vimal-cluster" + }, + "VirtualCenterStorageSettings": { + "Datastores": [ + { + "Datastore": "Datastore1", + "StorageOvercommit": "UNBOUNDED" + } + ], + "UseVSan": false, + "ViewComposerStorageSettings": { + "UseSeparateDatastoresReplicaAndOSDisks": false, + "ReplicaDiskDatastore": null, + "UseNativeSnapshots": false, + "SpaceReclamationSettings": { + "ReclaimVmDiskSpace": false, + "ReclamationThresholdGB": null, + "BlackoutTimes": null + } + } + }, + "VirtualCenterNetworkingSettings": { + "Nics": null + } + }, + "VirtualCenterManagedCommonSettings": { + "TransparentPageSharingScope": "VM" + }, + "CustomizationSettings": { + "CustomizationType": "CLONE_PREP", + "DomainAdministrator": null, + "AdContainer": "CN=Computers", + "ReusePreExistingAccounts": false, + "ClonePrepCustomizationSettings": { + "InstantCloneEngineDomainAdministrator": null, + "PowerOffScriptName": null, + "PowerOffScriptParameters": null, + "PostSynchronizationScriptName": null, + "PostSynchronizationScriptParameters": null + } + }, + "RdsServerMaxSessionsData": { + "MaxSessionsType": "UNLIMITED", + "MaxSessions": null + } + }, + "ManualFarmSpec": null, + "NetBiosName" : "ad-vimalg" +} diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index ed97a24..e5cb880 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1800,6 +1800,9 @@ function New-HVFarm { .PARAMETER LinkedClone Switch to Create Automated Linked Clone farm. +.PARAMETER InstantClone + Switch to Create Automated Instant Clone farm. + .PARAMETER Manual Switch to Create Manual farm. @@ -1824,38 +1827,38 @@ function New-HVFarm { .PARAMETER ParentVM Base image VM for RDS Servers. - Applicable only to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER SnapshotVM Base image snapshot for RDS Servers. .PARAMETER VmFolder VM folder to deploy the RDSServers to. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER HostOrCluster Host or cluster to deploy the RDSServers in. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER ResourcePool Resource pool to deploy the RDSServers. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER Datastores Datastore names to store the RDSServer. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER UseVSAN Whether to use vSphere VSAN. This is applicable for vSphere 5.5 or later. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER EnableProvisioning Set to true to enable provision of RDSServers immediately in farm. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER StopOnProvisioningError Set to true to stop provisioning of all RDSServers on error. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER TransparentPageSharingScope The transparent page sharing scope. @@ -1864,7 +1867,7 @@ function New-HVFarm { .PARAMETER NamingMethod Determines how the VMs in the farm are named. Set PATTERN to use naming pattern. - The default value is PATTERN. Curentlly only PATTERN is allowed. + The default value is PATTERN. Currently only PATTERN is allowed. .PARAMETER NamingPattern RDS Servers will be named according to the specified naming pattern. @@ -1879,26 +1882,42 @@ function New-HVFarm { .PARAMETER MaximumCount Maximum number of Servers in the farm. The default value is 1. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER AdContainer This is the Active Directory container which the Servers will be added to upon creation. The default value is 'CN=Computers'. - Applicable to Linked Clone farm. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER NetBiosName Domain Net Bios Name. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER DomainAdmin Domain Administrator user name which will be used to join the domain. Default value is null. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER SysPrepName The customization spec to use. Applicable to Linked Clone farms. +.PARAMETER PowerOffScriptName + Power off script. ClonePrep can run a customization script on instant-clone machines before they are powered off. Provide the path to the script on the parent virtual machine. + Applicable to Instant Clone farms. + +.PARAMETER PowerOffScriptParameters + Power off script parameters. Example: p1 p2 p3 + Applicable to Instant Clone farms. + +.PARAMETER PostSynchronizationScriptName + Post synchronization script. ClonePrep can run a customization script on instant-clone machines after they are created or recovered or a new image is pushed. Provide the path to the script on the parent virtual machine. + Applicable to Instant Clone farms. + +.PARAMETER PostSynchronizationScriptParameters + Post synchronization script parameters. Example: p1 p2 p3 + Applicable to Instant Clone farms. + .PARAMETER RdsServers List of existing registered RDS server names to add into manual farm. Applicable to Manual farms. @@ -1911,12 +1930,20 @@ function New-HVFarm { .EXAMPLE Creates new linkedClone farm by using naming pattern - New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + +.EXAMPLE + Creates new linkedClone farm by using naming pattern + New-HVFarm -InstantClone -FarmName 'ICFarmCL' -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_5' -VmFolder 'Instant_Clone_VMs' -HostOrCluster 'vimal-cluster' -ResourcePool 'vimal-cluster' -Datastores 'datastore1' -FarmDisplayName 'IC Farm using CL' -Description 'created IC Farm from PS command-line' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "ICFarmCL-" -NetBiosName "ad-vimalg" .EXAMPLE Creates new linkedClone farm by using json file New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false +.EXAMPLE + Creates new linkedClone farm by using json file + New-HVFarm -Spec C:\VMWare\Specs\InstantCloneFarm.json -Confirm:$false + .EXAMPLE Creates new manual farm by using rdsServers names New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" -Confirm:$false @@ -1945,6 +1972,10 @@ function New-HVFarm { [switch] $LinkedClone, + [Parameter(Mandatory = $true,ParameterSetName = "INSTANT_CLONE")] + [switch] + $InstantClone, + [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [switch] $Manual, @@ -1952,6 +1983,7 @@ function New-HVFarm { #farmSpec.farmData.name [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = "INSTANT_CLONE")] [string] $FarmName, @@ -2040,108 +2072,129 @@ function New-HVFarm { [string] $Url, - #farmSpec.automatedfarmSpec.virtualCenter if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenter if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $Vcenter, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVM if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVM if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $ParentVM, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshotVM if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshotVM if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $SnapshotVM, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.vmFolder if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.vmFolder if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $VmFolder, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.hostOrCluster if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.hostOrCluster if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $HostOrCluster, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.resourcePool if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.resourcePool if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $ResourcePool, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.dataCenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.dataCenter if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $dataCenter, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string[]] $Datastores, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string[]] $StorageOvercommit = $null, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $UseVSAN = $false, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $EnableProvisioning = $true, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.stopOnProvisioningError if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.stopOnProvisioningError if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $StopOnProvisioningError = $true, [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $TransparentPageSharingScope = 'VM', - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.namingMethod if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.namingMethod if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [ValidateSet('PATTERN')] [string] $NamingMethod = 'PATTERN', - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.namingPattern if LINKED_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.namingPattern if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $NamingPattern = $farmName + '{n:fixed=4}', - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [int] $MinReady = 0, - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers if LINKED_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [int] $MaximumCount = 1, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks if INSTANT_CLONE, LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $UseSeparateDatastoresReplicaAndOSDisks = $false, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore, if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $ReplicaDiskDatastore, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useNativeSnapshots + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useNativeSnapshots, if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $UseNativeSnapshots = $false, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace, if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $ReclaimVmDiskSpace = $false, @@ -2156,18 +2209,23 @@ function New-HVFarm { [VMware.Hv.FarmBlackoutTime[]] $BlackoutTimes, - #farmSpec.automatedfarmSpec.customizationSettings.adContainer if LINKED_CLONE + #farmSpec.automatedfarmSpec.customizationSettings.adContainer if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [string] $AdContainer = 'CN=Computers', #farmSpec.automatedfarmSpec.customizationSettings.domainAdministrator + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $true,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $NetBiosName, #farmSpec.automatedfarmSpec.customizationSettings.domainAdministrator + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [string] $DomainAdmin = $null, @@ -2181,14 +2239,36 @@ function New-HVFarm { [string] $SysPrepName, - #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE + #desktopSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptName if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PowerOffScriptName, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptParameters if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PowerOffScriptParameters, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptName if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PostSynchronizationScriptName, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptParameters if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PostSynchronizationScriptParameters, + + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [ValidateSet("UNLIMITED", "LIMITED")] [string] $MaxSessionsType = "UNLIMITED", - #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [ValidateRange(1, [int]::MaxValue)] [int] $MaxSessions, @@ -2225,6 +2305,9 @@ function New-HVFarm { # ADContainerId # FarmSysprepCustomizationSettings # CustomizationSpecId + # FarmCloneprepCustomizationSettings + # InstantCloneEngineDomainAdministratorId + # # FarmManualfarmSpec # RDSServerId[] # @@ -2271,14 +2354,31 @@ function New-HVFarm { if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenter) { $vCenter = $jsonObject.AutomatedFarmSpec.VirtualCenter } - $linkedClone = $true + $netBiosName = $jsonObject.NetBiosName - if ($null -ne $jsonObject.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator) { - $domainAdministrator = $jsonObject.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator + if (!$jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer) { + Write-Host "adContainer was empty using CN=Computers" + } else { + $AdContainer = $jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer + } + + #populate customization settings attributes based on the cutomizationType + if ($jsonObject.AutomatedFarmSpec.ProvisioningType -eq "INSTANT_CLONE_ENGINE") { + $InstantClone = $true + if ($null -ne $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings) { + $DomainAdmin = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator + $powerOffScriptName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptParameters + } + } elseif ($jsonObject.AutomatedFarmSpec.ProvisioningType -eq "VIEW_COMPOSER") { + $LinkedClone = $true + $DomainAdmin = $jsonObject.AutomatedFarmSpec.CustomizationSettings.domainAdministrator + $reusePreExistingAccounts = $jsonObject.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts + $sysprepCustomizationSettings = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings + $sysPrepName = $sysprepCustomizationSettings.CustomizationSpec } - $adContainer = $jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer - $reusePreExistingAccounts = $jsonObject.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts - $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern @@ -2305,25 +2405,33 @@ function New-HVFarm { $storageOvercommit += $dtStore.StorageOvercommit } $useVSan = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.UseVSan - $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks - if ($useSeparateDatastoresReplicaAndOSDisks) { + + ## ViewComposerStorageSettings for Linked-Clone farms + if ($LinkedClone -or $InstantClone) { + $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks + if ($useSeparateDatastoresReplicaAndOSDisks) { $replicaDiskDatastore = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.ReplicaDiskDatastore - } - $useNativeSnapshots = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseNativeSnapshots - $reclaimVmDiskSpace = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclaimVmDiskSpace - if ($reclaimVmDiskSpace) { - $ReclamationThresholdGB = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclamationThresholdGB - if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes) { - $blackoutTimesList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes - foreach ($blackout in $blackoutTimesList) { - $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime - $blackoutObj.Days = $blackout.Days - $blackoutObj.StartTime = $blackout.StartTime - $blackoutObj.EndTime = $blackoutObj.EndTime - $blackoutTimes += $blackoutObj + } + if ($LinkedClone) { + #For Instant clone desktops, this setting can only be set to false + $useNativeSnapshots = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseNativeSnapshots + $reclaimVmDiskSpace = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclaimVmDiskSpace + if ($reclaimVmDiskSpace) { + $ReclamationThresholdGB = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclamationThresholdGB + if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes) { + $blackoutTimesList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes + foreach ($blackout in $blackoutTimesList) { + $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime + $blackoutObj.Days = $blackout.Days + $blackoutObj.StartTime = $blackout.StartTime + $blackoutObj.EndTime = $blackoutObj.EndTime + $blackoutTimes += $blackoutObj + } } } + } } + $maxSessionsType = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType if ($maxSessionsType -eq "LIMITED") { $maxSessions = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessions @@ -2370,7 +2478,10 @@ function New-HVFarm { if ($linkedClone) { $farmType = 'AUTOMATED' $provisioningType = 'VIEW_COMPOSER' - } elseif ($manual) { + } elseif ($InstantClone) { + $farmType = 'AUTOMATED' + $provisioningType = 'INSTANT_CLONE_ENGINE' + }elseif ($manual) { $farmType = 'MANUAL' } @@ -2601,12 +2712,16 @@ function Test-HVFarmSpec { if ($null -eq $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.useVSan) { Throw "Must specify whether to use virtual SAN or not" } - if ($null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.customizationType) { + $customizationType = $PoolObject.AutomatedFarmSpec.CustomizationSettings.customizationType + if ($null -eq $customizationType) { Throw "Specify customization type" } - if ($null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { + if ($customizationType -eq 'SYS_PREP' -and $null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { Throw "Specify sysPrep customization settings" } + if ($customizationType -eq 'CLONE_PREP' -and $null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings) { + Throw "Specify clone customization settings" + } if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType) { Throw "Specify MaxSessionsType" } @@ -2751,9 +2866,11 @@ function Get-HVFarmStorageObject { $StorageObject.Datastores += $datastoresObj } if ($useSeparateDatastoresReplicaAndOSDisks) { + $StorageObject.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks = $UseSeparateDatastoresReplicaAndOSDisks $FarmReplicaDiskDatastore = ($datastoreList | Where-Object { $_.datastoredata.name -eq $replicaDiskDatastore }).id + $StorageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = $FarmReplicaDiskDatastore } - $StorageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = $FarmReplicaDiskDatastore + } if ($storageObject.Datastores.Count -eq 0) { throw "No datastores found with name: [$datastores]" @@ -2784,52 +2901,77 @@ function Get-HVFarmCustomizationSetting { [VMware.Hv.VirtualCenterId]$VcID ) if (!$customObject) { - $ViewComposerDomainAdministrator_service_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) | Where-Object { $_.base.domain -match $netBiosName }) - if (! [string]::IsNullOrWhitespace($domainAdmin)) { - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } elseif ($null -ne $ViewComposerDomainAdministratorID) { - $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + # View Composer and Instant Clone Engine Active Directory container for QuickPrep and ClonePrep. This must be set for Instant Clone Engine or SVI sourced desktops. + if ($InstantClone -or $LinkedClone) { + $ad_domain_helper = New-Object VMware.Hv.ADDomainService + $ADDomains = $ad_domain_helper.ADDomain_List($services) + if ($netBiosName) { + $adDomianId = ($ADDomains | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain found with netBiosName: [$netBiosName]" + } + } else { + $adDomianId = ($ADDomains[0] | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain configured in view administrator UI" + } + } + $ad_container_helper = New-Object VMware.Hv.AdContainerService + $adContainerId = ($ad_container_helper.ADContainer_ListByDomain($services,$adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id + if ($null -eq $adContainerId) { + throw "No AdContainer found with name: [$adContainer]" + } + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.AdContainer = $adContainerId } - if ($null -eq $ViewComposerDomainAdministratorID) { - throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" - } - $ADDomain_service_helper = New-Object VMware.Hv.ADDomainService - $ADDomains = $ADDomain_service_helper.ADDomain_List($services) - if ($netBiosName) { - $adDomianId = ( $ADDomains| Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain found with netBiosName: [$netBiosName]" + + if ($InstantClone) { + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' + $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService + $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) | Where-Object { $_.namesData.dnsName -match $netBiosName }) + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id + } elseIf ($null -ne $instantCloneEngineDomainAdministrator) { + $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id } - } else { - $adDomianId = ( $ADDomains[0] | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain configured in view administrator UI" + if ($null -eq $instantCloneEngineDomainAdministrator) { + throw "No Instant Clone Engine Domain Administrator found with netBiosName: [$netBiosName]" + } + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings = New-Object VMware.Hv.FarmClonePrepCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters + $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings + } elseif ($LinkedClone) { + $ViewComposerDomainAdministrator_service_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService + $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) | Where-Object { $_.base.domain -match $netBiosName }) + if (! [string]::IsNullOrWhitespace($domainAdmin)) { + $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id + } elseif ($null -ne $ViewComposerDomainAdministratorID) { + $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + } + if ($null -eq $ViewComposerDomainAdministratorID) { + throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" } - } - $ad_containder_service_helper = New-Object VMware.Hv.AdContainerService - $adContainerId = ($ad_containder_service_helper.ADContainer_ListByDomain($services, $adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id - if ($null -eq $adContainerId) { - throw "No AdContainer found with name: [$adContainer]" - } - #Support only Sysprep Customization - $sysprepCustomizationSettings = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings - # Get SysPrep CustomizationSpec ID - $CustomizationSpec_service_helper = New-Object VMware.Hv.CustomizationSpecService - $sysPrepIds = $CustomizationSpec_service_helper.CustomizationSpec_List($services, $vcID) | Where-Object { $_.customizationSpecData.name -eq $sysPrepName } | Select-Object -Property id - if ($sysPrepIds.Count -eq 0) { - throw "No Sysprep Customization spec found with Name: [$sysPrepName]" + #Support only Sysprep Customization + $sysprepCustomizationSettings = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings + + # Get SysPrep CustomizationSpec ID + $CustomizationSpec_service_helper = New-Object VMware.Hv.CustomizationSpecService + $sysPrepIds = $CustomizationSpec_service_helper.CustomizationSpec_List($services, $vcID) | Where-Object { $_.customizationSpecData.name -eq $sysPrepName } | Select-Object -Property id + if ($sysPrepIds.Count -eq 0) { + throw "No Sysprep Customization spec found with Name: [$sysPrepName]" + } + $sysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id + + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = $sysprepCustomizationSettings + $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings } - $sysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.AdContainer = $adContainerId - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = $sysprepCustomizationSettings - - $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings } return $customObject } @@ -5658,10 +5800,10 @@ function Set-HVPool { function Start-HVFarm { <# .SYNOPSIS - Perform maintenance tasks on the farm(s). + Performs maintenance tasks on the farm(s). .DESCRIPTION - This function is used to perform maintenance tasks like enable/disable, start/stop and recompose the farm. + This function is used to perform maintenance tasks like enable/disable, start/stop and recompose the farm. This function is also used for scheduling maintenance operation on instant-clone farm(s). .PARAMETER Farm Name/Object(s) of the farm. Object(s) should be of type FarmSummaryView/FarmInfo. @@ -5669,16 +5811,23 @@ function Start-HVFarm { .PARAMETER Recompose Switch for recompose operation. Requests a recompose of RDS Servers in the specified 'AUTOMATED' farm. This marks the RDS Servers for recompose, which is performed asynchronously. +.PARAMETER ScheduleMaintenance + Switch for ScheduleMaintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. + +.PARAMETER CancelMaintenance + Switch for cancelling maintenance operation. Requests for cancelling a scheduled maintenance operation on the specified Instant clone farm. This stops further maintenance operation on the given farm. + .PARAMETER StartTime - Specifies when to start the operation. If unset, the operation will begin immediately. + Specifies when to start the recompose/scheduleMaintenance operation. If unset, the recompose operation will begin immediately. + For IMMEDIATE maintenance if unset, maintenance will begin immediately. For RECURRING maintenance if unset, will be calculated based on recurring maintenance configuration. If in the past, maintenance will begin immediately. .PARAMETER LogoffSetting Determines when to perform the operation on machines which have an active session. This property will be one of: - "FORCE_LOGOFF" - Users will be forced to log off when the system is ready to operate on their RDS Servers. Before being forcibly logged off, users may have a grace period in which to save their work (Global Settings). + "FORCE_LOGOFF" - Users will be forced to log off when the system is ready to operate on their RDS Servers. Before being forcibly logged off, users may have a grace period in which to save their work (Global Settings). This is the default value. "WAIT_FOR_LOGOFF" - Wait for connected users to disconnect before the task starts. The operation starts immediately on RDS Servers without active sessions. .PARAMETER StopOnFirstError - Indicates that the operation should stop on first error. + Indicates that the operation should stop on first error. Defaults to true. .PARAMETER Servers The RDS Server(s) id to recompose. Provide a comma separated list for multiple RDSServerIds. @@ -5692,6 +5841,30 @@ function Start-HVFarm { .PARAMETER Vcenter Virtual Center server-address (IP or FQDN) of the given farm. This should be same as provided to the Connection Server while adding the vCenter server. +.PARAMETER MaintenanceMode + The mode of schedule maintenance for Instant Clone Farm. This property will be one of: + "IMMEDIATE" - All server VMs will be refreshed once, immediately or at user scheduled time. + "RECURRING" - All server VMs will be periodically refreshed based on MaintenancePeriod and MaintenanceStartTime. + +.PARAMETER MaintenanceStartTime + Configured start time for the recurring maintenance. This property must be in the form hh:mm in 24 hours format. + +.PARAMETER MaintenancePeriod + This represents the frequency at which to perform recurring maintenance. This property will be one of: + "DAILY" - Daily recurring maintenance + "WEEKLY" - Weekly recurring maintenance + "MONTHLY" - Monthly recurring maintenance + +.PARAMETER StartInt + Start index for weekly or monthly maintenance. Weekly: 1-7 (Sun-Sat), Monthly: 1-31. + This property is required if maintenancePeriod is set to "WEEKLY"or "MONTHLY". + This property has values 1-7 for maintenancePeriod "WEEKLY". + This property has values 1-31 for maintenancePeriod "MONTHLY". + +.PARAMETER EveryInt + How frequently to repeat maintenance, expressed as a multiple of the maintenance period. e.g. Every 2 weeks. + This property has a default value of 1. This property has values 1-100. + .PARAMETER HvServer Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. @@ -5704,6 +5877,18 @@ function Start-HVFarm { $myTime = Get-Date '10/03/2016 12:30:00' Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime +.EXAMPLE + Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. + Start-HVFarm -Farm 'ICFarm-01' -ScheduleMaintenance -MaintenanceMode IMMEDIATE + +.EXAMPLE + Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. + Start-HVFarm -ScheduleMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' + +.EXAMPLE + Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. + Start-HVFarm -CancelMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING + .OUTPUTS None @@ -5729,28 +5914,61 @@ function Start-HVFarm { [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [switch]$Recompose, + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [switch]$ScheduleMaintenance, + + [Parameter(Mandatory = $false,ParameterSetName = 'CANCELMAINTENANCE')] + [switch]$CancelMaintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [System.DateTime]$StartTime, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateSet('FORCE_LOGOFF','WAIT_FOR_LOGOFF')] - [string]$LogoffSetting, + [string]$LogoffSetting = 'FORCE_LOGOFF', [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [boolean]$StopOnFirstError = $true, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [string []]$Servers, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$ParentVM, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$SnapshotVM, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$Vcenter, + [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'CANCELMAINTENANCE')] + [ValidateSet('IMMEDIATE','RECURRING')] + [string]$MaintenanceMode, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidatePattern('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$')] + [string]$MaintenanceStartTime, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateSet('DAILY','WEEKLY','MONTHLY')] + [string]$MaintenancePeriod, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateRange(1, 31)] + [int]$StartInt, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateRange(1, 100)] + [int]$EveryInt = 1, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -5773,23 +5991,26 @@ function Start-HVFarm { $id = $farm.id $name = $farm.data.name $type = $farm.type + $source = $farm.source } elseif ($farm.GetType().name -eq 'FarmSummaryView') { $id = $farm.id $name = $farm.data.name $type = $farm.data.type + $source = $farm.data.source } elseif ($farm.GetType().name -eq 'String') { try { - $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer + $farmSpecObj = Get-HVFarmSummary -farmName $farm -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($farmSpecObj) { $id = $farmSpecObj.id $name = $farmSpecObj.data.name $type = $farmSpecObj.data.type + $source = $farmSpecObj.data.source } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farm]" break @@ -5798,7 +6019,7 @@ function Start-HVFarm { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" break } - if ($type -eq 'AUTOMATED') { + if (!$source) { $source = 'VIEW_COMPOSER' } $farmList.Add($id,$name) @@ -5848,7 +6069,64 @@ function Start-HVFarm { Write-Host "Performed recompose task on farm: " $farmList.$item } } - } + 'SCHEDULEMAINTENANCE' { + if ($farmSource.$item -ne 'INSTANT_CLONE_ENGINE') { + Write-Error "SCHEDULEMAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." + break + } else { + $spec = New-Object VMware.Hv.FarmMaintenanceSpec + $spec.MaintenanceMode = $MaintenanceMode + $spec.ScheduledTime = $StartTime + if ($MaintenanceMode -eq "RECURRING") { + $spec.RecurringMaintenanceSettings = New-Object VMware.Hv.FarmRecurringMaintenanceSettings + $spec.RecurringMaintenanceSettings.MaintenancePeriod = $MaintenancePeriod + $spec.RecurringMaintenanceSettings.EveryInt = $EveryInt + if (!$MaintenanceStartTime) { + Write-Error "MaintenanceStartTime must be defined for MaintenanceMode = RECURRING." + break; + } else { + $spec.RecurringMaintenanceSettings.StartTime = $MaintenanceStartTime + } + if ($MaintenancePeriod -ne 'DAILY') { + if (!$StartInt) { + Write-Error "StartInt must be defined for MaintenancePeriod WEEKLY or MONTHLY." + break; + } else { + $spec.RecurringMaintenanceSettings.StartInt = $StartInt + } + } + } + #image settings are specified + if ($ParentVM -and $SnapshotVM) { + $spec.ImageMaintenanceSettings = New-Object VMware.Hv.FarmImageMaintenanceSettings + $spec.ImageMaintenanceSettings.LogoffSetting = $LogoffSetting + $spec.ImageMaintenanceSettings.StopOnFirstError = $StopOnFirstError + $vcId = Get-VcenterID -services $services -vCenter $Vcenter + if ($null -eq $vcId) { + Write-Error "VCenter is required if you specify ParentVM name." + break + } + try { + $spec.ImageMaintenanceSettings = Set-HVFarmSpec -vcId $vcId -spec $spec.ImageMaintenanceSettings + } catch { + Write-Error "SCHEDULEMAINTENANCE task failed with error: $_" + break + } + } + # call scheduleMaintenance service on farm + if ($pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_ScheduleMaintenance($services, $item, $spec) + Write-Host "Performed SCHEDULEMAINTENANCE task on farm: " $farmList.$item + } + } + } + 'CANCELMAINTENANCE' { + if ($pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_CancelScheduleMaintenance($services, $item, $MaintenanceMode) + Write-Host "Performed CancelMaintenance task on farm: " $farmList.$item + } + } + } return } } From ce4dab7df6c5285b738af1a38c823305548deef6 Mon Sep 17 00:00:00 2001 From: vkg1215 Date: Mon, 23 Jan 2017 12:15:03 +0530 Subject: [PATCH 29/90] Use Get-HVFarm instead of Get-HVFarmSummary Use Get-HVFarm instead of Get-HVFarmSummary --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index e5cb880..9e51390 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -6001,16 +6001,16 @@ function Start-HVFarm { } elseif ($farm.GetType().name -eq 'String') { try { - $farmSpecObj = Get-HVFarmSummary -farmName $farm -hvServer $hvServer + $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer } catch { - Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" break } if ($farmSpecObj) { $id = $farmSpecObj.id $name = $farmSpecObj.data.name - $type = $farmSpecObj.data.type - $source = $farmSpecObj.data.source + $type = $farmSpecObj.type + $source = $farmSpecObj.source } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farm]" break From 142e6361efcf9bb114889b1c65eb689502827356 Mon Sep 17 00:00:00 2001 From: vkg1215 Date: Mon, 23 Jan 2017 13:06:33 +0530 Subject: [PATCH 30/90] remove unused variable, and gave better name to farm operations Renamed SCHEDULEDMAINTENANCE operation to SCHEDULED_MAINTENANCE and CANCELMAINTENANCE to CANCEL_MAINTENANCE --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 9e51390..c0ffe49 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -2376,8 +2376,7 @@ function New-HVFarm { $LinkedClone = $true $DomainAdmin = $jsonObject.AutomatedFarmSpec.CustomizationSettings.domainAdministrator $reusePreExistingAccounts = $jsonObject.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts - $sysprepCustomizationSettings = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings - $sysPrepName = $sysprepCustomizationSettings.CustomizationSpec + $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec } $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod @@ -5811,14 +5810,14 @@ function Start-HVFarm { .PARAMETER Recompose Switch for recompose operation. Requests a recompose of RDS Servers in the specified 'AUTOMATED' farm. This marks the RDS Servers for recompose, which is performed asynchronously. -.PARAMETER ScheduleMaintenance - Switch for ScheduleMaintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. +.PARAMETER Schedule_Maintenance + Switch for Schedule_Maintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. -.PARAMETER CancelMaintenance +.PARAMETER Cancel_Maintenance Switch for cancelling maintenance operation. Requests for cancelling a scheduled maintenance operation on the specified Instant clone farm. This stops further maintenance operation on the given farm. .PARAMETER StartTime - Specifies when to start the recompose/scheduleMaintenance operation. If unset, the recompose operation will begin immediately. + Specifies when to start the recompose/Schedule_Maintenance operation. If unset, the recompose operation will begin immediately. For IMMEDIATE maintenance if unset, maintenance will begin immediately. For RECURRING maintenance if unset, will be calculated based on recurring maintenance configuration. If in the past, maintenance will begin immediately. .PARAMETER LogoffSetting @@ -5878,16 +5877,16 @@ function Start-HVFarm { Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime .EXAMPLE - Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. - Start-HVFarm -Farm 'ICFarm-01' -ScheduleMaintenance -MaintenanceMode IMMEDIATE + Requests a Schedule_Maintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. + Start-HVFarm -Farm 'ICFarm-01' -Schedule_Maintenance -MaintenanceMode IMMEDIATE .EXAMPLE - Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. - Start-HVFarm -ScheduleMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' + Requests a Schedule_Maintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. + Start-HVFarm -Schedule_Maintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' .EXAMPLE - Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. - Start-HVFarm -CancelMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING + Requests a Cancel_Maintenance task for instant-clone farm. Cancels recurring maintenance. + Start-HVFarm -Cancel_Maintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING .OUTPUTS None @@ -5914,58 +5913,58 @@ function Start-HVFarm { [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [switch]$Recompose, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] - [switch]$ScheduleMaintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [switch]$Schedule_Maintenance, - [Parameter(Mandatory = $false,ParameterSetName = 'CANCELMAINTENANCE')] - [switch]$CancelMaintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'CANCEL_MAINTENANCE')] + [switch]$Cancel_Maintenance, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [System.DateTime]$StartTime, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [ValidateSet('FORCE_LOGOFF','WAIT_FOR_LOGOFF')] [string]$LogoffSetting = 'FORCE_LOGOFF', [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [boolean]$StopOnFirstError = $true, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [string []]$Servers, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [string]$ParentVM, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [string]$SnapshotVM, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [string]$Vcenter, - [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULEMAINTENANCE')] - [Parameter(Mandatory = $true,ParameterSetName = 'CANCELMAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'CANCEL_MAINTENANCE')] [ValidateSet('IMMEDIATE','RECURRING')] [string]$MaintenanceMode, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [ValidatePattern('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$')] [string]$MaintenanceStartTime, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [ValidateSet('DAILY','WEEKLY','MONTHLY')] [string]$MaintenancePeriod, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [ValidateRange(1, 31)] [int]$StartInt, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] [ValidateRange(1, 100)] [int]$EveryInt = 1, @@ -6069,9 +6068,9 @@ function Start-HVFarm { Write-Host "Performed recompose task on farm: " $farmList.$item } } - 'SCHEDULEMAINTENANCE' { + 'SCHEDULE_MAINTENANCE' { if ($farmSource.$item -ne 'INSTANT_CLONE_ENGINE') { - Write-Error "SCHEDULEMAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." + Write-Error "SCHEDULE_MAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." break } else { $spec = New-Object VMware.Hv.FarmMaintenanceSpec @@ -6109,21 +6108,21 @@ function Start-HVFarm { try { $spec.ImageMaintenanceSettings = Set-HVFarmSpec -vcId $vcId -spec $spec.ImageMaintenanceSettings } catch { - Write-Error "SCHEDULEMAINTENANCE task failed with error: $_" + Write-Error "SCHEDULE_MAINTENANCE task failed with error: $_" break } } # call scheduleMaintenance service on farm if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_ScheduleMaintenance($services, $item, $spec) - Write-Host "Performed SCHEDULEMAINTENANCE task on farm: " $farmList.$item + Write-Host "Performed SCHEDULE_MAINTENANCE task on farm: " $farmList.$item } } } - 'CANCELMAINTENANCE' { + 'CANCEL_MAINTENANCE' { if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_CancelScheduleMaintenance($services, $item, $MaintenanceMode) - Write-Host "Performed CancelMaintenance task on farm: " $farmList.$item + Write-Host "Performed CANCEL_MAINTENANCE task on farm: " $farmList.$item } } } From a15c61cedfc25c28f7c23d6c81eaeb9fd89b49ac Mon Sep 17 00:00:00 2001 From: vkg1215 Date: Mon, 23 Jan 2017 14:36:26 +0530 Subject: [PATCH 31/90] Following code convention. do not name variables having _ Renamed SCHEDULEDMAINTENANCE operation to SCHEDULED_MAINTENANCE and CANCELMAINTENANCE to CANCEL_MAINTENANCE --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index c0ffe49..b7f272a 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1941,7 +1941,7 @@ function New-HVFarm { New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false .EXAMPLE - Creates new linkedClone farm by using json file + Creates new instantClone farm by using json file New-HVFarm -Spec C:\VMWare\Specs\InstantCloneFarm.json -Confirm:$false .EXAMPLE @@ -5810,14 +5810,14 @@ function Start-HVFarm { .PARAMETER Recompose Switch for recompose operation. Requests a recompose of RDS Servers in the specified 'AUTOMATED' farm. This marks the RDS Servers for recompose, which is performed asynchronously. -.PARAMETER Schedule_Maintenance - Switch for Schedule_Maintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. +.PARAMETER ScheduleMaintenance + Switch for ScheduleMaintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. -.PARAMETER Cancel_Maintenance +.PARAMETER CancelMaintenance Switch for cancelling maintenance operation. Requests for cancelling a scheduled maintenance operation on the specified Instant clone farm. This stops further maintenance operation on the given farm. .PARAMETER StartTime - Specifies when to start the recompose/Schedule_Maintenance operation. If unset, the recompose operation will begin immediately. + Specifies when to start the recompose/ScheduleMaintenance operation. If unset, the recompose operation will begin immediately. For IMMEDIATE maintenance if unset, maintenance will begin immediately. For RECURRING maintenance if unset, will be calculated based on recurring maintenance configuration. If in the past, maintenance will begin immediately. .PARAMETER LogoffSetting @@ -5877,16 +5877,16 @@ function Start-HVFarm { Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime .EXAMPLE - Requests a Schedule_Maintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. - Start-HVFarm -Farm 'ICFarm-01' -Schedule_Maintenance -MaintenanceMode IMMEDIATE + Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. + Start-HVFarm -Farm 'ICFarm-01' -ScheduleMaintenance -MaintenanceMode IMMEDIATE .EXAMPLE - Requests a Schedule_Maintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. - Start-HVFarm -Schedule_Maintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' + Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. + Start-HVFarm -ScheduleMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' .EXAMPLE - Requests a Cancel_Maintenance task for instant-clone farm. Cancels recurring maintenance. - Start-HVFarm -Cancel_Maintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING + Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. + Start-HVFarm -CancelMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING .OUTPUTS None @@ -5913,58 +5913,58 @@ function Start-HVFarm { [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [switch]$Recompose, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] - [switch]$Schedule_Maintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [switch]$ScheduleMaintenance, - [Parameter(Mandatory = $false,ParameterSetName = 'CANCEL_MAINTENANCE')] - [switch]$Cancel_Maintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'CANCELMAINTENANCE')] + [switch]$CancelMaintenance, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [System.DateTime]$StartTime, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateSet('FORCE_LOGOFF','WAIT_FOR_LOGOFF')] [string]$LogoffSetting = 'FORCE_LOGOFF', [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [boolean]$StopOnFirstError = $true, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [string []]$Servers, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$ParentVM, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$SnapshotVM, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$Vcenter, - [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULE_MAINTENANCE')] - [Parameter(Mandatory = $true,ParameterSetName = 'CANCEL_MAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'CANCELMAINTENANCE')] [ValidateSet('IMMEDIATE','RECURRING')] [string]$MaintenanceMode, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidatePattern('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$')] [string]$MaintenanceStartTime, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateSet('DAILY','WEEKLY','MONTHLY')] [string]$MaintenancePeriod, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateRange(1, 31)] [int]$StartInt, - [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULE_MAINTENANCE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateRange(1, 100)] [int]$EveryInt = 1, @@ -6068,9 +6068,9 @@ function Start-HVFarm { Write-Host "Performed recompose task on farm: " $farmList.$item } } - 'SCHEDULE_MAINTENANCE' { + 'SCHEDULEMAINTENANCE' { if ($farmSource.$item -ne 'INSTANT_CLONE_ENGINE') { - Write-Error "SCHEDULE_MAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." + Write-Error "SCHEDULEMAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." break } else { $spec = New-Object VMware.Hv.FarmMaintenanceSpec @@ -6108,21 +6108,21 @@ function Start-HVFarm { try { $spec.ImageMaintenanceSettings = Set-HVFarmSpec -vcId $vcId -spec $spec.ImageMaintenanceSettings } catch { - Write-Error "SCHEDULE_MAINTENANCE task failed with error: $_" + Write-Error "SCHEDULEMAINTENANCE task failed with error: $_" break } } # call scheduleMaintenance service on farm if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_ScheduleMaintenance($services, $item, $spec) - Write-Host "Performed SCHEDULE_MAINTENANCE task on farm: " $farmList.$item + Write-Host "Performed SCHEDULEMAINTENANCE task on farm: " $farmList.$item } } } - 'CANCEL_MAINTENANCE' { + 'CANCELMAINTENANCE' { if ($pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_CancelScheduleMaintenance($services, $item, $MaintenanceMode) - Write-Host "Performed CANCEL_MAINTENANCE task on farm: " $farmList.$item + Write-Host "Performed CANCELMAINTENANCE task on farm: " $farmList.$item } } } From 307f3d29978c80666797f8a8e3c4a8fa83f7d203 Mon Sep 17 00:00:00 2001 From: William Lam Date: Mon, 23 Jan 2017 08:00:33 -0800 Subject: [PATCH 32/90] First VAMI function Get-VAMISummary --- Modules/VAMI.psm1 | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Modules/VAMI.psm1 diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 new file mode 100644 index 0000000..abdf4a3 --- /dev/null +++ b/Modules/VAMI.psm1 @@ -0,0 +1,36 @@ +Function Get-VAMISummary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves some basic information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return basic VAMI summary info + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMISummary +#> + $systemVersionAPI = Get-CisService -Name 'com.vmware.appliance.system.version' + $results = $systemVersionAPI.get() | select product, type, version, build, install_time + + $systemUptimeAPI = Get-CisService -Name 'com.vmware.appliance.system.uptime' + $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) + $uptime = $ts.ToString("hh\:mm\:ss\,fff") + + $summaryResult = "" | Select Product, Type, Version, Build, InstallTime, Uptime + $summaryResult.Product = $results.product + $summaryResult.Type = $results.type + $summaryResult.Version = $results.version + $summaryResult.Build = $results.build + $summaryResult.InstallTime = $results.install_time + $summaryResult.Uptime = $uptime + + $summaryResult +} \ No newline at end of file From aae3d54c68c72bf9393710ba8905b435dada600b Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 24 Jan 2017 17:39:20 +0530 Subject: [PATCH 33/90] Remove overhead of passing -Confirm:$false as extra parameter Now default behavior will be no pop-up will be shown for confirmation --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 72 ++++++++++++------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index b7f272a..6c94206 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -54,6 +54,17 @@ function Get-ViewAPIService { return $null } +function Get-HVConfirmFlag { + Param( + [Parameter(Mandatory = $true)] + $keys + ) + if (($keys -contains 'Confirm') -or ($keys -contains 'WhatIf')) { + return $true + } + return $false +} + function Get-VcenterID { param( [Parameter(Mandatory = $true)] @@ -237,6 +248,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys try { $desktopPool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { @@ -294,7 +306,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A return } } - if ($pscmdlet.ShouldProcess($machines)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($machines)) { $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) } return $machineList @@ -409,6 +421,7 @@ function Add-HVRDSServer { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys try { $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer } catch { @@ -432,7 +445,7 @@ function Add-HVRDSServer { 'MANUAL' { try { $serverList = Get-RegisteredRDSServer -services $services -serverList $rdsServers - if ($pscmdlet.ShouldProcess($rdsServers)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($rdsServers)) { $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) } return $serverList @@ -2321,7 +2334,7 @@ function New-HVFarm { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($farmName) { try { $sourceFarm = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer @@ -2630,7 +2643,7 @@ function New-HVFarm { $myDebug | out-file -filepath c:\temp\copiedfarm.json #> - if ($pscmdlet.ShouldProcess($farmSpecObj.data.name)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmSpecObj.data.name)) { $Id = $farm_service_helper.Farm_Create($services, $farmSpecObj) } else { try { @@ -4016,7 +4029,7 @@ function New-HVPool { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($poolName) { try { $sourcePool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer @@ -4627,7 +4640,7 @@ function New-HVPool { $myDebug | out-file -filepath c:\temp\copieddesktop.json #> $desktop_helper = New-Object VMware.Hv.DesktopService - if ($pscmdlet.ShouldProcess($desktopSpecObj.base.name)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($desktopSpecObj.base.name)) { $id = $desktop_helper.Desktop_create($services,$desktopSpecObj) } else { try { @@ -5201,6 +5214,7 @@ function Remove-HVFarm { } } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $farmList = @() if ($farmName) { try { @@ -5231,7 +5245,7 @@ function Remove-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList) { - if ($pscmdlet.ShouldProcess($item.Name)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.Name)) { $farm_service_helper.Farm_Delete($services, $item.id) } Write-Host "Farm Deleted: " $item.Name @@ -5323,6 +5337,7 @@ function Remove-HVPool { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $poolList = @() if ($poolName) { try { @@ -5375,7 +5390,7 @@ function Remove-HVPool { } } Write-Host "Deleting Pool: " $item.Name - if ($pscmdlet.ShouldProcess($item.Name)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.Name)) { $desktop_service_helper.Desktop_Delete($services,$item.id,$deleteSpec) } } @@ -5503,6 +5518,7 @@ function Set-HVFarm { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $farmList = @{} if ($farmName) { try { @@ -5575,7 +5591,7 @@ function Set-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList.Keys) { - if ($pscmdlet.ShouldProcess($farmList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_Update($services,$item,$updates) } Write-Host "Update successful for farm: " $farmList.$item @@ -5711,6 +5727,7 @@ function Set-HVPool { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $poolList = @{} if ($poolName) { try { @@ -5784,7 +5801,7 @@ function Set-HVPool { } $desktop_helper = New-Object VMware.Hv.DesktopService foreach ($item in $poolList.Keys) { - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } } @@ -5981,6 +5998,7 @@ function Start-HVFarm { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $farmList = @{} $farmType = @{} $farmSource = @{} @@ -6061,7 +6079,7 @@ function Start-HVFarm { $updates = @() $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - if ($pscmdlet.ShouldProcess($farmList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_Update($services,$item,$updates) $farm_service_helper.Farm_Recompose($services,$item,$spec) } @@ -6113,14 +6131,14 @@ function Start-HVFarm { } } # call scheduleMaintenance service on farm - if ($pscmdlet.ShouldProcess($farmList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_ScheduleMaintenance($services, $item, $spec) Write-Host "Performed SCHEDULEMAINTENANCE task on farm: " $farmList.$item } } } 'CANCELMAINTENANCE' { - if ($pscmdlet.ShouldProcess($farmList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { $farm_service_helper.Farm_CancelScheduleMaintenance($services, $item, $MaintenanceMode) Write-Host "Performed CANCELMAINTENANCE task on farm: " $farmList.$item } @@ -6353,7 +6371,7 @@ function Start-HVPool { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $poolList = @{} $poolType = @{} $poolSource = @{} @@ -6406,7 +6424,7 @@ function Start-HVPool { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRebalanceSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Rebalance($services,$item,$spec) } Write-Host "Performed rebalance task on Pool: " $PoolList.$item @@ -6416,7 +6434,7 @@ function Start-HVPool { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRefreshSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Refresh($services,$item,$spec) } Write-Host "Performed refresh task on Pool: " $PoolList.$item @@ -6435,7 +6453,7 @@ function Start-HVPool { $updates = @() $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } Write-Host "Performed recompose task on Pool: " $PoolList.$item @@ -6453,7 +6471,7 @@ function Start-HVPool { $spec.Settings.LogoffSetting = $logoffSetting $spec.Settings.StopOnFirstError = $stopOnFirstError if ($startTime) { $spec.Settings.startTime = $startTime } - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) } Write-Host "Performed push_image task on Pool: " $PoolList.$item @@ -6464,7 +6482,7 @@ function Start-HVPool { Write-Error "$poolList.$item is not a INSTANT CLONE pool" break } else { - if ($pscmdlet.ShouldProcess($poolList.$item)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) } Write-Host "Performed cancel_push_image task on Pool: " $PoolList.$item @@ -7360,6 +7378,7 @@ function New-HVEntitlement { } } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $userInfo = Get-UserInfo -UserName $User $UserOrGroupName = $userInfo.Name $Domain = $userInfo.Domain @@ -7490,7 +7509,7 @@ function New-HVEntitlement { $base.UserOrGroup = $results.id foreach ($ResourceObj in $ResourceObjs) { $base.Resource = $ResourceObj.id - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $id = $services.UserEntitlement.UserEntitlement_Create($base) } } @@ -7802,6 +7821,7 @@ function Remove-HVEntitlement { } } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $AndFilter = @() $results = $null $userInfo = Get-UserInfo -UserName $User @@ -7827,7 +7847,7 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.localData.desktopUserEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } Write-Host $userEntitlements.Length " desktopUserEntitlement(s) are removed for UserOrGroup " $user @@ -7847,7 +7867,7 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.localData.applicationUserEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } Write-Host $userEntitlements.Length " applicationUserEntitlement(s) are removed for UserOrGroup " $user @@ -7877,12 +7897,12 @@ function Remove-HVEntitlement { foreach ($result in $Results) { if ($result.GetType().Name -eq 'EntitledUserOrGroupLocalSummaryView') { $userEntitlements = $result.localData.urlRedirectionUserEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } } else { $userEntitlements = $result.globalData.urlRedirectionUserEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } } @@ -7907,7 +7927,7 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.globalData.globalUserApplicationEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } Write-Host $userEntitlements.Length " GlobalApplicationEntitlement(s) are removed for UserOrGroup " $user @@ -7931,7 +7951,7 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.globalData.globalUserEntitlements - if ($pscmdlet.ShouldProcess($User)) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } Write-Host $userEntitlements.Length " GlobalEntitlement(s) are removed for UserOrGroup " $user From e8298afe3a8047a7673cc37804ac81623c0d79cd Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 25 Jan 2017 05:54:32 -0800 Subject: [PATCH 34/90] Adding Get-VAMIHealth function --- Modules/VAMI.psm1 | 57 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index abdf4a3..6c8c835 100644 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -2,15 +2,15 @@ <# .NOTES =========================================================================== - Created by: William Lam + Created by: William Lam Date: Jan 20, 2016 - Organization: VMware + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== .SYNOPSIS This function retrieves some basic information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION Function to return basic VAMI summary info .EXAMPLE @@ -33,4 +33,53 @@ $summaryResult.Uptime = $uptime $summaryResult -} \ No newline at end of file +} + +Function Get-VAMIHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves health information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI health + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIHealth +#> + $healthOverall = (Get-CisService -Name 'com.vmware.appliance.health.system').get() + $healthLastCheck = (Get-CisService -Name 'com.vmware.appliance.health.system').lastcheck() + $healthCPU = (Get-CisService -Name 'com.vmware.appliance.health.load').get() + $healthMem = (Get-CisService -Name 'com.vmware.appliance.health.mem').get() + $healthSwap = (Get-CisService -Name 'com.vmware.appliance.health.swap').get() + $healthStorage = (Get-CisService -Name 'com.vmware.appliance.health.storage').get() + + # DB health only applicable for Embedded/External VCSA Node + $vami = (Get-CisService -Name 'com.vmware.appliance.system.version').get() + + if($vami.type -eq "vCenter Server with an embedded Platform Services Controller" -or $vami.type -eq "vCenter Server with an external Platform Services Controller") { + $healthVCDB = (Get-CisService -Name 'com.vmware.appliance.health.databasestorage').get() + } else { + $healthVCDB = "N/A" + } + $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() + + $healthResult = "" | Select HealthOverall, HealthLastCheck, HealthCPU, HealthMem, HealthSwap, HealthStorage, HealthVCDB, HealthSoftware + $healthResult.HealthOverall = $healthOverall + $healthResult.HealthLastCheck = $healthLastCheck + $healthResult.HealthCPU = $healthCPU + $healthResult.HealthMem = $healthMem + $healthResult.HealthSwap = $healthSwap + $healthResult.HealthStorage = $healthStorage + $healthResult.HealthVCDB = $healthVCDB + $healthResult.HealthSoftware = $healthSoftwareUpdates + + $healthResult +} From 358337db74693a1c9d7ea4c668482b6af8325c44 Mon Sep 17 00:00:00 2001 From: William Lam Date: Thu, 26 Jan 2017 06:25:56 -0800 Subject: [PATCH 35/90] Adding Get-VAMIAccess function --- Modules/VAMI.psm1 | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) mode change 100644 => 100755 Modules/VAMI.psm1 diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 old mode 100644 new mode 100755 index 6c8c835..3272e6d --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -83,3 +83,36 @@ Function Get-VAMIHealth { $healthResult } + +Function Get-VAMIAccess { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 25, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves access information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + $consoleAccess = (Get-CisService -Name 'com.vmware.appliance.access.consolecli').get() + $dcuiAccess = (Get-CisService -Name 'com.vmware.appliance.access.dcui').get() + $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() + $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() + + $accessResult = "" | Select Console, DCUI, BashShell, SSH + $accessResult.Console = $consoleAccess + $accessResult.DCUI = $dcuiAccess + $accessResult.BashShell = $shellAccess.enabled + $accessResult.SSH = $sshAccess + + $accessResult +} \ No newline at end of file From 3f8d35a92aecb3ccd51cd9480def8246c929ee4f Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 27 Jan 2017 10:54:16 +0530 Subject: [PATCH 36/90] NetBiosName Fix making netbiosname as optional. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 6c94206..5cf780e 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -2939,11 +2939,15 @@ function Get-HVFarmCustomizationSetting { if ($InstantClone) { $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) | Where-Object { $_.namesData.dnsName -match $netBiosName }) + $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) + $instantCloneEngineDomainAdministrator = ($insDomainAdministrators | Where-Object { $_.namesData.dnsName -match $netBiosName }) if (![string]::IsNullOrWhitespace($domainAdmin)) { $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id - } elseIf ($null -ne $instantCloneEngineDomainAdministrator) { + } + If ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id + } elseif ($null -ne $insDomainAdministrators) { + $instantCloneEngineDomainAdministrator = $insDomainAdministrators[0].id } if ($null -eq $instantCloneEngineDomainAdministrator) { throw "No Instant Clone Engine Domain Administrator found with netBiosName: [$netBiosName]" @@ -4903,11 +4907,15 @@ function Get-HVPoolCustomizationSetting { if ($InstantClone) { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) | Where-Object { $_.namesData.dnsName -match $netBiosName }) + $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) + $instantCloneEngineDomainAdministrator = ($insDomainAdministrators | Where-Object { $_.namesData.dnsName -match $netBiosName }) if (![string]::IsNullOrWhitespace($domainAdmin)) { $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id - } elseIf ($null -ne $instantCloneEngineDomainAdministrator) { + } + If ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id + } elseif ($null -ne $insDomainAdministrators) { + $instantCloneEngineDomainAdministrator = $insDomainAdministrators[0].id } if ($null -eq $instantCloneEngineDomainAdministrator) { throw "No Instant Clone Engine Domain Administrator found with netBiosName: [$netBiosName]" @@ -6093,7 +6101,9 @@ function Start-HVFarm { } else { $spec = New-Object VMware.Hv.FarmMaintenanceSpec $spec.MaintenanceMode = $MaintenanceMode - $spec.ScheduledTime = $StartTime + if ($startTime) { + $spec.ScheduledTime = $StartTime + } if ($MaintenanceMode -eq "RECURRING") { $spec.RecurringMaintenanceSettings = New-Object VMware.Hv.FarmRecurringMaintenanceSettings $spec.RecurringMaintenanceSettings.MaintenancePeriod = $MaintenancePeriod From fd13b0b16d4e5ed7d24a56c7eeaceb67cf6cc99d Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 27 Jan 2017 14:32:21 +0530 Subject: [PATCH 37/90] Mixed mode changes Use few parameters from json file and few parameters from command line to create new pool or farm. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 93 +++++++++++++++---- 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 5cf780e..7a0741d 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1961,6 +1961,10 @@ function New-HVFarm { Creates new manual farm by using rdsServers names New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" -Confirm:$false +.EXAMPLE + Creates new instant clone farm by reading few parameters from json and few parameters from command line. + New-HVFarm -Spec C:\VMWare\Specs\AutomatedInstantCloneFarm.json -FarmName 'InsPool' -NamingPattern 'InsFarm-' + .OUTPUTS None @@ -1997,6 +2001,7 @@ function New-HVFarm { [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = "INSTANT_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $FarmName, @@ -2172,6 +2177,7 @@ function New-HVFarm { #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.namingPattern if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $NamingPattern = $farmName + '{n:fixed=4}', @@ -2393,7 +2399,9 @@ function New-HVFarm { } $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod - $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern + if (! $NamingPattern) { + $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern + } $maximumCount = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers $enableProvisioning = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.EnableProvisioning $stopProvisioningOnError = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError @@ -2461,7 +2469,9 @@ function New-HVFarm { $farmDisplayName = $jsonObject.Data.DisplayName $description = $jsonObject.Data.Description $accessGroup = $jsonObject.Data.AccessGroup - $farmName = $jsonObject.Data.name + if (! $FarmName) { + $farmName = $jsonObject.Data.name + } if ($null -ne $jsonObject.Data.Enabled) { $enable = $jsonObject.Data.Enabled } @@ -2940,10 +2950,16 @@ function Get-HVFarmCustomizationSetting { $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) - $instantCloneEngineDomainAdministrator = ($insDomainAdministrators | Where-Object { $_.namesData.dnsName -match $netBiosName }) - if (![string]::IsNullOrWhitespace($domainAdmin)) { - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.namesData.dnsName -match $netBiosName' } + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $strFilterSet += '$_.base.userName -eq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $instantCloneEngineDomainAdministrator = $insDomainAdministrators | Where $scriptBlock If ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id } elseif ($null -ne $insDomainAdministrators) { @@ -2961,11 +2977,21 @@ function Get-HVFarmCustomizationSetting { $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings } elseif ($LinkedClone) { $ViewComposerDomainAdministrator_service_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) | Where-Object { $_.base.domain -match $netBiosName }) - if (! [string]::IsNullOrWhitespace($domainAdmin)) { - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } elseif ($null -ne $ViewComposerDomainAdministratorID) { + $lcDomainAdministrators = $ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.base.domain -match $netBiosName' + } + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $strFilterSet += '$_.base.userName -ieq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $ViewComposerDomainAdministratorID = $lcDomainAdministrators | Where $scriptBlock + if ($null -ne $ViewComposerDomainAdministratorID) { $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + } elseif ($null -ne $lcDomainAdministrators) { + $ViewComposerDomainAdministratorID = $lcDomainAdministrators[0].id } if ($null -eq $ViewComposerDomainAdministratorID) { throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" @@ -3393,6 +3419,10 @@ function New-HVPool { Create new unmanaged manual pool from unmanaged VirtualMachines. New-HVPool -MANUAL -PoolName 'unmangedVMWare' -PoolDisplayName 'unMngPl' -Description 'unmanaged Manual Pool creation' -UserAssignment FLOATING -Source UNMANAGED -VM 'myphysicalmachine.vmware.com' +.EXAMPLE + Creates new instant clone pool by reading few parameters from json and few parameters from command line. + New-HVPool -spec 'C:\Json\InstantClone.json' -PoolName 'InsPool1'-NamingPattern 'INSPool-' + .OUTPUTS None @@ -3447,6 +3477,7 @@ function New-HVPool { [Parameter(Mandatory = $true,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $true,ParameterSetName = 'RDS')] [Parameter(Mandatory = $true,ParameterSetName = 'CLONED_POOL')] + [Parameter(Mandatory = $true,ParameterSetName = 'JSON_FILE')] [string] $PoolName, @@ -3722,14 +3753,17 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $RedirectWindowsProfile = $true, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.useSeparateDatastoresPersistentAndOSDisks if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [boolean] $UseSeparateDatastoresPersistentAndOSDisks = $false, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [string[]] $PersistentDiskDatastores, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [string[]] @@ -3827,6 +3861,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] + [Parameter(Mandatory = $true,ParameterSetName = 'JSON_FILE')] [string] $NamingPattern = $poolName + '{n:fixed=4}', @@ -4112,7 +4147,9 @@ function New-HVPool { $namingMethod = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod $transparentPageSharingScope = $jsonObject.AutomatedDesktopSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope if ($namingMethod -eq "PATTERN") { - $namingPattern = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.namingPattern + if (!$namingPattern) { + $namingPattern = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.namingPattern + } $maximumCount = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.maxNumberOfMachines $spareCount = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.numberOfSpareMachines $provisioningTime = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.provisioningTime @@ -4232,7 +4269,9 @@ function New-HVPool { $poolDisplayName = $jsonObject.base.DisplayName $description = $jsonObject.base.Description $accessGroup = $jsonObject.base.AccessGroup - $poolName = $jsonObject.base.name + if (!$poolName) { + $poolName = $jsonObject.base.name + } <# # Populate desktop settings @@ -4908,10 +4947,16 @@ function Get-HVPoolCustomizationSetting { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) - $instantCloneEngineDomainAdministrator = ($insDomainAdministrators | Where-Object { $_.namesData.dnsName -match $netBiosName }) - if (![string]::IsNullOrWhitespace($domainAdmin)) { - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.namesData.dnsName -match $netBiosName' } + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $strFilterSet += '$_.base.userName -eq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $instantCloneEngineDomainAdministrator = $insDomainAdministrators | Where $scriptBlock If ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id } elseif ($null -ne $insDomainAdministrators) { @@ -4930,14 +4975,24 @@ function Get-HVPoolCustomizationSetting { else { if ($LinkedClone) { $viewComposerDomainAdministrator_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService - $ViewComposerDomainAdministratorID = ($viewComposerDomainAdministrator_helper.ViewComposerDomainAdministrator_List($services,$vcID) | Where-Object { $_.base.domain -match $netBiosName }) + $lcDomainAdministrators = $viewComposerDomainAdministrator_helper.ViewComposerDomainAdministrator_List($services,$vcID) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.base.domain -match $netBiosName' + } if (![string]::IsNullOrWhitespace($domainAdmin)) { - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } elseIf ($null -ne $ViewComposerDomainAdministratorID) { - $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + $strFilterSet += '$_.base.userName -ieq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $ViewComposerDomainAdministratorID = $lcDomainAdministrators | Where $scriptBlock + If ($null -ne $ViewComposerDomainAdministratorID) { + $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + } elseif ($null -ne $lcDomainAdministrators) { + $ViewComposerDomainAdministratorID = $lcDomainAdministrators[0].id } if ($null -eq $ViewComposerDomainAdministratorID) { - throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" + throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" } if ($custType -eq 'SYS_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' From c5de6447f6c9ceb7847cc837252bc63b9baf6191 Mon Sep 17 00:00:00 2001 From: William Lam Date: Fri, 27 Jan 2017 09:48:52 -0800 Subject: [PATCH 38/90] Adding Get-VAMITime function --- Modules/VAMI.psm1 | 48 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 3272e6d..37e54f3 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -88,9 +88,9 @@ Function Get-VAMIAccess { <# .NOTES =========================================================================== - Created by: William Lam + Created by: William Lam Date: Jan 25, 2016 - Organization: VMware + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== @@ -115,4 +115,48 @@ Function Get-VAMIAccess { $accessResult.SSH = $sshAccess $accessResult +} + +Function Get-VAMITime { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 27, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the time and NTP info from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return current Time and NTP information + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMITime +#> + $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' + $timeResults = $systemTimeAPI.get() + + $timeResult = "" | Select Timezone, Date, CurrentTime, Mode, NTPServers, NTPStatus + $timeResult.Timezone = $timeResults.timezone + $timeResult.Date = $timeResults.date + $timeResult.CurrentTime = $timeResults.time + + $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() + $timeSyncMode = $timeSync.mode + + $timeResult.Mode = $timeSyncMode + + if($timeSyncMode -eq "NTP") { + $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() + $timeResult.NTPServers = $ntpServers.servers + $timeResult.NTPStatus = $ntpServers.status + } else { + $timeResult.NTPServers = "N/A" + $timeResult.NTPStatus = "N/A" + } + + $timeResult } \ No newline at end of file From 860b62df5b69fd45144c9220f89eacd1d224a587 Mon Sep 17 00:00:00 2001 From: William Lam Date: Fri, 27 Jan 2017 09:50:49 -0800 Subject: [PATCH 39/90] Fixing spaces --- Modules/VAMI.psm1 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 37e54f3..515b274 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -2,9 +2,9 @@ <# .NOTES =========================================================================== - Created by: William Lam + Created by: William Lam Date: Jan 20, 2016 - Organization: VMware + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== @@ -39,9 +39,9 @@ Function Get-VAMIHealth { <# .NOTES =========================================================================== - Created by: William Lam - Date: Jan 20, 2016 - Organization: VMware + Created by: William Lam + Date: Jan 25, 2016 + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== @@ -88,9 +88,9 @@ Function Get-VAMIAccess { <# .NOTES =========================================================================== - Created by: William Lam - Date: Jan 25, 2016 - Organization: VMware + Created by: William Lam + Date: Jan 26, 2016 + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== @@ -121,9 +121,9 @@ Function Get-VAMITime { <# .NOTES =========================================================================== - Created by: William Lam + Created by: William Lam Date: Jan 27, 2016 - Organization: VMware + Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== From f595e7e67411c87f45f1d43528c65f48bcc2d33f Mon Sep 17 00:00:00 2001 From: William Lam Date: Tue, 31 Jan 2017 15:13:56 -0800 Subject: [PATCH 40/90] Adding Get-VAMINetwork function --- Modules/VAMI.psm1 | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 515b274..16abc76 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -159,4 +159,54 @@ Function Get-VAMITime { } $timeResult +} + +Function Get-VAMINetwork { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 31, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves access information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMINetwork +#> + $netResults = @() + + $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() + $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() + + Write-Host "Hostname: " $hostname + Write-Host "DNS Servers: " $dns.servers + + $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() + foreach ($interface in $interfaces) { + $interfaceResult = "" | Select Inteface, MAC, Status, Mode, IP, Prefix, Gateway, Updateable + + $interfaceResult.Inteface = $interface.name + $interfaceResult.MAC = $interface.mac + $interfaceResult.Status = $interface.status + + $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') + $spec = $ipv4API.Help.get.interfaces.CreateExample() + $spec+= $interface.name + $ipv4result = $ipv4API.get($spec) + + $interfaceResult.Mode = $ipv4result.mode + $interfaceResult.IP = $ipv4result.address + $interfaceResult.Prefix = $ipv4result.prefix + $interfaceResult.Gateway = $ipv4result.default_gateway + $interfaceResult.Updateable = $ipv4result.updateable + $netResults += $interfaceResult + } + $netResults } \ No newline at end of file From 769401af818f6631e3cc48ae1eff42203f323d6e Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Wed, 1 Feb 2017 15:00:33 +0530 Subject: [PATCH 41/90] Fix to mixed mode bug making few attributes like NamingPattern as optional. --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 7a0741d..0f3bf52 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -2399,7 +2399,7 @@ function New-HVFarm { } $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod - if (! $NamingPattern) { + if ($NamingPattern -eq '{n:fixed=4}') { $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern } $maximumCount = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers @@ -2998,6 +2998,7 @@ function Get-HVFarmCustomizationSetting { } #Support only Sysprep Customization + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = New-Object VMware.Hv.FarmSysprepCustomizationSettings $sysprepCustomizationSettings = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings # Get SysPrep CustomizationSpec ID @@ -3477,7 +3478,7 @@ function New-HVPool { [Parameter(Mandatory = $true,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $true,ParameterSetName = 'RDS')] [Parameter(Mandatory = $true,ParameterSetName = 'CLONED_POOL')] - [Parameter(Mandatory = $true,ParameterSetName = 'JSON_FILE')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $PoolName, @@ -3861,7 +3862,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] - [Parameter(Mandatory = $true,ParameterSetName = 'JSON_FILE')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $NamingPattern = $poolName + '{n:fixed=4}', @@ -4147,7 +4148,7 @@ function New-HVPool { $namingMethod = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod $transparentPageSharingScope = $jsonObject.AutomatedDesktopSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope if ($namingMethod -eq "PATTERN") { - if (!$namingPattern) { + if ($NamingPattern -eq '{n:fixed=4}') { $namingPattern = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.namingPattern } $maximumCount = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.maxNumberOfMachines From 04985c7301a43e1e3daae9fbdbcee177d4281638 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Wed, 1 Feb 2017 17:38:36 +0530 Subject: [PATCH 42/90] Changing module version from 1.0 --> 1.1 Update version 1.0 to 1.1 in VMware.HV.Helper --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 index 705cc80..42dc6aa 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 @@ -12,7 +12,7 @@ # RootModule = '' # Version number of this module. -ModuleVersion = '1.0' +ModuleVersion = '1.1' # ID used to uniquely identify this module GUID = '6d3f7fb5-4e52-43d8-91e1-f65f72532a1d' From dd9723cf7b76934be10c5d5e312b51c4f6ec410e Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 08:24:05 -0800 Subject: [PATCH 43/90] Fixing comments --- Modules/VAMI.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 16abc76..8a1dc05 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -172,10 +172,10 @@ Function Get-VAMINetwork { Twitter: @lamw =========================================================================== .SYNOPSIS - This function retrieves access information from VAMI interface (5480) + This function retrieves network information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION - Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + Function to return networking information including details for each interface .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMINetwork From 2d6d3205e11b3d2d1b15a072c1ca5ba8b271eab8 Mon Sep 17 00:00:00 2001 From: William Lam Date: Tue, 31 Jan 2017 15:13:56 -0800 Subject: [PATCH 44/90] Adding Get-VAMINetwork function Fixing comments --- Modules/VAMI.psm1 | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 515b274..8a1dc05 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -159,4 +159,54 @@ Function Get-VAMITime { } $timeResult +} + +Function Get-VAMINetwork { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Jan 31, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves network information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return networking information including details for each interface + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMINetwork +#> + $netResults = @() + + $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() + $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() + + Write-Host "Hostname: " $hostname + Write-Host "DNS Servers: " $dns.servers + + $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() + foreach ($interface in $interfaces) { + $interfaceResult = "" | Select Inteface, MAC, Status, Mode, IP, Prefix, Gateway, Updateable + + $interfaceResult.Inteface = $interface.name + $interfaceResult.MAC = $interface.mac + $interfaceResult.Status = $interface.status + + $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') + $spec = $ipv4API.Help.get.interfaces.CreateExample() + $spec+= $interface.name + $ipv4result = $ipv4API.get($spec) + + $interfaceResult.Mode = $ipv4result.mode + $interfaceResult.IP = $ipv4result.address + $interfaceResult.Prefix = $ipv4result.prefix + $interfaceResult.Gateway = $ipv4result.default_gateway + $interfaceResult.Updateable = $ipv4result.updateable + $netResults += $interfaceResult + } + $netResults } \ No newline at end of file From e3c9108ae8752c13912d5d2ce5142f67345e0426 Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 08:56:02 -0800 Subject: [PATCH 45/90] Using splat method for creating PS Objects --- Modules/VAMI.psm1 | 83 ++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 8a1dc05..e3aaab4 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -24,13 +24,14 @@ $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) $uptime = $ts.ToString("hh\:mm\:ss\,fff") - $summaryResult = "" | Select Product, Type, Version, Build, InstallTime, Uptime - $summaryResult.Product = $results.product - $summaryResult.Type = $results.type - $summaryResult.Version = $results.version - $summaryResult.Build = $results.build - $summaryResult.InstallTime = $results.install_time - $summaryResult.Uptime = $uptime + $summaryResult = New-Object PSObject -Property @{ + Product = $results.product; + Type = $results.type; + Version = $results.version; + Build = $results.build; + InstallTime = $results.install_time; + Uptime = $uptime + } $summaryResult } @@ -71,15 +72,16 @@ Function Get-VAMIHealth { } $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() - $healthResult = "" | Select HealthOverall, HealthLastCheck, HealthCPU, HealthMem, HealthSwap, HealthStorage, HealthVCDB, HealthSoftware - $healthResult.HealthOverall = $healthOverall - $healthResult.HealthLastCheck = $healthLastCheck - $healthResult.HealthCPU = $healthCPU - $healthResult.HealthMem = $healthMem - $healthResult.HealthSwap = $healthSwap - $healthResult.HealthStorage = $healthStorage - $healthResult.HealthVCDB = $healthVCDB - $healthResult.HealthSoftware = $healthSoftwareUpdates + $healthResult = New-Object PSObject -Property @{ + HealthOverall = $healthOverall; + HealthLastCheck = $healthLastCheck; + HealthCPU = $healthCPU; + HealthMem = $healthMem; + HealthSwap = $healthSwap; + HealthStorage = $healthStorage; + HealthVCDB = $healthVCDB; + HealthSoftware = $healthSoftwareUpdates + } $healthResult } @@ -108,11 +110,12 @@ Function Get-VAMIAccess { $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() - $accessResult = "" | Select Console, DCUI, BashShell, SSH - $accessResult.Console = $consoleAccess - $accessResult.DCUI = $dcuiAccess - $accessResult.BashShell = $shellAccess.enabled - $accessResult.SSH = $sshAccess + $accessResult = New-Object PSObject -Property @{ + Console = $consoleAccess; + DCUI = $dcuiAccess; + BashShell = $shellAccess.enabled; + SSH = $sshAccess + } $accessResult } @@ -139,23 +142,22 @@ Function Get-VAMITime { $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' $timeResults = $systemTimeAPI.get() - $timeResult = "" | Select Timezone, Date, CurrentTime, Mode, NTPServers, NTPStatus - $timeResult.Timezone = $timeResults.timezone - $timeResult.Date = $timeResults.date - $timeResult.CurrentTime = $timeResults.time - $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() $timeSyncMode = $timeSync.mode - $timeResult.Mode = $timeSyncMode + $timeResult = New-Object PSObject -Property @{ + Timezone = $timeResults.timezone; + Date = $timeResults.date; + CurrentTime = $timeResults.time; + Mode = $timeSyncMode; + NTPServers = "N/A"; + NTPStatus = "N/A"; + } if($timeSyncMode -eq "NTP") { $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() $timeResult.NTPServers = $ntpServers.servers $timeResult.NTPStatus = $ntpServers.status - } else { - $timeResult.NTPServers = "N/A" - $timeResult.NTPStatus = "N/A" } $timeResult @@ -190,22 +192,21 @@ Function Get-VAMINetwork { $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() foreach ($interface in $interfaces) { - $interfaceResult = "" | Select Inteface, MAC, Status, Mode, IP, Prefix, Gateway, Updateable - - $interfaceResult.Inteface = $interface.name - $interfaceResult.MAC = $interface.mac - $interfaceResult.Status = $interface.status - $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') $spec = $ipv4API.Help.get.interfaces.CreateExample() $spec+= $interface.name $ipv4result = $ipv4API.get($spec) - $interfaceResult.Mode = $ipv4result.mode - $interfaceResult.IP = $ipv4result.address - $interfaceResult.Prefix = $ipv4result.prefix - $interfaceResult.Gateway = $ipv4result.default_gateway - $interfaceResult.Updateable = $ipv4result.updateable + $interfaceResult = New-Object PSObject -Property @{ + Inteface = $interface.name; + MAC = $interface.mac; + Status = $interface.status; + Mode = $ipv4result.mode; + IP = $ipv4result.address; + Prefix = $ipv4result.prefix; + Gateway = $ipv4result.default_gateway; + Updateable = $ipv4result.updateable + } $netResults += $interfaceResult } $netResults From 0d1c8c79c8ec776b86cd726718eb33833b402e7c Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 09:45:32 -0800 Subject: [PATCH 46/90] Fixing spacing --- Modules/VAMI.psm1 | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index e3aaab4..cf50563 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -8,12 +8,12 @@ Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== - .SYNOPSIS - This function retrieves some basic information from VAMI interface (5480) + .SYNOPSIS + This function retrieves some basic information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return basic VAMI summary info - .EXAMPLE + .DESCRIPTION + Function to return basic VAMI summary info + .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMISummary #> @@ -32,7 +32,6 @@ InstallTime = $results.install_time; Uptime = $uptime } - $summaryResult } @@ -46,12 +45,12 @@ Function Get-VAMIHealth { Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== - .SYNOPSIS + .SYNOPSIS This function retrieves health information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION + .DESCRIPTION Function to return VAMI health - .EXAMPLE + .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMIHealth #> @@ -82,7 +81,6 @@ Function Get-VAMIHealth { HealthVCDB = $healthVCDB; HealthSoftware = $healthSoftwareUpdates } - $healthResult } @@ -96,12 +94,12 @@ Function Get-VAMIAccess { Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== - .SYNOPSIS + .SYNOPSIS This function retrieves access information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION + .DESCRIPTION Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) - .EXAMPLE + .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMIAccess #> @@ -116,7 +114,6 @@ Function Get-VAMIAccess { BashShell = $shellAccess.enabled; SSH = $sshAccess } - $accessResult } @@ -130,12 +127,12 @@ Function Get-VAMITime { Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== - .SYNOPSIS + .SYNOPSIS This function retrieves the time and NTP info from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION + .DESCRIPTION Function to return current Time and NTP information - .EXAMPLE + .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMITime #> @@ -159,7 +156,6 @@ Function Get-VAMITime { $timeResult.NTPServers = $ntpServers.servers $timeResult.NTPStatus = $ntpServers.status } - $timeResult } @@ -173,12 +169,12 @@ Function Get-VAMINetwork { Blog: www.virtuallyghetto.com Twitter: @lamw =========================================================================== - .SYNOPSIS + .SYNOPSIS This function retrieves network information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION + .DESCRIPTION Function to return networking information including details for each interface - .EXAMPLE + .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMINetwork #> From e210b2f229fb2df519f79b70b4575ca2c987c88b Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 09:46:57 -0800 Subject: [PATCH 47/90] More spacing fixes --- Modules/VAMI.psm1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index cf50563..cc8e92e 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -46,10 +46,10 @@ Function Get-VAMIHealth { Twitter: @lamw =========================================================================== .SYNOPSIS - This function retrieves health information from VAMI interface (5480) + This function retrieves health information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION - Function to return VAMI health + Function to return VAMI health .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMIHealth @@ -95,10 +95,10 @@ Function Get-VAMIAccess { Twitter: @lamw =========================================================================== .SYNOPSIS - This function retrieves access information from VAMI interface (5480) + This function retrieves access information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION - Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMIAccess @@ -128,10 +128,10 @@ Function Get-VAMITime { Twitter: @lamw =========================================================================== .SYNOPSIS - This function retrieves the time and NTP info from VAMI interface (5480) + This function retrieves the time and NTP info from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION - Function to return current Time and NTP information + Function to return current Time and NTP information .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMITime @@ -170,10 +170,10 @@ Function Get-VAMINetwork { Twitter: @lamw =========================================================================== .SYNOPSIS - This function retrieves network information from VAMI interface (5480) + This function retrieves network information from VAMI interface (5480) for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. .DESCRIPTION - Function to return networking information including details for each interface + Function to return networking information including details for each interface .EXAMPLE Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! Get-VAMINetwork From 38baff0d311865409b6d1af67d0afbdab9997b52 Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 12:21:09 -0800 Subject: [PATCH 48/90] Using pscustomobject to keep ordering --- Modules/VAMI.psm1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index cc8e92e..6b80497 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -24,7 +24,7 @@ $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) $uptime = $ts.ToString("hh\:mm\:ss\,fff") - $summaryResult = New-Object PSObject -Property @{ + $summaryResult = [pscustomobject] @{ Product = $results.product; Type = $results.type; Version = $results.version; @@ -71,7 +71,7 @@ Function Get-VAMIHealth { } $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() - $healthResult = New-Object PSObject -Property @{ + $healthResult = [pscustomobject] @{ HealthOverall = $healthOverall; HealthLastCheck = $healthLastCheck; HealthCPU = $healthCPU; @@ -142,7 +142,7 @@ Function Get-VAMITime { $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() $timeSyncMode = $timeSync.mode - $timeResult = New-Object PSObject -Property @{ + $timeResult = [pscustomobject] @{ Timezone = $timeResults.timezone; Date = $timeResults.date; CurrentTime = $timeResults.time; @@ -193,7 +193,7 @@ Function Get-VAMINetwork { $spec+= $interface.name $ipv4result = $ipv4API.get($spec) - $interfaceResult = New-Object PSObject -Property @{ + $interfaceResult = [pscustomobject] @{ Inteface = $interface.name; MAC = $interface.mac; Status = $interface.status; From d4afcd2ab45e7525dbf772fa4b62a177fca358c3 Mon Sep 17 00:00:00 2001 From: William Lam Date: Wed, 1 Feb 2017 19:33:35 -0800 Subject: [PATCH 49/90] Adding Get-VAMIDisks & Start-VAMIDisksResize func --- Modules/VAMI.psm1 | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 6b80497..f1c0635 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -206,4 +206,55 @@ Function Get-VAMINetwork { $netResults += $interfaceResult } $netResults +} + +Function Get-VAMIDisks { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 02, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VMDK disk number to partition mapping VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VMDK disk number to OS partition mapping + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIDisks +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + $disks = $storageAPI.list() + + foreach ($disk in $disks | sort {[int]$_.disk.toString()}) { + $disk | Select Disk, Partition + } +} + +Function Start-VAMIDiskResize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 02, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function triggers an OS partition resize after adding additional disk capacity + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function triggers OS partition resize operation + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIDiskResize +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + Write-Host "Initiated OS partition resize operation ..." + $storageAPI.resize() } \ No newline at end of file From 4b711cca91b2609c60c71912e4fcd73c730cd690 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 3 Feb 2017 15:50:03 +0530 Subject: [PATCH 50/90] Enabling cloning for Instant clone pool If current image state is not ready, clonging of instant clone pools is not supported. --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 0f3bf52..d10215e 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -4362,6 +4362,8 @@ function New-HVPool { $DesktopVirtualCenterNetworkingSettings = $DesktopVirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings $DesktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings $DesktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings + $CurrentImageState =` + $clonePool.AutomatedDesktopData.provisioningStatusData.instantCloneProvisioningStatusData.instantCloneCurrentImageState } elseIf ($clonePool.ManualDesktopData) { if (! $VM) { @@ -4380,8 +4382,8 @@ function New-HVPool { break } } - if ($provisioningType -eq 'INSTANT_CLONE_ENGINE' -and $poolType -eq 'AUTOMATED') { - Write-Error "Cloning is not supported for instant clone pools" + if ($provisioningType -eq 'INSTANT_CLONE_ENGINE' -and $poolType -eq 'AUTOMATED' -and $CurrentImageState -ne 'READY') { + Write-Error "Instant clone pool's Current Image State should be in 'READY' state, otherwise cloning is not supported" break } } else { From 7a919a49b9a1426b621e1720be1eda678f20c0e1 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 3 Feb 2017 17:39:47 +0530 Subject: [PATCH 51/90] Adding new Advanced function Set-HVMachine Using this AF, we can allow the machine to enter the maintanence mode and as well as exit the maintanence mode --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 150 +++++++++++++++++- 1 file changed, 148 insertions(+), 2 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index d10215e..eeee4aa 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5867,11 +5867,11 @@ function Set-HVPool { } $desktop_helper = New-Object VMware.Hv.DesktopService foreach ($item in $poolList.Keys) { + Write-Host "Updating the Pool: " $poolList.$item if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { $desktop_helper.Desktop_Update($services,$item,$updates) } } - Write-Host "Update successful for Pool: " $poolList.$item } end { @@ -8039,4 +8039,150 @@ function Remove-HVEntitlement { } } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement +function Set-HVMachine { +<# +.Synopsis + Sets existing virtual Machine(s). + +.DESCRIPTION + This cmdlet allows user to edit Machine configuration by passing key/value pair. + Allows the machine in to Maintenance mode and vice versa + +.PARAMETER MachineName + The name of the Machine to edit. + +.PARAMETER Machine + Object(s) of the virtual Machine(s) to edit. + +.PARAMETER Maintenance + The virtual machine is in maintenance mode. Users cannot log in or use the virtual machine + +PARAMETER Key + Property names path separated by . (dot) from the root of machine info spec. + +.PARAMETER Value + Property value corresponds to above key name. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Moving the machine in to Maintenance mode using machine name + Set-HVMachine -MachineName 'Agent_Praveen' -Maintenance ENTER_MAINTENANCE_MODE + +.EXAMPLE + Moving the machine in to Maintenance mode using machine object(s) + Get-HVMachine -MachineName 'Agent_Praveen' | Set-HVMachine -Maintenance ENTER_MAINTENANCE_MODE + +.EXAMPLE + Moving the machine in to Maintenance mode using machine object(s) + $machine = Get-HVMachine -MachineName 'Agent_Praveen'; Set-HVMachine -Machine $machine -Maintenance EXIT_MAINTENANCE_MODE + +.OUTPUTS + None + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $true ,ParameterSetName = 'option')] + [string] + $MachineName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'pipeline')] + $Machine, + + [Parameter(Mandatory = $false)] + [ValidateSet('ENTER_MAINTENANCE_MODE', 'EXIT_MAINTENANCE_MODE')] + [string] + $Maintenance, + + [Parameter(Mandatory = $false)] + [string]$Key, + + [Parameter(Mandatory = $false)] + $Value, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $machineList = @{} + if ($machineName) { + try { + $machines = Get-HVMachineSummary -MachineName $machineName -hvServer $hvServer + } catch { + Write-Error "Make sure Get-HVMachineSummary advanced function is loaded, $_" + break + } + if ($machines) { + foreach ($macineObj in $machines) { + $machineList.add($macineObj.id, $macineObj.base.Name) + } + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Machine) { + foreach ($item in $machine) { + if (($item.GetType().name -eq 'MachineNamesView') -or ($item.GetType().name -eq 'MachineInfo')) { + $machineList.add($item.id, $item.Base.Name) + } else { + Write-Error "In pipeline did not get object of expected type MachineNamesView/MachineInfo" + [System.gc]::collect() + return + } + } + } + $updates = @() + if ($key -and $value) { + $updates += Get-MapEntry -key $key -value $value + } elseif ($key -or $value) { + Write-Error "Both key:[$key] and value:[$value] needs to be specified" + } + + if ($Maintenance) { + if ($Maintenance -eq 'ENTER_MAINTENANCE_MODE') { + $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $true + } else { + $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $false + } + } + $machine_helper = New-Object VMware.Hv.MachineService + foreach ($item in $machineList.Keys) { + Write-Host "Updating the Machine: " $machineList.$item + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($machineList.$item)) { + $machine_helper.Machine_Update($services,$item,$updates) + } + } + } + + end { + [System.gc]::collect() + } + +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine From 0e037f19a441dc8bbb9ed45100c7af09d9436783 Mon Sep 17 00:00:00 2001 From: William Lam Date: Mon, 6 Feb 2017 12:37:01 -0800 Subject: [PATCH 52/90] Adding Get-VAMIStatsList & Get-VAMIStorageUsed functions --- Modules/VAMI.psm1 | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index f1c0635..62887d9 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -257,4 +257,129 @@ Function Start-VAMIDiskResize { $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' Write-Host "Initiated OS partition resize operation ..." $storageAPI.resize() +} + +Function Get-VAMIStatsList { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 06, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list avialable monitoring metrics in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of available monitoring metrics that can be queried + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStatsList +#> + $monitoringAPI = Get-CisService -Name 'com.vmware.appliance.monitoring' + $ids = $monitoringAPI.list() | Select id | Sort-Object -Property id + + foreach ($id in $ids) { + $id + } +} + +Function Get-VAMIStorageUsed { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 06, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the individaul OS partition storage utilization + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return individual OS partition storage utilization + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStorageUsed +#> + $monitoringAPI = Get-CisService 'com.vmware.appliance.monitoring' + $querySpec = $monitoringAPI.help.query.item.CreateExample() + + # List of IDs from Get-VAMIStatsList to query + $querySpec.Names = @( + "storage.used.filesystem.autodeploy", + "storage.used.filesystem.boot", + "storage.used.filesystem.coredump", + "storage.used.filesystem.imagebuilder", + "storage.used.filesystem.invsvc", + "storage.used.filesystem.log", + "storage.used.filesystem.netdump", + "storage.used.filesystem.root", + "storage.used.filesystem.updatemgr", + "storage.used.filesystem.vcdb_core_inventory", + "storage.used.filesystem.vcdb_seat", + "storage.used.filesystem.vcdb_transaction_log", + "storage.totalsize.filesystem.autodeploy", + "storage.totalsize.filesystem.boot", + "storage.totalsize.filesystem.coredump", + "storage.totalsize.filesystem.imagebuilder", + "storage.totalsize.filesystem.invsvc", + "storage.totalsize.filesystem.log", + "storage.totalsize.filesystem.netdump", + "storage.totalsize.filesystem.root", + "storage.totalsize.filesystem.updatemgr", + "storage.totalsize.filesystem.vcdb_core_inventory", + "storage.totalsize.filesystem.vcdb_seat", + "storage.totalsize.filesystem.vcdb_transaction_log" + ) + + # Tuple (Filesystem Name, Used, Total) to store results + $storageStats = @{ + "autodeploy"=@{"name"="/storage/autodeploy";"used"=0;"total"=0}; + "boot"=@{"name"="/boot";"used"=0;"total"=0}; + "coredump"=@{"name"="/storage/core";"used"=0;"total"=0}; + "imagebuilder"=@{"name"="/storage/imagebuilder";"used"=0;"total"=0}; + "invsvc"=@{"name"="/storage/invsvc";"used"=0;"total"=0}; + "log"=@{"name"="/storage/log";"used"=0;"total"=0}; + "netdump"=@{"name"="/storage/netdump";"used"=0;"total"=0}; + "root"=@{"name"="/";"used"=0;"total"=0}; + "updatemgr"=@{"name"="/storage/updatemgr";"used"=0;"total"=0}; + "vcdb_core_inventory"=@{"name"="/storage/db";"used"=0;"total"=0}; + "vcdb_seat"=@{"name"="/storage/seat";"used"=0;"total"=0}; + "vcdb_transaction_log"=@{"name"="/storage/dblog";"used"=0;"total"=0} + } + + $querySpec.interval = "DAY1" + $querySpec.function = "MAX" + $querySpec.start_time = ((get-date).AddDays(-1)) + $querySpec.end_time = (Get-Date) + $queryResults = $monitoringAPI.query($querySpec) | Select * -ExcludeProperty Help + + foreach ($queryResult in $queryResults) { + # Update hash if its used storage results + if($queryResult.name -match "used") { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["used"] = $value + # Update hash if its total storage results + } else { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["total"] = $value + } + } + + $storageResults = @() + foreach ($key in $storageStats.keys | Sort-Object -Property name) { + $statResult = [pscustomobject] @{ + Filesystem = $storageStats[$key].name; + Used = $storageStats[$key].used; + Total = $storageStats[$key].total + } + $storageResults += $statResult + } + $storageResults } \ No newline at end of file From d55a16f19bd327e134149511a3352738116e5266 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 7 Feb 2017 16:40:29 +0530 Subject: [PATCH 53/90] Adding new AFs New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement New-HVGlobalEntitlement: Creates a Global Entitlement. , Remove-HVGlobalEntitlement: Deletes a Global Entitlement., Get-HVGlobalEntitlement: Gets Global Entitlement(s). --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 455 +++++++++++++++++- 1 file changed, 452 insertions(+), 3 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index eeee4aa..8c8db50 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5377,7 +5377,7 @@ function Remove-HVPool { ConfirmImpact = 'High' )] param( - [Parameter(Mandatory = $false,ParameterSetName = 'option')] + [Parameter(Mandatory = $true,ParameterSetName = 'option')] [string] $poolName, # PoolObject @@ -5420,7 +5420,7 @@ function Remove-HVPool { Write-Error "No desktopsummarydata found with pool name: [$pool]" break } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Pool) { foreach ($item in $pool) { if ($item.GetType().name -eq 'DesktopSummaryView') { $poolList += @{id = $item.id; name = $item.desktopSummaryData.name} @@ -8185,4 +8185,453 @@ PARAMETER Key } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine +function New-HVGlobalEntitlement { + + <# +.Synopsis + Creates a Global Entitlement. + +.DESCRIPTION + Global entitlements are used to route users to their resources across multiple pods. + These are persisted in a global ldap instance that is replicated across all pods in a linked mode view set. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER Type + Specify whether to create desktop/app global entitlement + +.PARAMETER Description + Description of Global Entitlement. + +.PARAMETER Scope + Scope for this global entitlement. Visibility and Placement policies are defined by this value. + +.PARAMETER Dedicated + Specifies whether dedicated/floating resources associated with this global entitlement. + +.PARAMETER FromHome + This value defines the starting location for resource placement and search. + When true, a pod in the user's home site is used to start the search. When false, the current site is used. + +.PARAMETER RequireHomeSite + This value determines whether we fail if a home site isn't defined for this global entitlement. + +.PARAMETER MultipleSessionAutoClean + This value is used to determine if automatic session clean up is enabled. + This cannot be enabled when this Global Entitlement is associated with a Desktop that has dedicated user assignment. + +.PARAMETER Enabled + If this Global Entitlement is enabled. + +.PARAMETER SupportedDisplayProtocols + The set of supported display protocols for the global entitlement. + +.PARAMETER DefaultDisplayProtocol + The default display protocol for the global entitlement. + +.PARAMETER AllowUsersToChooseProtocol + Whether the users can choose the protocol used. + +.PARAMETER AllowUsersToResetMachines + Whether users are allowed to reset/restart their machines. + +.PARAMETER EnableHTMLAccess + If set to true, the desktops that are associated with this GlobalEntitlement must also have HTML Access enabled. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Creates new global application entitlement + New-HVGlobalEntitlement -DisplayName 'GE_APP' -Type APPLICATION_ENTITLEMENT + +.EXAMPLE + Creates new global desktop entitlement + New-HVGlobalEntitlement -DisplayName 'GE_DESKTOP' -Type DESKTOP_ENTITLEMENT + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $true)] + [ValidateSet('DESKTOP_ENTITLEMENT','APPLICATION_ENTITLEMENT')] + [String] + $Type, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $Description, + + [Parameter(Mandatory = $false)] + [ValidateSet('LOCAL','SITE','ANY')] + [String] + $Scope = "ANY", + + [Parameter(Mandatory = $false)] + [Boolean] + $Dedicated, + + [Parameter(Mandatory = $false)] + [Boolean] + $FromHome, + + [Parameter(Mandatory = $false)] + [Boolean] + $RequireHomeSite, + + [Parameter(Mandatory = $false)] + [Boolean] + $MultipleSessionAutoClean, + + [Parameter(Mandatory = $false)] + [Boolean] + $Enabled, + + [Parameter(Mandatory = $false)] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [String[]] + $SupportedDisplayProtocols = @("PCOIP","BLAST"), + + [Parameter(Mandatory = $false)] + [ValidateSet("PCOIP",'RDP',"BLAST")] + [String] + $DefaultDisplayProtocol = 'PCOIP', + + [Parameter(Mandatory = $false)] + [Boolean] + $AllowUsersToChooseProtocol = $true, + + [Parameter(Mandatory = $false)] + [Boolean] + $AllowUsersToResetMachines = $false, + + [Parameter(Mandatory = $false)] + [Boolean] + $EnableHTMLAccess = $false, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + if ($Type -eq 'DESKTOP_ENTITLEMENT') { + $GeService = New-Object VMware.HV.GlobalEntitlementService + $geBaseHelper = $GeService.getGlobalEntitlementBaseHelper() + $geBase = $geBaseHelper.getDataObject() + $geBase.Dedicated = $dedicated + $geBase.AllowUsersToResetMachines = $AllowUsersToResetMachines + } else { + $GeService = New-Object VMware.Hv.GlobalApplicationEntitlementService + $geBaseHelper = $GeService.getGlobalApplicationEntitlementBaseHelper() + $geBase = $geBaseHelper.getDataObject() + } + $geBase.DisplayName = $displayName + if ($description) { + $geBaseHelper.setDescription($Description) + } + $geBase.Scope = $Scope + $geBase.FromHome = $fromHome + $geBase.RequireHomeSite = $requireHomeSite + $geBase.MultipleSessionAutoClean = $multipleSessionAutoClean + $geBase.Enabled = $enabled + $geBase.DefaultDisplayProtocol = $defaultDisplayProtocol + $geBase.AllowUsersToChooseProtocol = $AllowUsersToChooseProtocol + $geBase.EnableHTMLAccess = $enableHTMLAccess + $geBase.SupportedDisplayProtocols = $supportedDisplayProtocols + Write-Host "Creating new global entitlement with DisplayName: " $DisplayName + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($displayName)) { + if ($type -eq 'DESKTOP_ENTITLEMENT') { + $GeService.GlobalEntitlement_Create($services, $geBase) + } else { + $GeService.GlobalApplicationEntitlement_Create($services, $geBase) + } + } + } + end { + [System.gc]::collect() + } + +} + + +function Find-HVGlobalEntitlement { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Param, + [Parameter(Mandatory = $true)] + [String] + $Type + ) + + # This translates the function arguments into the View API properties that must be queried + $GeSelectors = @{ + 'displayName' = 'base.displayName'; + 'description' = 'base.description'; + } + + $params = $Param + + $query_service_helper = New-Object VMware.Hv.QueryServiceService + $query = New-Object VMware.Hv.QueryDefinition + + $wildCard = $false + #Only supports wild card '*' + if ($params['displayName'] -and $params['displayName'].contains('*')) { + $wildcard = $true + } + + # build the query values + $query.queryEntityType = $Type + if (! $wildcard) { + [VMware.Hv.queryfilter[]]$filterSet = @() + foreach ($setting in $GeSelectors.Keys) { + if ($null -ne $params[$setting]) { + $equalsFilter = New-Object VMware.Hv.QueryFilterEquals + $equalsFilter.memberName = $GeSelectors[$setting] + $equalsFilter.value = $params[$setting] + $filterSet += $equalsFilter + } + } + if ($filterSet.Count -gt 0) { + $andFilter = New-Object VMware.Hv.QueryFilterAnd + $andFilter.Filters = $filterset + $query.Filter = $andFilter + } + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $GeList = $queryResults.results + } + if ($wildcard -or [string]::IsNullOrEmpty($GeList)) { + $query.Filter = $null + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $strFilterSet = @() + foreach ($setting in $GeSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and ($setting -eq 'displayName') ) { + $strFilterSet += '($_.' + $GeSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $GeSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } + } + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $GeList = $queryResults.results | where $scriptBlock + } + Return $GeList +} + +function Get-HVGlobalEntitlement { + + <# +.Synopsis + +.DESCRIPTION + Global entitlements are used to route users to their resources across multiple pods. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER Description + Description of Global Entitlement. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Retrieves global application/desktop entitlement(s) with displayName 'GEAPP' + Get-HVGlobalEntitlement -DisplayName 'GEAPP' + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $Description, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $result = @() + $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalEntitlementSummaryView' + $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalApplicationEntitlementInfo' + if (! $result) { + Write-Host "Get-HVGlobalEntitlement: No global entitlement Found with given search parameters" + break + } + return $result + } + end { + [System.gc]::collect() + } +} + + +function Remove-HVGlobalEntitlement { + + <# +.Synopsis + Deletes a Global Entitlement. + +.DESCRIPTION + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Deletes global application/desktop entitlement with displayName 'GE_APP' + Remove-HVGlobalEntitlement -DisplayName 'GE_APP' + +.EXAMPLE + Deletes global application/desktop entitlement(s), if displayName matches with 'GE_*' + Get-HVGlobalEntitlement -DisplayName 'GE_*' | Remove-HVGlobalEntitlement + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.0.3 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'pipeline')] + $GlobalEntitlement, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $GeList = @() + if ($DisplayName) { + try { + $GeList = Get-HVGlobalEntitlement -DisplayName $DisplayName -hvServer $hvServer + } catch { + Write-Error "Make sure Get-HVGlobalEntitlement advanced function is loaded, $_" + break + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $GlobalEntitlement) { + foreach ($item in $GlobalEntitlement) { + if (($item.GetType().name -ne 'GlobalEntitlementSummaryView') -and ($item.GetType().name -ne 'GlobalApplicationEntitlementInfo')) { + Write-Error "In pipeline did not get object of expected type GlobalApplicationEntitlementInfo/GlobalEntitlementSummaryView" + [System.gc]::collect() + return + } + $GeList += ,$item + } + } + foreach ($item in $GeList) { + Write-Host "Deleting global entitlement with DisplayName: " $item.base.displayName + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.base.displayName)) { + if ($item.GetType().Name -eq 'GlobalEntitlementSummaryView') { + $services.GlobalEntitlement.GlobalEntitlement_Delete($item.id) + } else { + $services.GlobalApplicationEntitlement.GlobalApplicationEntitlement_Delete($item.id) + } + } + } + } + end { + [System.gc]::collect() + } + +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement From 552793585ae2ce7cc944520e173558c99cae37b0 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 7 Feb 2017 16:43:29 +0530 Subject: [PATCH 54/90] Added more description to Global Entitlement Added more description to Global Entitlement(s) --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 8c8db50..b668296 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -8458,8 +8458,10 @@ function Get-HVGlobalEntitlement { <# .Synopsis + Gets Global Entitlement(s) with given search parameters. .DESCRIPTION + Queries and returns global entitlement(s) and global application entitlement(s). Global entitlements are used to route users to their resources across multiple pods. .PARAMETER DisplayName @@ -8541,6 +8543,8 @@ function Remove-HVGlobalEntitlement { Deletes a Global Entitlement. .DESCRIPTION + Deletes global entitlement(s) and global application entitlement(s). + Optionally, user can pipe the global entitlement(s) as input to this function. .PARAMETER DisplayName Display Name of Global Entitlement. From 1fc4b3786353fdc997560ad5f7431610a6a5888a Mon Sep 17 00:00:00 2001 From: William Lam Date: Tue, 7 Feb 2017 12:42:06 -0800 Subject: [PATCH 55/90] Adding {Get,Start,Stop}-VAMIService function --- Modules/VAMI.psm1 | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 62887d9..a66d3c1 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -382,4 +382,146 @@ Function Get-VAMIStorageUsed { $storageResults += $statResult } $storageResults +} + +Function Get-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 08, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIService + .EXAMPLE + Get-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + if($Name -ne "") { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + $serviceStatus = $vMonAPI.get($name,0) + $serviceString = [pscustomobject] @{ + Name = $name; + State = $serviceStatus.state; + Health = ""; + Startup = $serviceStatus.startup_type + } + if($serviceStatus.health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $serviceStatus.health } + $serviceString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + $services = $vMonAPI.list_details() + + $serviceResult = @() + foreach ($key in $services.keys | Sort-Object -Property Value) { + $serviceString = [pscustomobject] @{ + Name = $key; + State = $services[$key].state; + Health = "N/A"; + Startup = $services[$key].Startup_type + } + if($services[$key].health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $services[$key].health } + + $serviceResult += $serviceString + } + $serviceResult + } +} + +Function Start-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 08, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Starting $name service ..." + $vMonAPI.start($name) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Stop-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Feb 08, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Stop-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Stopping $name service ..." + $vMonAPI.stop($name) + } catch { + Write-Error $Error[0].exception.Message + } } \ No newline at end of file From 49834682a95177cada9139af2dec37dbebffd1a5 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 10 Feb 2017 12:48:29 +0530 Subject: [PATCH 56/90] Formatting Examples Formatting Advanced functions Examples and Description --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 263 +++++++++--------- 1 file changed, 132 insertions(+), 131 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index b668296..01b1170 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -186,20 +186,20 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A View API service object of Connect-HVServer cmdlet. .EXAMPLE - Add managed manual VMs to existing manual pool Add-HVDesktop -PoolName 'ManualPool' -Machines 'manualPool1', 'manualPool2' -Confirm:$false + Add managed manual VMs to existing manual pool .EXAMPLE - Add virtual machines to automated specific named dedicated pool Add-HVDesktop -PoolName 'SpecificNamed' -Machines 'vm-01', 'vm-02' -Users 'user1', 'user2' + Add virtual machines to automated specific named dedicated pool .EXAMPLE - Add machines to automated specific named Floating pool Add-HVDesktop -PoolName 'SpecificNamed' -Machines 'vm-03', 'vm-04' + Add machines to automated specific named Floating pool .EXAMPLE - Add machines to unmanged manual pool Add-HVDesktop -PoolName 'Unmanaged' -Machines 'desktop-1.eng.vmware.com' + Add machines to unmanged manual pool .NOTES Author : Praveen Mathamsetty. @@ -379,8 +379,8 @@ function Add-HVRDSServer { View API service object of Connect-HVServer cmdlet. .EXAMPLE - Add RDSServers to manual farm Add-HVRDSServer -Farm "manualFarmTest" -RdsServers "vm-for-rds","vm-for-rds-2" -Confirm:$false + Add RDSServers to manual farm .OUTPUTS None @@ -483,21 +483,21 @@ function Connect-HVEvent { Password corresponds to 'dbUserName' user. .EXAMPLE - Connecting to the database with default username configured on Connection Server $hvServer. Connect-HVEvent -HvServer $hvServer + Connecting to the database with default username configured on Connection Server $hvServer. .EXAMPLE - Connecting to the database configured on Connection Server $hvServer with customised user name 'system'. $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' + Connecting to the database configured on Connection Server $hvServer with customised user name 'system'. .EXAMPLE - Connecting to the database with customised user name and password. $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword 'censored' + Connecting to the database with customised user name and password. .EXAMPLE + C:\PS>$password = Read-Host 'Database Password' -AsSecureString + C:\PS>$hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword $password Connecting to the database with customised user name and password, with password being a SecureString. - $password = Read-Host 'Database Password' -AsSecureString - $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword $password .OUTPUTS Returns a custom object that has database connection as 'dbConnection' property. @@ -614,8 +614,8 @@ function Disconnect-HVEvent { Connection object returned by Connect-HVEvent advanced function. This is a mandatory input. .EXAMPLE - Disconnecting the database connection on $hvDbServer. Disconnect-HVEvent -HvDbServer $hvDbServer + Disconnecting the database connection on $hvDbServer. .OUTPUTS None @@ -704,15 +704,15 @@ function Get-HVEvent { String that can applied in filtering on 'Message' column. .EXAMPLE + C:\PS>$e = Get-HVEvent -hvDbServer $hvDbServer + C:\PS>$e.Events Querying all the database events on database $hvDbServer. - $e = Get-HVEvent -hvDbServer $hvDbServer - $e.Events .EXAMPLE + C:\PS>$e = Get-HVEvent -HvDbServer $hvDbServer -TimePeriod 'all' -FilterType 'startsWith' -UserFilter 'aduser' -SeverityFilter 'err' -TimeFilter 'HH:MM:SS.fff' -ModuleFilter 'broker' -MessageFilter 'aduser' + C:\PS>$e.Events | Export-Csv -Path 'myEvents.csv' -NoTypeInformation Querying all the database events where user name startswith 'aduser', severity is of 'err' type, having module name as 'broker', message starting with 'aduser' and time starting with 'HH:MM:SS.fff'. The resulting events will be exported to a csv file 'myEvents.csv'. - $e = Get-HVEvent -HvDbServer $hvDbServer -TimePeriod 'all' -FilterType 'startsWith' -UserFilter 'aduser' -SeverityFilter 'err' -TimeFilter 'HH:MM:SS.fff' -ModuleFilter 'broker' -MessageFilter 'aduser' - $e.Events | Export-Csv -Path 'myEvents.csv' -NoTypeInformation .OUTPUTS Returns a custom object that has events information in 'Events' property. Events property will have events information with five columns: UserName, Severity, EventTime, Module and Message. @@ -931,24 +931,24 @@ function Get-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Queries and returns farmInfo based on given parameter farmName Get-HVFarm -FarmName 'Farm-01' + Queries and returns farmInfo based on given parameter farmName .EXAMPLE - Queries and returns farmInfo based on given parameters farmName, farmDisplayName Get-HVFarm -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Queries and returns farmInfo based on given parameters farmName, farmDisplayName .EXAMPLE - Queries and returns farmInfo based on given parameters farmName, farmType Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' + Queries and returns farmInfo based on given parameters farmName, farmType .EXAMPLE - Queries and returns farmInfo based on given parameters farmName, FarmType etc Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Queries and returns farmInfo based on given parameters farmName, FarmType etc .EXAMPLE - Queries and returns farmInfo based on parameter farmName with wild character * Get-HVFarm -FarmName 'Farm-0*' + Queries and returns farmInfo based on parameter farmName with wild character * .OUTPUTs Returns the list of FarmInfo object matching the query criteria. @@ -1037,24 +1037,24 @@ function Get-HVFarmSummary { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Queries and returns farmSummary objects based on given parameter farmName Get-HVFarmSummary -FarmName 'Farm-01' + Queries and returns farmSummary objects based on given parameter farmName .EXAMPLE - Queries and returns farmSummary objects based on given parameters farmName, farmDisplayName Get-HVFarmSummary -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Queries and returns farmSummary objects based on given parameters farmName, farmDisplayName .EXAMPLE - Queries and returns farmSummary objects based on given parameters farmName, farmType Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' + Queries and returns farmSummary objects based on given parameters farmName, farmType .EXAMPLE - Queries and returns farmSummary objects based on given parameters farmName, FarmType etc Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Queries and returns farmSummary objects based on given parameters farmName, FarmType etc .EXAMPLE - Queries and returns farmSummary objects based on given parameter farmName with wild character * Get-HVFarmSummary -FarmName 'Farm-0*' + Queries and returns farmSummary objects based on given parameter farmName with wild character * .OUTPUTs Returns the list of FarmSummary object matching the query criteria. @@ -1218,20 +1218,20 @@ function Get-HVPool { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Queries and returns pool object(s) based on given parameters poolName, poolType etc. Get-HVPool -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true + Queries and returns pool object(s) based on given parameters poolName, poolType etc. .EXAMPLE - Queries and returns pool object(s) based on given parameters poolType and userAssignment Get-HVPool -PoolType AUTOMATED -UserAssignment FLOATING + Queries and returns pool object(s) based on given parameters poolType and userAssignment .EXAMPLE - Queries and returns pool object(s) based on given parameters poolName, PoolType etc. Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false + Queries and returns pool object(s) based on given parameters poolName, PoolType etc. .EXAMPLE - Queries and returns pool object(s) based on given parameters poolName and HvServer etc. Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs + Queries and returns pool object(s) based on given parameters poolName and HvServer etc. .OUTPUTS Returns list of objects of type DesktopInfo @@ -1348,20 +1348,20 @@ function Get-HVPoolSummary { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Queries and returns desktopSummaryView based on given parameters poolName, poolType etc. Get-HVPoolSummary -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true + Queries and returns desktopSummaryView based on given parameters poolName, poolType etc. .EXAMPLE - Queries and returns desktopSummaryView based on given parameters poolType, userAssignment. Get-HVPoolSummary -PoolType AUTOMATED -UserAssignment FLOATING + Queries and returns desktopSummaryView based on given parameters poolType, userAssignment. .EXAMPLE - Queries and returns desktopSummaryView based on given parameters poolName, poolType, userAssignment etc. Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false + Queries and returns desktopSummaryView based on given parameters poolName, poolType, userAssignment etc. .EXAMPLE - Queries and returns desktopSummaryView based on given parameters poolName, HvServer etc. Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs + Queries and returns desktopSummaryView based on given parameters poolName, HvServer etc. .OUTPUTS Returns list of DesktopSummaryView @@ -1535,41 +1535,41 @@ function Get-HVQueryFilter { .EXAMPLE - Creates queryFilterEquals with given parameters memberName(position 0) and memberValue(position 2) Get-HVQueryFilter data.name -Eq vmware + Creates queryFilterEquals with given parameters memberName(position 0) and memberValue(position 2) .EXAMPLE - Creates queryFilterEquals with given parameters memberName and memberValue Get-HVQueryFilter -MemberName data.name -Eq -MemberValue vmware + Creates queryFilterEquals with given parameters memberName and memberValue .EXAMPLE - Creates queryFilterNotEquals filter with given parameters memberName and memberValue Get-HVQueryFilter data.name -Ne vmware + Creates queryFilterNotEquals filter with given parameters memberName and memberValue .EXAMPLE - Creates queryFilterContains with given parameters memberName and memberValue Get-HVQueryFilter data.name -Contains vmware + Creates queryFilterContains with given parameters memberName and memberValue .EXAMPLE - Creates queryFilterStartsWith with given parameters memberName and memberValue Get-HVQueryFilter data.name -Startswith vmware + Creates queryFilterStartsWith with given parameters memberName and memberValue .EXAMPLE + C:\PS>$filter = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>Get-HVQueryFilter -Not $filter Creates queryFilterNot with given parameter filter - $filter = Get-HVQueryFilter data.name -Startswith vmware - Get-HVQueryFilter -Not $filter .EXAMPLE + C:\PS>$filter1 = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>$filter2 = Get-HVQueryFilter data.name -Contains pool + C:\PS>Get-HVQueryFilter -And @($filter1, $filter2) Creates queryFilterAnd with given parameter filters array - $filter1 = Get-HVQueryFilter data.name -Startswith vmware - $filter2 = Get-HVQueryFilter data.name -Contains pool - Get-HVQueryFilter -And @($filter1, $filter2) .EXAMPLE + C:\PS>$filter1 = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>$filter2 = Get-HVQueryFilter data.name -Contains pool + C:\PS>Get-HVQueryFilter -Or @($filter1, $filter2) Creates queryFilterOr with given parameter filters array - $filter1 = Get-HVQueryFilter data.name -Startswith vmware - $filter2 = Get-HVQueryFilter data.name -Contains pool - Get-HVQueryFilter -Or @($filter1, $filter2) .OUTPUTS Returns the QueryFilter object @@ -1695,25 +1695,25 @@ function Get-HVQueryResult { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Returns query results of entityType DesktopSummaryView(position 0) Get-HVQueryResult DesktopSummaryView + Returns query results of entityType DesktopSummaryView(position 0) .EXAMPLE - Returns query results of entityType DesktopSummaryView(position 0) with given filter(position 1) Get-HVQueryResult DesktopSummaryView (Get-HVQueryFilter data.name -Eq vmware) + Returns query results of entityType DesktopSummaryView(position 0) with given filter(position 1) .EXAMPLE - Returns query results of entityType DesktopSummaryView with given filter Get-HVQueryResult -EntityType DesktopSummaryView -Filter (Get-HVQueryFilter desktopSummaryData.name -Eq vmware) + Returns query results of entityType DesktopSummaryView with given filter .EXAMPLE + C:\PS>$myFilter = Get-HVQueryFilter data.name -Contains vmware + C:\PS>Get-HVQueryResult -EntityType DesktopSummaryView -Filter $myFilter -SortBy desktopSummaryData.displayName -SortDescending $false Returns query results of entityType DesktopSummaryView with given filter and also sorted based on dispalyName - $myFilter = Get-HVQueryFilter data.name -Contains vmware - Get-HVQueryResult -EntityType DesktopSummaryView -Filter $myFilter -SortBy desktopSummaryData.displayName -SortDescending $false .EXAMPLE - Returns query results of entityType DesktopSummaryView, maximum count equal to limit Get-HVQueryResult DesktopSummaryView -Limit 10 + Returns query results of entityType DesktopSummaryView, maximum count equal to limit .OUTPUTS Returns the list of objects of entityType @@ -1942,28 +1942,28 @@ function New-HVFarm { Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Creates new linkedClone farm by using naming pattern New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + Creates new linkedClone farm by using naming pattern .EXAMPLE - Creates new linkedClone farm by using naming pattern New-HVFarm -InstantClone -FarmName 'ICFarmCL' -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_5' -VmFolder 'Instant_Clone_VMs' -HostOrCluster 'vimal-cluster' -ResourcePool 'vimal-cluster' -Datastores 'datastore1' -FarmDisplayName 'IC Farm using CL' -Description 'created IC Farm from PS command-line' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "ICFarmCL-" -NetBiosName "ad-vimalg" + Creates new linkedClone farm by using naming pattern .EXAMPLE - Creates new linkedClone farm by using json file New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false + Creates new linkedClone farm by using json file -.EXAMPLE - Creates new instantClone farm by using json file +.EXAMPLE New-HVFarm -Spec C:\VMWare\Specs\InstantCloneFarm.json -Confirm:$false + Creates new instantClone farm by using json file .EXAMPLE - Creates new manual farm by using rdsServers names New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" -Confirm:$false + Creates new manual farm by using rdsServers names .EXAMPLE - Creates new instant clone farm by reading few parameters from json and few parameters from command line. New-HVFarm -Spec C:\VMWare\Specs\AutomatedInstantCloneFarm.json -FarmName 'InsPool' -NamingPattern 'InsFarm-' + Creates new instant clone farm by reading few parameters from json and few parameters from command line. .OUTPUTS None @@ -3365,6 +3365,7 @@ function New-HVPool { .PARAMETER PostSynchronizationScriptParameters Post synchronization script parameters. Example: p1 p2 p3 Applicable to Linked, Instant Clone pools. + .PARAMETER Source Source of the Virtual machines for manual pool. Supported values are 'VIRTUAL_CENTER','UNMANAGED'. @@ -3391,38 +3392,38 @@ function New-HVPool { first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE + C:\PS>New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 0 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root Create new automated linked clone pool with naming method pattern - New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 0 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root .EXAMPLE - Create new automated linked clone pool by using JSON spec file New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false + Create new automated linked clone pool by using JSON spec file .EXAMPLE - Clones new pool by using existing pool configuration - Get-HVPool -PoolName 'vmwarepool' | New-HVPool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + C:\PS>Get-HVPool -PoolName 'vmwarepool' | New-HVPool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; (OR) - $vmwarepool = Get-HVPool -PoolName 'vmwarepool'; New-HVPool -ClonePool $vmwarepool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + C:\PS>$vmwarepool = Get-HVPool -PoolName 'vmwarepool'; New-HVPool -ClonePool $vmwarepool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + Clones new pool by using existing pool configuration .EXAMPLE - Create new automated instant clone pool with naming method pattern New-HVPool -InstantClone -PoolName "InsPoolvmware" -PoolDisplayName "insPool" -Description "create instant pool" -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -NamingMethod PATTERN -Datastores 'datastore1' -NamingPattern "inspool2" -NetBiosName 'adviewdev' -DomainAdmin root + Create new automated instant clone pool with naming method pattern .EXAMPLE - Create new automated full clone pool with naming method pattern New-HVPool -FullClone -PoolName "FullClone" -PoolDisplayName "FullClonePra" -Description "create full clone" -UserAssignment DEDICATED -Template 'powerCLI-VM-TEMPLATE' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -NamingPattern 'FullCln1' -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root + Create new automated full clone pool with naming method pattern .EXAMPLE - Create new managed manual pool from virtual center managed VirtualMachines. New-HVPool -MANUAL -PoolName 'manualVMWare' -PoolDisplayName 'MNLPUL' -Description 'Manual pool creation' -UserAssignment FLOATING -Source VIRTUAL_CENTER -VM 'PowerCLIVM1', 'PowerCLIVM2' + Create new managed manual pool from virtual center managed VirtualMachines. .EXAMPLE - Create new unmanaged manual pool from unmanaged VirtualMachines. New-HVPool -MANUAL -PoolName 'unmangedVMWare' -PoolDisplayName 'unMngPl' -Description 'unmanaged Manual Pool creation' -UserAssignment FLOATING -Source UNMANAGED -VM 'myphysicalmachine.vmware.com' + Create new unmanaged manual pool from unmanaged VirtualMachines. .EXAMPLE - Creates new instant clone pool by reading few parameters from json and few parameters from command line. New-HVPool -spec 'C:\Json\InstantClone.json' -PoolName 'InsPool1'-NamingPattern 'INSPool-' + Creates new instant clone pool by reading few parameters from json and few parameters from command line. .OUTPUTS None @@ -5229,17 +5230,17 @@ function Remove-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Delete a given farm. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer -Confirm:$false + Delete a given farm. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. .EXAMPLE - Deletes a given Farm object(s). For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. $farm_array | Remove-HVFarm -HvServer $hvServer + Deletes a given Farm object(s). For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. .EXAMPLE + C:\PS>$farm1 = Get-HVFarm -FarmName 'Farm-01' + C:\PS>Remove-HVFarm -Farm $farm1 Deletes a given Farm object. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. - $farm1 = Get-HVFarm -FarmName 'Farm-01' - Remove-HVFarm -Farm $farm1 .OUTPUTS None @@ -5347,16 +5348,16 @@ function Remove-HVPool { Logs off a session forcibly to virtual machine(s). This operation will also log off a locked session. .EXAMPLE - Deletes pool from disk with given parameters PoolName etc. Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk -Confirm:$false + Deletes pool from disk with given parameters PoolName etc. .EXAMPLE - Deletes specified pool from disk $pool_array | Remove-HVPool -HvServer $hvServer -DeleteFromDisk + Deletes specified pool from disk .EXAMPLE - Deletes specified pool and VM(s) associations are removed from view Manager Remove-HVPool -Pool $pool1 + Deletes specified pool and VM(s) associations are removed from view Manager .OUTPUTS None @@ -5506,24 +5507,24 @@ function Set-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Updates farm configuration by using json file Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' -Confirm:$false + Updates farm configuration by using json file .EXAMPLE - Updates farm configuration with given parameters key and value Set-HVFarm -FarmName 'Farm-01' -Key 'base.description' -Value 'updated description' + Updates farm configuration with given parameters key and value .EXAMPLE - Updates farm(s) configuration with given parameters key and value $farm_array | Set-HVFarm -Key 'base.description' -Value 'updated description' + Updates farm(s) configuration with given parameters key and value .EXAMPLE - Enables provisioning to specified farm Set-HVFarm -farm 'Farm2' -Start + Enables provisioning to specified farm .EXAMPLE - Enables specified farm Set-HVFarm -farm 'Farm2' -Enable + Enables specified farm .OUTPUTS None @@ -5709,28 +5710,28 @@ function Set-HVPool { Path of the JSON specification file containing key/value pair. .EXAMPLE - Updates pool configuration by using json file Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' -Confirm:$false + Updates pool configuration by using json file .EXAMPLE - Updates pool configuration with given parameters key and value Set-HVPool -PoolName 'RDSPool' -Key 'base.description' -Value 'update description' + Updates pool configuration with given parameters key and value .Example - Disables specified pool Set-HVPool -PoolName 'LnkClone' -Disable + Disables specified pool .Example - Enables specified pool Set-HVPool -PoolName 'LnkClone' -Enable + Enables specified pool .Example - Enables provisioning to specified pool Set-HVPool -PoolName 'LnkClone' -Start + Enables provisioning to specified pool .Example - Disables provisioning to specified pool Set-HVPool -PoolName 'LnkClone' -Stop + Disables provisioning to specified pool .OUTPUTS None @@ -5951,25 +5952,25 @@ function Start-HVFarm { Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. .EXAMPLE - Requests a recompose of RDS Servers in the specified automated farm Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' -Confirm:$false + Requests a recompose of RDS Servers in the specified automated farm .EXAMPLE + C:\PS>$myTime = Get-Date '10/03/2016 12:30:00' + C:\PS>Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime Requests a recompose task for automated farm in specified time - $myTime = Get-Date '10/03/2016 12:30:00' - Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime .EXAMPLE - Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. Start-HVFarm -Farm 'ICFarm-01' -ScheduleMaintenance -MaintenanceMode IMMEDIATE + Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. .EXAMPLE - Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. Start-HVFarm -ScheduleMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' + Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. .EXAMPLE - Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. Start-HVFarm -CancelMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING + Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. .OUTPUTS None @@ -6331,25 +6332,25 @@ function Start-HVPool { View API service object of Connect-HVServer cmdlet. .EXAMPLE - Requests a recompose of machines in the specified pool Start-HVPool -Recompose -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' + Requests a recompose of machines in the specified pool .EXAMPLE - Requests a refresh of machines in the specified pool Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -Confirm:$false + Requests a refresh of machines in the specified pool .EXAMPLE + C:\PS>$myTime = Get-Date '10/03/2016 12:30:00' + C:\PS>Start-HVPool -Rebalance -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -StartTime $myTime Requests a rebalance of machines in a pool with specified time - $myTime = Get-Date '10/03/2016 12:30:00' - Start-HVPool -Rebalance -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -StartTime $myTime .EXAMPLE - Requests an update of push image operation on the specified Instant Clone Engine sourced pool Start-HVPool -SchedulePushImage -Pool 'InstantPool' -LogoffSetting FORCE_LOGOFF -ParentVM 'InsParentVM' -SnapshotVM 'InsSnapshotVM' + Requests an update of push image operation on the specified Instant Clone Engine sourced pool .EXAMPLE - Requests a cancellation of the current scheduled push image operation on the specified Instant Clone Engine sourced pool Start-HVPool -CancelPushImage -Pool 'InstantPool' + Requests a cancellation of the current scheduled push image operation on the specified Instant Clone Engine sourced pool .OUTPUTS None @@ -6774,20 +6775,20 @@ function Get-HVMachine { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Queries VM(s) with given parameter poolName Get-HVDesktop -PoolName 'ManualPool' + Queries VM(s) with given parameter poolName .EXAMPLE - Queries VM(s) with given parameter machineName Get-HVDesktop -MachineName 'PowerCLIVM' + Queries VM(s) with given parameter machineName .EXAMPLE - Queries VM(s) with given parameter vm state Get-HVDesktop -State CUSTOMIZING + Queries VM(s) with given parameter vm state .EXAMPLE - Queries VM(s) with given parameter dnsName with wildcard character * Get-HVDesktop -DnsName 'powercli-*' + Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS Returns list of objects of type MachineInfo @@ -6895,20 +6896,20 @@ function Get-HVMachineSummary { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Queries VM(s) with given parameter poolName Get-HVDesktopSummary -PoolName 'ManualPool' + Queries VM(s) with given parameter poolName .EXAMPLE - Queries VM(s) with given parameter machineName Get-HVDesktopSummary -MachineName 'PowerCLIVM' + Queries VM(s) with given parameter machineName .EXAMPLE - Queries VM(s) with given parameter vm state Get-HVDesktopSummary -State CUSTOMIZING + Queries VM(s) with given parameter vm state .EXAMPLE - Queries VM(s) with given parameter dnsName with wildcard character * Get-HVDesktopSummary -DnsName 'powercli-*' + Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS Returns list of objects of type MachineNamesView @@ -6986,12 +6987,12 @@ function Get-HVPoolSpec { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Converts DesktopInfo to DesktopSpec Get-HVPoolSpec -DesktopInfo $DesktopInfoObj + Converts DesktopInfo to DesktopSpec .EXAMPLE - Converts DesktopInfo to DesktopSpec and also dumps json object Get-HVPool -PoolName 'LnkClnJson' | Get-HVPoolSpec -FilePath "C:\temp\LnkClnJson.json" + Converts DesktopInfo to DesktopSpec and also dumps json object .OUTPUTS Returns desktop specification @@ -7222,8 +7223,8 @@ function Get-HVInternalName { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Decodes Horizon API Id and returns human readable name Get-HVInternalName -EntityId $entityId + Decodes Horizon API Id and returns human readable name .OUTPUTS Returns human readable name @@ -7372,28 +7373,28 @@ function New-HVEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Associate a user/group with a pool New-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'InsClnPol' -Confirm:$false + Associate a user/group with a pool .EXAMPLE - Associate a user/group with a application New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'Calculator' -ResourceType Application + Associate a user/group with a application .EXAMPLE - Associate a user/group with a URLRedirection settings New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + Associate a user/group with a URLRedirection settings .EXAMPLE - Associate a user/group with a desktop entitlement New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'GE1' -ResourceType GlobalEntitlement + Associate a user/group with a desktop entitlement .EXAMPLE - Associate a user/group with a application entitlement New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + Associate a user/group with a application entitlement .EXAMPLE - Associate a user/group with list of pools $pools = Get-HVPool; $pools | New-HVEntitlement -User 'adviewdev\administrator' -Confirm:$false + Associate a user/group with list of pools .NOTES @@ -7617,20 +7618,20 @@ function Get-HVEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Gets all the entitlements related to application pool Get-HVEntitlement -ResourceType Application + Gets all the entitlements related to application pool .EXAMPLE - Gets entitlements specific to user or group name and application resource Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'calculator' -ResourceType Application + Gets entitlements specific to user or group name and application resource .EXAMPLE - Gets entitlements specific to user or group and URLRedirection resource Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + Gets entitlements specific to user or group and URLRedirection resource .EXAMPLE - Gets entitlements specific to user or group and GlobalEntitlement resource Get-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'GE1' -ResourceType GlobalEntitlement + Gets entitlements specific to user or group and GlobalEntitlement resource .NOTES Author : Praveen Mathamsetty. @@ -7829,16 +7830,16 @@ function Remove-HVEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Deletes entitlement between a user/group and a pool resource Remove-HVEntitlement -User 'administrator@adviewdev' -ResourceName LnkClnJSon -Confirm:$false + Deletes entitlement between a user/group and a pool resource .EXAMPLE - Deletes entitlement between a user/group and a Application resource Remove-HVEntitlement -User 'adviewdev\puser2' -ResourceName 'calculator' -ResourceType Application + Deletes entitlement between a user/group and a Application resource .EXAMPLE - Deletes entitlement between a user/group and a GlobalApplicationEntitlement resource Remove-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + Deletes entitlement between a user/group and a GlobalApplicationEntitlement resource .NOTES Author : Praveen Mathamsetty. @@ -8068,16 +8069,16 @@ PARAMETER Key first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Moving the machine in to Maintenance mode using machine name Set-HVMachine -MachineName 'Agent_Praveen' -Maintenance ENTER_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine name .EXAMPLE - Moving the machine in to Maintenance mode using machine object(s) Get-HVMachine -MachineName 'Agent_Praveen' | Set-HVMachine -Maintenance ENTER_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine object(s) .EXAMPLE - Moving the machine in to Maintenance mode using machine object(s) $machine = Get-HVMachine -MachineName 'Agent_Praveen'; Set-HVMachine -Machine $machine -Maintenance EXIT_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine object(s) .OUTPUTS None @@ -8244,12 +8245,12 @@ function New-HVGlobalEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Creates new global application entitlement New-HVGlobalEntitlement -DisplayName 'GE_APP' -Type APPLICATION_ENTITLEMENT + Creates new global application entitlement .EXAMPLE - Creates new global desktop entitlement New-HVGlobalEntitlement -DisplayName 'GE_DESKTOP' -Type DESKTOP_ENTITLEMENT + Creates new global desktop entitlement .NOTES @@ -8475,8 +8476,8 @@ function Get-HVGlobalEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Retrieves global application/desktop entitlement(s) with displayName 'GEAPP' Get-HVGlobalEntitlement -DisplayName 'GEAPP' + Retrieves global application/desktop entitlement(s) with displayName 'GEAPP' .NOTES @@ -8554,12 +8555,12 @@ function Remove-HVGlobalEntitlement { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Deletes global application/desktop entitlement with displayName 'GE_APP' Remove-HVGlobalEntitlement -DisplayName 'GE_APP' + Deletes global application/desktop entitlement with displayName 'GE_APP' .EXAMPLE - Deletes global application/desktop entitlement(s), if displayName matches with 'GE_*' Get-HVGlobalEntitlement -DisplayName 'GE_*' | Remove-HVGlobalEntitlement + Deletes global application/desktop entitlement(s), if displayName matches with 'GE_*' .NOTES From dcf76d6b1a39c2e6bc09b2b86a59ed9eca929194 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 10 Feb 2017 15:06:02 +0530 Subject: [PATCH 57/90] Example Text correction Example Text correction --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 01b1170..009cb47 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -928,7 +928,7 @@ function Get-HVFarm { Switch to get list of FarmSummaryView or FarmInfo objects in the result. If it is true a list of FarmInfo objects is returned ohterwise a list of FarmSummaryView objects is returned. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Get-HVFarm -FarmName 'Farm-01' @@ -1034,7 +1034,7 @@ function Get-HVFarmSummary { search for farms which are enabled .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Get-HVFarmSummary -FarmName 'Farm-01' @@ -1215,7 +1215,7 @@ function Get-HVPool { .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVPool -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true @@ -1345,7 +1345,7 @@ function Get-HVPoolSummary { .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVPoolSummary -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true @@ -1692,7 +1692,7 @@ function Get-HVQueryResult { .PARAMETER HvServer Reference to Horizon View Server to query the data from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVQueryResult DesktopSummaryView @@ -1939,7 +1939,7 @@ function New-HVFarm { Path of the JSON specification file. .PARAMETER HvServer - Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" @@ -3389,7 +3389,7 @@ function New-HVPool { .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer. + first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE C:\PS>New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 0 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root @@ -5227,7 +5227,7 @@ function Remove-HVFarm { Object(s) of the farm to be deleted. Object(s) should be of type FarmSummaryView/FarmInfo. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer -Confirm:$false @@ -5504,7 +5504,7 @@ function Set-HVFarm { Path of the JSON specification file containing key/value pair. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' -Confirm:$false @@ -5949,7 +5949,7 @@ function Start-HVFarm { This property has a default value of 1. This property has values 1-100. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' -Confirm:$false @@ -6772,7 +6772,7 @@ function Get-HVMachine { .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVDesktop -PoolName 'ManualPool' @@ -6893,7 +6893,7 @@ function Get-HVMachineSummary { .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVDesktopSummary -PoolName 'ManualPool' @@ -6984,7 +6984,7 @@ function Get-HVPoolSpec { .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVPoolSpec -DesktopInfo $DesktopInfoObj @@ -7220,7 +7220,7 @@ function Get-HVInternalName { .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVInternalName -EntityId $entityId @@ -7354,13 +7354,13 @@ function New-HVEntitlement { This represents a simple association between a single user/group and a resource that they can be assigned. .PARAMETER User - User prinicipal name of user or group + User principal name of user or group .PARAMETER ResourceName - The resource(Application, Pool etc.) name + The resource(Application, Desktop etc.) name .PARAMETER Resource - Object(s) of the resource(Application, Desktop etc) to entitle + Object(s) of the resource(Application, Desktop etc.) to entitle .PARAMETER ResourceType Type of Resource(Application, Desktop etc) @@ -7370,7 +7370,7 @@ function New-HVEntitlement { .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE New-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'InsClnPol' -Confirm:$false @@ -7599,23 +7599,23 @@ function Get-HVEntitlement { Provides entitlement Info between a single user/group and a resource that they can be assigned. .PARAMETER User - User prinicipal name of user or group + User principal name of user or group .PARAMETER ResourceName The resource(Application, Pool etc.) name .PARAMETER Resource - Object(s) of the resource(Application, Desktop etc) to entitle + Object(s) of the resource(Application, Desktop etc.) to entitle .PARAMETER ResourceType - Type of Resource(Application, Desktop etc) + Type of Resource(Application, Desktop etc.) .PARAMETER Type Whether or not this is a group or a user. .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVEntitlement -ResourceType Application @@ -7811,13 +7811,13 @@ function Remove-HVEntitlement { Removes entitlement between a single user/group and a resource that already been assigned. .PARAMETER User - User prinicipal name of user or group + User principal name of user or group .PARAMETER ResourceName - The resource(Application, Pool etc.) name + The resource(Application, Desktop etc.) name .PARAMETER Resource - Object(s) of the resource(Application, Desktop etc) to entitle + Object(s) of the resource(Application, Desktop etc.) to entitle .PARAMETER ResourceType Type of Resource(Application, Desktop etc) @@ -7827,7 +7827,7 @@ function Remove-HVEntitlement { .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Remove-HVEntitlement -User 'administrator@adviewdev' -ResourceName LnkClnJSon -Confirm:$false @@ -8066,7 +8066,7 @@ PARAMETER Key .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Set-HVMachine -MachineName 'Agent_Praveen' -Maintenance ENTER_MAINTENANCE_MODE @@ -8242,7 +8242,7 @@ function New-HVGlobalEntitlement { .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE New-HVGlobalEntitlement -DisplayName 'GE_APP' -Type APPLICATION_ENTITLEMENT @@ -8473,7 +8473,7 @@ function Get-HVGlobalEntitlement { .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVGlobalEntitlement -DisplayName 'GEAPP' From 60f4948ea7612f9e9a5e4188922298dce1601a5b Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 10 Feb 2017 16:06:49 +0530 Subject: [PATCH 58/90] Changing Information message Changing Information message in entitlements. --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 009cb47..0c8450d 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -7576,13 +7576,13 @@ function New-HVEntitlement { } $base = New-Object VMware.HV.UserEntitlementBase $base.UserOrGroup = $results.id + Write-host $ResourceObjs.Length " resource(s) will be entitled with UserOrGroup: " $User foreach ($ResourceObj in $ResourceObjs) { $base.Resource = $ResourceObj.id if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $id = $services.UserEntitlement.UserEntitlement_Create($base) } } - Write-host $ResourceObjs.Length " resource(s) entitled with User or group: " $User } end { [System.gc]::collect() @@ -7916,10 +7916,10 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.localData.desktopUserEntitlements + Write-Host $userEntitlements.Length " desktopUserEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } - Write-Host $userEntitlements.Length " desktopUserEntitlement(s) are removed for UserOrGroup " $user } } } @@ -7936,10 +7936,10 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.localData.applicationUserEntitlements + Write-Host $userEntitlements.Length " applicationUserEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } - Write-Host $userEntitlements.Length " applicationUserEntitlement(s) are removed for UserOrGroup " $user } } } @@ -7966,16 +7966,17 @@ function Remove-HVEntitlement { foreach ($result in $Results) { if ($result.GetType().Name -eq 'EntitledUserOrGroupLocalSummaryView') { $userEntitlements = $result.localData.urlRedirectionUserEntitlements + Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } } else { $userEntitlements = $result.globalData.urlRedirectionUserEntitlements + Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } } - Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) are removed for UserOrGroup " $user } } } @@ -7996,10 +7997,10 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.globalData.globalUserApplicationEntitlements + Write-Host $userEntitlements.Length " GlobalApplicationEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } - Write-Host $userEntitlements.Length " GlobalApplicationEntitlement(s) are removed for UserOrGroup " $user } } } @@ -8020,10 +8021,10 @@ function Remove-HVEntitlement { if ($results) { foreach ($result in $Results) { $userEntitlements = $result.globalData.globalUserEntitlements + Write-Host $userEntitlements.Length " GlobalEntitlement(s) will be removed for UserOrGroup " $user if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) } - Write-Host $userEntitlements.Length " GlobalEntitlement(s) are removed for UserOrGroup " $user } } From b885a9a39427635cc9872750932093868cb1d7a1 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Wed, 15 Feb 2017 20:25:48 +0530 Subject: [PATCH 59/90] Updating Resource Name Get Help for AF HVEntitlement. Updating Resource Name for Get Help for Advanced function HVEntitlement. --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 0c8450d..7d405c1 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -7357,7 +7357,8 @@ function New-HVEntitlement { User principal name of user or group .PARAMETER ResourceName - The resource(Application, Desktop etc.) name + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. .PARAMETER Resource Object(s) of the resource(Application, Desktop etc.) to entitle @@ -7602,7 +7603,8 @@ function Get-HVEntitlement { User principal name of user or group .PARAMETER ResourceName - The resource(Application, Pool etc.) name + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. .PARAMETER Resource Object(s) of the resource(Application, Desktop etc.) to entitle @@ -7814,7 +7816,8 @@ function Remove-HVEntitlement { User principal name of user or group .PARAMETER ResourceName - The resource(Application, Desktop etc.) name + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. .PARAMETER Resource Object(s) of the resource(Application, Desktop etc.) to entitle From f6aebe0c7b8f0f48e0c633c2e271856adb93523a Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 17 Feb 2017 18:07:18 +0530 Subject: [PATCH 60/90] MaintenanceSettings fix for farm MaintenanceSettings fix for instant clone farm --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 7d405c1..97ffe68 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -6163,6 +6163,8 @@ function Start-HVFarm { if ($startTime) { $spec.ScheduledTime = $StartTime } + $spec.LogoffSetting = $LogoffSetting + $spec.StopOnFirstError = $StopOnFirstError if ($MaintenanceMode -eq "RECURRING") { $spec.RecurringMaintenanceSettings = New-Object VMware.Hv.FarmRecurringMaintenanceSettings $spec.RecurringMaintenanceSettings.MaintenancePeriod = $MaintenancePeriod @@ -6185,8 +6187,6 @@ function Start-HVFarm { #image settings are specified if ($ParentVM -and $SnapshotVM) { $spec.ImageMaintenanceSettings = New-Object VMware.Hv.FarmImageMaintenanceSettings - $spec.ImageMaintenanceSettings.LogoffSetting = $LogoffSetting - $spec.ImageMaintenanceSettings.StopOnFirstError = $StopOnFirstError $vcId = Get-VcenterID -services $services -vCenter $Vcenter if ($null -eq $vcId) { Write-Error "VCenter is required if you specify ParentVM name." From 86ebda35d44034569097f3e940bd2d31fab7ce02 Mon Sep 17 00:00:00 2001 From: mycloudrevolution Date: Sun, 19 Feb 2017 11:52:43 +0100 Subject: [PATCH 61/90] added function Get-NICDetails --- Modules/Get-NICDetails.psm1 | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Modules/Get-NICDetails.psm1 diff --git a/Modules/Get-NICDetails.psm1 b/Modules/Get-NICDetails.psm1 new file mode 100644 index 0000000..30f1440 --- /dev/null +++ b/Modules/Get-NICDetails.psm1 @@ -0,0 +1,93 @@ +function Get-NICDetails { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2017.02 ver 1.0 Base Release + =========================================================================== + External Code Sources: + - + =========================================================================== + Tested Against Environment: + vSphere Version: ESXi 6.0 U2, ESXi 6.5 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2008 R2, Server 2012 R2 + Keyword: ESXi, NIC, vmnic, Driver, Firmware + =========================================================================== + + .DESCRIPTION + Reports Firmware and Driver Details for your ESXi vmnics. + + .Example + Get-NICDetails -Clustername * + + .PARAMETER Clustername + Name or Wildcard of your vSphere Cluster Name to process. + + +#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)] + [ValidateNotNullorEmpty()] + [String] $Clustername + +) + +Begin { + $Validate = $True + + if (($myCluster = Get-Cluster -Name $Clustername).count -lt 1) { + $Validate = $False + thow "No Cluster '$myCluster' found!" + } + +} + +Process { + + $MyView = @() + if ($Validate -eq $True) { + + foreach ($myVMhost in ($myCluster | Get-VMHost)) { + + $esxcli2 = Get-ESXCLI -VMHost $myVMhost -V2 + $niclist = $esxcli2.network.nic.list.invoke() + + $nicdetails = @() + foreach ($nic in $niclist) { + + $args = $esxcli2.network.nic.get.createargs() + $args.nicname = $nic.name + $nicdetail = $esxcli2.network.nic.get.Invoke($args) + $nicdetails += $nicdetail + + } + ForEach ($nicdetail in $nicdetails){ + $NICReport = [PSCustomObject] @{ + Host = $myVMhost.Name + vmnic = $nicdetail.Name + LinkStatus = $nicdetail.LinkStatus + BusInfo = $nicdetail.driverinfo.BusInfo + Driver = $nicdetail.driverinfo.Driver + FirmwareVersion = $nicdetail.driverinfo.FirmwareVersion + DriverVersion = $nicdetail.driverinfo.Version + } + $MyView += $NICReport + } + + } + + $MyView + + } +} +} \ No newline at end of file From 373579a2914be917bb7475aae321288fb582c238 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 21 Feb 2017 00:22:19 +0530 Subject: [PATCH 62/90] Suppresstext info Suppress text info, when no result found with given search parameters. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 113 +++++++++++++----- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 97ffe68..9383c24 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -423,7 +423,7 @@ function Add-HVRDSServer { process { $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys try { - $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break @@ -924,8 +924,8 @@ function Get-HVFarm { .PARAMETER Enabled search for farms which are enabled -.PARAMETER Full - Switch to get list of FarmSummaryView or FarmInfo objects in the result. If it is true a list of FarmInfo objects is returned ohterwise a list of FarmSummaryView objects is returned. +.PARAMETER SuppressInfo + Suppress text info, when no farm found with given search parameters .PARAMETER HvServer Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. @@ -987,6 +987,10 @@ function Get-HVFarm { [boolean] $Enabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -998,8 +1002,10 @@ function Get-HVFarm { } $farmList = Find-HVFarm -Param $PSBoundParameters if (! $farmList) { - Write-Host "Get-HVFarm: No Farm Found with given search parameters" - break + if (! $SuppressInfo) { + Write-Host "Get-HVFarm: No Farm Found with given search parameters" + } + return $farmList } $farm_service_helper = New-Object VMware.Hv.FarmService $queryResults = @() @@ -1020,18 +1026,21 @@ function Get-HVFarmSummary { This function queries the specified Connection Server for farms which are configured on the server. If no farm is configured on the specified connection server or no farm matches the given search criteria, it will return null. .PARAMETER FarmName - farmName to be searched + FarmName to be searched .PARAMETER FarmDisplayName - farmDisplayName to be searched + FarmDisplayName to be searched .PARAMETER FarmType - farmType to be searched. It can take following values: + FarmType to be searched. It can take following values: "AUTOMATED" - search for automated farms only 'MANUAL' - search for manual farms only .PARAMETER Enabled - search for farms which are enabled + Search for farms which are enabled + +.PARAMETER SuppressInfo + Suppress text info, when no farm found with given search parameters .PARAMETER HvServer Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. @@ -1093,6 +1102,10 @@ function Get-HVFarmSummary { [boolean] $Enabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1102,7 +1115,10 @@ function Get-HVFarmSummary { Write-Error "Could not retrieve ViewApi services from connection object" break } - Return Find-HVFarm -Param $PSBoundParameters + $farmList = Find-HVFarm -Param $PSBoundParameters + if (!$farmList -and !$SuppressInfo) { + Write-Host "Get-HVFarmSummary: No Farm Found with given search parameters" + } } function Find-HVFarm { @@ -1213,6 +1229,9 @@ function Get-HVPool { If the value is true then only pools which are enabled would be returned. If the value is false then only pools which are disabled would be returned. +.PARAMETER SuppressInfo + Suppress text info, when no pool found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer @@ -1279,6 +1298,10 @@ function Get-HVPool { [boolean] $ProvisioningEnabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1290,8 +1313,10 @@ function Get-HVPool { } $poolList = Find-HVPool -Param $PSBoundParameters if (! $poolList) { - Write-Host "Get-HVPool: No Pool Found with given search parameters" - break + if (! $SuppressInfo) { + Write-Host "Get-HVPool: No Pool Found with given search parameters" + } + return $poolList } $queryResults = @() $desktop_helper = New-Object VMware.Hv.DesktopService @@ -1343,6 +1368,9 @@ function Get-HVPoolSummary { If the value is true then only pools which are enabled would be returned. If the value is false then only pools which are disabled would be returned. +.PARAMETER SuppressInfo + Suppress text info, when no pool found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer @@ -1409,6 +1437,10 @@ function Get-HVPoolSummary { [boolean] $ProvisioningEnabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1418,7 +1450,11 @@ function Get-HVPoolSummary { Write-Error "Could not retrieve ViewApi services from connection object" break } - Return Find-HVPool -Param $psboundparameters + $pool_list = Find-HVPool -Param $psboundparameters + if (!$pool_list -and !$suppressInfo) { + Write-Host "Get-HVPoolSummary: No Pool Found with given search parameters" + } + Return $pool_list } function Find-HVPool { @@ -2343,7 +2379,7 @@ function New-HVFarm { $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($farmName) { try { - $sourceFarm = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer + $sourceFarm = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break @@ -4073,7 +4109,7 @@ function New-HVPool { $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($poolName) { try { - $sourcePool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $sourcePool = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -5285,7 +5321,7 @@ function Remove-HVFarm { $farmList = @() if ($farmName) { try { - $farmSpecObj = Get-HVFarm -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarm -farmName $farmName -hvServer $hvServer -SuppressInfo $true } catch { Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" break @@ -5408,7 +5444,7 @@ function Remove-HVPool { $poolList = @() if ($poolName) { try { - $myPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $myPools = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -5589,7 +5625,7 @@ function Set-HVFarm { $farmList = @{} if ($farmName) { try { - $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break @@ -5798,7 +5834,7 @@ function Set-HVPool { $poolList = @{} if ($poolName) { try { - $desktopPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $desktopPools = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -5811,6 +5847,9 @@ function Set-HVPool { } $poolList.add($desktopObj.id, $desktopObj.DesktopSummaryData.Name) } + } else { + Write-Error "No desktopsummarydata found with pool name: [$poolName]" + break } } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Pool) { foreach ($item in $pool) { @@ -6085,7 +6124,7 @@ function Start-HVFarm { } elseif ($farm.GetType().name -eq 'String') { try { - $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer + $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer -SuppressInfo $true } catch { Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" break @@ -6458,7 +6497,7 @@ function Start-HVPool { $type = $item.desktopsummarydata.type } elseif ($item.GetType().name -eq 'String') { try { - $poolObj = Get-HVPoolSummary -poolName $item -hvServer $hvServer + $poolObj = Get-HVPoolSummary -poolName $item -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -6661,7 +6700,7 @@ function Find-HVMachine { try { if ($params['PoolName']) { - $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -hvServer $params['HvServer'] + $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -suppressInfo $true -hvServer $params['HvServer'] if ($poolObj.Length -ne 1) { Write-Host "Failed to retrieve specific pool object with given PoolName : " $params['PoolName'] break; @@ -6891,6 +6930,9 @@ function Get-HVMachineSummary { If the value is null or not provided then filter will not be applied, otherwise the virtual machines which has display name same as value will be returned. +.PARAMETER SuppressInfo + Suppress text info, when no machine found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer @@ -6957,6 +6999,10 @@ function Get-HVMachineSummary { [string] $JsonFilePath, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -6968,6 +7014,9 @@ function Get-HVMachineSummary { } $machineList = Find-HVMachine -Param $PSBoundParameters + if (!$machineList -and !$SuppressInfo) { + Write-Host "Get-HVMachineSummary: No machine(s) found with given search parameters" + } return $machineList } @@ -7467,7 +7516,7 @@ function New-HVEntitlement { switch($ResourceType){ "Desktop" { if ($ResourceName) { - $ResourceObjs = Get-HVPool -PoolName $ResourceName + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer if (! $ResourceObjs) { Write-Host "No pool found with given resourceName: " $ResourceName return @@ -7704,7 +7753,7 @@ function Get-HVEntitlement { switch($ResourceType) { "Desktop" { if ($ResourceName) { - $ResourceObjs = Get-HVPool -PoolName $ResourceName -HvServer $HvServer + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer if (! $ResourceObjs) { Write-Host "No pool found with given resourceName: " $ResourceName return @@ -7908,7 +7957,7 @@ function Remove-HVEntitlement { $info = $services.PodFederation.PodFederation_get() switch($ResourceType) { "Desktop" { - $ResourceObjs = Get-HVPool -PoolName $ResourceName -HvServer $HvServer + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer if (! $ResourceObjs) { Write-Host "No pool found with given resourceName: " $ResourceName return @@ -8140,7 +8189,7 @@ PARAMETER Key $machineList = @{} if ($machineName) { try { - $machines = Get-HVMachineSummary -MachineName $machineName -hvServer $hvServer + $machines = Get-HVMachineSummary -MachineName $machineName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVMachineSummary advanced function is loaded, $_" break @@ -8475,6 +8524,9 @@ function Get-HVGlobalEntitlement { .PARAMETER Description Description of Global Entitlement. +.PARAMETER SuppressInfo + Suppress text info, when no global entitlement(s) found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer @@ -8510,6 +8562,10 @@ function Get-HVGlobalEntitlement { [String] $Description, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -8529,9 +8585,8 @@ function Get-HVGlobalEntitlement { $result = @() $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalEntitlementSummaryView' $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalApplicationEntitlementInfo' - if (! $result) { + if (!$result -and !$SuppressInfo) { Write-Host "Get-HVGlobalEntitlement: No global entitlement Found with given search parameters" - break } return $result } @@ -8611,7 +8666,7 @@ function Remove-HVGlobalEntitlement { $GeList = @() if ($DisplayName) { try { - $GeList = Get-HVGlobalEntitlement -DisplayName $DisplayName -hvServer $hvServer + $GeList = Get-HVGlobalEntitlement -DisplayName $DisplayName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVGlobalEntitlement advanced function is loaded, $_" break From 10b540c8c843d3fe4cad8a33a360725cecc4f293 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Tue, 21 Feb 2017 00:27:38 +0530 Subject: [PATCH 63/90] suppress text Info bug fix suppress text Info bug fix --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 9383c24..2756357 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1119,6 +1119,7 @@ function Get-HVFarmSummary { if (!$farmList -and !$SuppressInfo) { Write-Host "Get-HVFarmSummary: No Farm Found with given search parameters" } + Return $farmList } function Find-HVFarm { From 94f8f19d840c7b621a2bf41af8a6cdf149b92fdb Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Wed, 22 Feb 2017 18:48:24 +0530 Subject: [PATCH 64/90] Adding prefix to output message Adding prefix to output message in AF Get-HVMachine --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 2756357..a4a46d4 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -6888,7 +6888,7 @@ function Get-HVMachine { $machineList = Find-HVMachine -Param $PSBoundParameters if (!$machineList) { - Write-Host "No Virtual Machine(s) Found with given search parameters" + Write-Host "Get-HVMachine: No Virtual Machine(s) Found with given search parameters" break } $queryResults = @() From 530764394a87ffa5729810f2a9d4814848a46fc6 Mon Sep 17 00:00:00 2001 From: William Lam Date: Thu, 2 Mar 2017 07:06:53 -0800 Subject: [PATCH 65/90] Adding Get-VAMIBackupSize function --- Modules/VAMI.psm1 | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index a66d3c1..6c8038e 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -3,7 +3,6 @@ .NOTES =========================================================================== Created by: William Lam - Date: Jan 20, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -40,7 +39,6 @@ Function Get-VAMIHealth { .NOTES =========================================================================== Created by: William Lam - Date: Jan 25, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -89,7 +87,6 @@ Function Get-VAMIAccess { .NOTES =========================================================================== Created by: William Lam - Date: Jan 26, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -122,7 +119,6 @@ Function Get-VAMITime { .NOTES =========================================================================== Created by: William Lam - Date: Jan 27, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -164,7 +160,6 @@ Function Get-VAMINetwork { .NOTES =========================================================================== Created by: William Lam - Date: Jan 31, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -213,7 +208,6 @@ Function Get-VAMIDisks { .NOTES =========================================================================== Created by: William Lam - Date: Feb 02, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -240,7 +234,6 @@ Function Start-VAMIDiskResize { .NOTES =========================================================================== Created by: William Lam - Date: Feb 02, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -264,7 +257,6 @@ Function Get-VAMIStatsList { .NOTES =========================================================================== Created by: William Lam - Date: Feb 06, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -291,7 +283,6 @@ Function Get-VAMIStorageUsed { .NOTES =========================================================================== Created by: William Lam - Date: Feb 06, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -389,7 +380,6 @@ Function Get-VAMIService { .NOTES =========================================================================== Created by: William Lam - Date: Feb 08, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -455,7 +445,6 @@ Function Start-VAMIService { .NOTES =========================================================================== Created by: William Lam - Date: Feb 08, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -493,7 +482,6 @@ Function Stop-VAMIService { .NOTES =========================================================================== Created by: William Lam - Date: Feb 08, 2016 Organization: VMware Blog: www.virtuallyghetto.com Twitter: @lamw @@ -524,4 +512,38 @@ Function Stop-VAMIService { } catch { Write-Error $Error[0].exception.Message } +} + +Function Get-VAMIBackupSize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the backup size of the VCSA from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return the current backup size of the VCSA (common and core data) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIBackupSize +#> + $recoveryAPI = Get-CisService 'com.vmware.appliance.recovery.backup.parts' + $backupParts = $recoveryAPI.list() | select id + + $estimateBackupSize = 0 + $backupPartSizes = "" + foreach ($backupPart in $backupParts) { + $partId = $backupPart.id.value + $partSize = $recoveryAPI.get($partId) + $estimateBackupSize += $partSize + $backupPartSizes += $partId + " data is " + $partSize + " MB`n" + } + + Write-Host "Estimated Backup Size: $estimateBackupSize MB" + Write-Host $backupPartSizes } \ No newline at end of file From 0038d751f00223067cd39cddf9cda06a561c5027 Mon Sep 17 00:00:00 2001 From: William Lam Date: Mon, 6 Mar 2017 14:53:16 -0800 Subject: [PATCH 66/90] Adding {New,Get,Remove}-VAMIUser functions --- Modules/VAMI.psm1 | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 index 6c8038e..92c5d5f 100755 --- a/Modules/VAMI.psm1 +++ b/Modules/VAMI.psm1 @@ -546,4 +546,171 @@ Function Get-VAMIBackupSize { Write-Host "Estimated Backup Size: $estimateBackupSize MB" Write-Host $backupPartSizes +} + +Function Get-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VAMI local users using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to retrieve VAMI local users + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIUser +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + $userResults = @() + + if($Name -ne "") { + try { + $user = $userAPI.get($name) + + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $users = $userAPI.list() + + foreach ($user in $users) { + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } + } + $userResults +} + +Function New-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to create new VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to create a new VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + New-VAMIUser -name lamw -fullname "William Lam" -role "operator" -email "lamw@virtuallyghetto.com" -password "VMware1!" +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$true) + ] + [String]$fullname, + [Parameter( + Mandatory=$true) + ] + [ValidateSet("admin","operator","superAdmin")][String]$role, + [Parameter( + Mandatory=$false) + ] + [String]$email="", + [Parameter( + Mandatory=$true) + ] + [String]$password + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + $createSpec = $userAPI.Help.add.config.CreateExample() + + $createSpec.username = $name + $createSpec.fullname = $fullname + $createSpec.role = $role + $createSpec.email = $email + $createSpec.password = [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$password + + try { + Write-Host "Creating new user $name ..." + $userAPI.add($createSpec) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Remove-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to remove VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to remove VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$false) + ] + [boolean]$confirm=$false + ) + + if(!$confirm) { + $answer = Read-Host -Prompt "Do you want to delete user $name (Y or N)" + if($answer -eq "Y" -or $answer -eq "y") { + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + try { + Write-Host "Deleting user $name ..." + $userAPI.delete($name) + } catch { + Write-Error $Error[0].exception.Message + } + } + } } \ No newline at end of file From 492f89cc91cc84fb27909bcd220da91747fb0312 Mon Sep 17 00:00:00 2001 From: William Lam Date: Tue, 7 Mar 2017 07:59:26 -0800 Subject: [PATCH 67/90] Adding Proactive HA Module --- Modules/ProactiveHA.psm1 | 468 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 Modules/ProactiveHA.psm1 diff --git a/Modules/ProactiveHA.psm1 b/Modules/ProactiveHA.psm1 new file mode 100644 index 0000000..ea4e92f --- /dev/null +++ b/Modules/ProactiveHA.psm1 @@ -0,0 +1,468 @@ +Function New-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to register a new Proactive HA Provider with vCenter Server + .PARAMETER ProviderName + Name of ProactiveHA Provider + .PARAMETER ComponentType + Name of a supported ComponentType that ProactiveHA supports (Fan, Memory, Network, Power or Storage) + .PARAMETER ComponentDescription + Description of the health check for the given component + .PARAMETER ComponentId + Unique identifier for the given component within a ProactiveHA Provider + .EXAMPLE + New-PHAProvider -ProviderName "virtuallyGhetto" -ComponentType Power -ComponentDescription "Simulated ProactiveHA Provider" -ComponentId "Power" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderName, + [Parameter(Mandatory=$true)][ValidateSet("Fan","Memory","Network","Power","Storage")][String]$ComponentType, + [Parameter(Mandatory=$true)][String]$ComponentDescription, + [Parameter(Mandatory=$true)][String]$ComponentId + ) + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthInfo = [VMware.Vim.HealthUpdateInfo] @{ + ComponentType = $ComponentType + description = $ComponentDescription + Id = $ComponentId + } + + try { + Write-Host "`nRegistering new Proactive HA Provider $ProviderName ..." + $providerId = $healthManager.RegisterHealthUpdateProvider($ProviderName,$healthInfo) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} + +Function Get-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return list of all Proactive HA Providers registered with vCenter Server + .EXAMPLE + Get-PHAProvider +#> + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthProviderResults = @() + $hpIDs = $healthManager.QueryProviderList() + + foreach ($hpID in $hpIDs) { + $hpName = $healthManager.QueryProviderName($hpID) + $hpConfig = $healthManager.QueryHealthUpdateInfos($hpID) + + $hp = [pscustomobject] @{ + ProviderName = $hpName + ProviderID = $hpID + ComponentType = $hpConfig.componentType + ComponentID = $hpConfig.id + Description = $hpConfig.description + } + $healthProviderResults+=$hp + } + $healthProviderResults +} + +Function Remove-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to remove a registered Proactive HA Provider from vCenter Server + .PARAMETER ProviderId + The ProactiveHA provider ID (retrieved from Get-PHAProvider) to unregister + .EXAMPLE + Remove-PHAProvider -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + try { + Write-Host "`nUnregistering Proactive HA Provider $ProviderId ... " + $healthManager.UnregisterHealthUpdateProvider($providerId) + } catch { + if($Error[0].Exception.InnerException.MethodFault.getType().Name -eq "InvalidState") { + Write-host -ForegroundColor Red "The Proactive HA Provider is still in use, please disable it before unregistering" + } else { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Set-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to enable/disable Proactive HA for vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to enable Proactive HA + .PARAMETER ProviderId + Proactive HA Provider ID to enable in vSphere Cluster + .PARAMETER ClusterMode + Whether Proactive HA should be "Automated" or "Manual" for actions it will take + .PARAMETER ModerateRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Moderate issue is observed + .PARAMETER SevereRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Severe issue is observed + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Enabled -ClusterMode Automated -ModerateRemediation QuarantineMode -SevereRemediation QuarantineMode -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Disabled -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$Cluster, + [Parameter(Mandatory=$false)][ValidateSet("Automated","Manual")]$ClusterMode="Manual", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$ModerateRemediation="QuarantineMode", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$SevereRemediation="QuarantineMode", + [Switch]$Enabled, + [Switch]$Disabled + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,Host,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + $vmhosts = $ClusterView.host + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + if($Enabled) { + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if(-not $healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Enabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.AddMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + $healthProviders+=$currentHP + } + if(-not ($healthProviders -contains $ProviderID)) { + $healthProviders+=$ProviderId + } + } else { + $healthProviders+=$ProviderId + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Enabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } + + if($Disabled) { + foreach ($vmhost in $vmhosts) { + if($vmhost.runtime.inQuarantineMode) { + Write-Host -ForegroundColor Red $vmhost.name " is currently still in Quaratine Mode, please remediate this before disabling Proactive HA" + break + } + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + if($currentHP -ne $ProviderId) { + $healthProviders+=$currentHP + } + } + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Disabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + $ClusterView.UpdateViewData() + + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if($healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Disabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.RemoveMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Get-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve Proactive HA configuration for a vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA configuration + .EXAMPLE + Get-PHAConfig -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $phSettings = $ClusterView.ConfigurationEx.InfraUpdateHaConfig + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + $healthProviders = @() + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthProviders+=$providerName + } + + $pHAConfig = [pscustomobject] @{ + Enabled = $phSettings.Enabled + ClusterMode = $phSettings.behavior + ModerateRemediation = $phSettings.ModerateRemediation + SevereRemediation = $phSettings.SevereRemediation + HealthProviders = $healthProviders + } + $pHAConfig + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function Get-PHAHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve the Proactive HA health info for all ESXi hosts in vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA health information + .EXAMPLE + Get-PHAHealth -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthUpdates = $healthManager.QueryHealthUpdates($provider) + + $healthResults = @() + Write-Host -NoNewline -ForegroundColor Magenta "Health summary for Proactive HA Provider $providerName`:`n" + foreach ($healthUpdate in $healthUpdates) { + $vmhost = Get-View $healthUpdate.Entity + + $hr = [PSCustomObject] @{ + Entity = $vmhost.name + Status = $healthUpdate.status + HealthComponentId = $healthUpdate.HealthUpdateInfoId + HealthUpdateId = $healthUpdate.Id + Remediation = $healthUpdate.Remediation + } + $healthResults+=$hr + } + $healthResults + } + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function New-PHASimulation { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return VCHA Configuration + .PARAMETER ProviderId + The Proactive HA Provider ID that you like to simulate a health update from + .PARAMETER EsxiHost + The name of ESXi host to update the health on + .PARAMETER Component + The name of the matching component ID from Proactive HA Provider to simulate a health update from + .PARAMETER HealthStatus + The health value (green, yellow or red) for the given simulated health Update + .PARAMETER Remediation + The remediation message associated with simulated health update + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus green -Remediation "" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus red -Remediation "Please replace my virtual PSU" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$EsxiHost, + [Parameter(Mandatory=$true)][String]$Component, + [Parameter(Mandatory=$true)][ValidateSet("green","red","yellow")][String]$HealthStatus, + [Parameter(Mandatory=$false)][String]$Remediation + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $vmhost = Get-View -ViewType HostSystem -Property Name -Filter @{"name" = $EsxiHost} + + if($vmhost -eq $null) { + Write-Host -ForegroundColor Red "`nUnable to find ESXi host $EsxiHost ..." + break + } + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + # Randomly generating an ID for Health Update + # In general, you would want to generate a specific ID + # which can be referenced between ProactiveHA Provider + # and VMware logs for troubleshooting purposes + $HealthUpdateID = "vghetto-" + (Get-Random -Minimum 1 -Maximum 100000) + + # All other Health Status can have a remediation message + # but for green, it must be an empty string or API call will fail + if($HealthStatus -eq "green") { + $Remediation = "" + } + + $healthUpdate = [VMware.Vim.HealthUpdate] @{ + Entity = $vmhost.moref + HealthUpdateInfoId = $Component + Id = $HealthUpdateId + Status = $HealthStatus + Remediation = $Remediation + } + + try { + Write-Host "`nSimulating Proactive HA Health Update to ..." + Write-Host "`tHost: $EsxiHost " + Write-Host -NoNewline "`tStatus: " + Write-Host -ForegroundColor $HealthStatus "$HealthStatus" + Write-Host "`tRemediation Messsage: $Remediation" + $healthManager.PostHealthUpdates($providerId,$healthUpdate) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} \ No newline at end of file From a8dbe6392916ba3563de75bb2792d5e39908eec1 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 31 Mar 2017 17:00:57 +0530 Subject: [PATCH 68/90] Update VMware.HV.Helper.psm1 Updated AF Version and Horizon build no. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index a4a46d4..723b781 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1,7 +1,7 @@ #Script Module : VMware.Hv.Helper -#Version : 1.0 +#Version : 1.1 -#Copyright 2016 VMware, Inc. All Rights Reserved. +#Copyright © 2016 VMware, Inc. All Rights Reserved. #Permission is hereby granted, free of charge, to any person obtaining a copy of #this software and associated documentation files (the "Software"), to deal in @@ -204,11 +204,11 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 Dependencies : Make sure pool already exists before adding VMs to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 @@ -386,13 +386,13 @@ function Add-HVRDSServer { None .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 Dependencies : Make sure farm already exists before adding RDSServers to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -505,10 +505,10 @@ function Connect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -623,10 +623,10 @@ function Disconnect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -720,10 +720,10 @@ function Get-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -954,12 +954,12 @@ function Get-HVFarm { Returns the list of FarmInfo object matching the query criteria. .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -1071,10 +1071,10 @@ function Get-HVFarmSummary { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -1259,10 +1259,10 @@ function Get-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -1398,10 +1398,10 @@ function Get-HVPoolSummary { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -1614,10 +1614,10 @@ function Get-HVQueryFilter { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -1758,10 +1758,10 @@ function Get-HVQueryResult { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -2006,12 +2006,12 @@ function New-HVFarm { None .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -3468,10 +3468,10 @@ function New-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -5283,12 +5283,12 @@ function Remove-HVFarm { None .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -5405,7 +5405,7 @@ function Remove-HVPool { Version : 1.0 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -5567,12 +5567,12 @@ function Set-HVFarm { None .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -5776,10 +5776,10 @@ function Set-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -6016,12 +6016,12 @@ function Start-HVFarm { None .NOTES - Author : Ankit Gupta. + Author : Ankit Gupta, Praveen Mathamsetty Author email : guptaa@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -6398,10 +6398,10 @@ function Start-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -6839,7 +6839,7 @@ function Get-HVMachine { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -7053,7 +7053,7 @@ function Get-HVPoolSpec { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -7285,7 +7285,7 @@ function Get-HVInternalName { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -7454,7 +7454,7 @@ function New-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -7691,7 +7691,7 @@ function Get-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -7900,7 +7900,7 @@ function Remove-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -8143,7 +8143,7 @@ PARAMETER Key Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -8313,7 +8313,7 @@ function New-HVGlobalEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> @@ -8543,7 +8543,7 @@ function Get-HVGlobalEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 + Horizon View Server Version : 7.0.2, 7.1.0 PowerCLI Version : PowerCLI 6.5 PowerShell Version : 5.0 #> From 294863d49549bcfb1b97edb831529019704ae0df Mon Sep 17 00:00:00 2001 From: Rasmus Sjoerslev Date: Wed, 5 Apr 2017 21:06:18 +0200 Subject: [PATCH 69/90] Added Get-HVPodSessions function to VMware.Hv.Helper.psm1 which will show all connections across a Pod Federation. See Synopsis and Description for more details --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 7c06a99..9d7167f 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5256,5 +5256,70 @@ function Get-HVMachineSummary { return $machineList } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool +function Get-HVPodSessions { +<# +.Synopsis + Gets the total amount of sessions for all Pods in a Federation + +.DESCRIPTION + Gets the total amout of current sessions (connected and disconnected) for all Pods in a Federation (CPA) + based on the global query service. + The default object response is used which contains both success and fault information as well as the + session count per pod and the ID of each pod. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Get-HVPodSessions + +.OUTPUTS + Returns list of objects of type GlobalSessionPodSessionCounter + +.NOTES + Author : Rasmus Sjoerslev + Author email : rasmus.sjorslev@vmware.com + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $query_service_helper = New-Object VMware.Hv.GlobalSessionQueryServiceService + $count_spec = New-Object VMware.Hv.GlobalSessionQueryServiceCountSpec + $queryResults = @() + + foreach ($pod in $services.Pod.Pod_List()) { + $count_spec.Pod = $pod.Id + $info = $query_service_helper.GlobalSessionQueryService_GetCountWithSpec($services,$count_spec) + + foreach ($res in $info) { + if ($pod.Id.Id -eq $res.Id.Id) { + $queryResults += $res + } + } + } + return $queryResults +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,Get-HVPodSessions From d1fc624c57361b1f962ed2fa5254d117acd73bbd Mon Sep 17 00:00:00 2001 From: Rasmus Sjoerslev Date: Thu, 6 Apr 2017 11:47:20 +0200 Subject: [PATCH 70/90] Changed the name of the cmdlet from Get-HVPodSessions to Get-HVPodSession --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 9d7167f..7a3f909 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5256,7 +5256,7 @@ function Get-HVMachineSummary { return $machineList } -function Get-HVPodSessions { +function Get-HVPodSession { <# .Synopsis Gets the total amount of sessions for all Pods in a Federation @@ -5272,7 +5272,7 @@ function Get-HVPodSessions { first element from global:DefaultHVServers would be considered inplace of hvServer .EXAMPLE - Get-HVPodSessions + Get-HVPodSession .OUTPUTS Returns list of objects of type GlobalSessionPodSessionCounter @@ -5321,5 +5321,5 @@ function Get-HVPodSessions { return $queryResults } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,Get-HVPodSessions +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,Get-HVPodSession From 1b4ca3fb2ed30e421ad99e7cea6abddf2ebdf1fc Mon Sep 17 00:00:00 2001 From: Alessio Rocchi Date: Fri, 7 Apr 2017 11:03:47 +0200 Subject: [PATCH 71/90] Create SetLunReservation.ps1 --- Scripts/SetLunReservation.ps1 | 103 ++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 Scripts/SetLunReservation.ps1 diff --git a/Scripts/SetLunReservation.ps1 b/Scripts/SetLunReservation.ps1 new file mode 100644 index 0000000..ae610a0 --- /dev/null +++ b/Scripts/SetLunReservation.ps1 @@ -0,0 +1,103 @@ +<# + .SYNOPSIS + Set a given LUN ID to Perennially Reserved. + + .DESCRIPTION + A description of the file. + + .PARAMETER vCenter + Set vCenter server to connect to + + .PARAMETER Username + Set username to use + + .PARAMETER Password + Set password to be used + + .PARAMETER VirtualMachine + Name of the virtual machine which has the RDM + + .NOTES + =========================================================================== + Created on: 20/03/2017 15:05 + Created by: Alessio Rocchi + Organization: VMware + Filename: SetLunReservation.ps1 + =========================================================================== +#> +param +( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true, + Position = 0)] + [ValidateNotNullOrEmpty()] + [String]$vCenter, + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + HelpMessage = 'Set vCenter Username')] + [AllowNull()] + [String]$Username, + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + HelpMessage = 'Set vCenterPassword')] + [AllowNull()] + [String]$Password, + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [String]$VirtualMachine +) + +Import-Module -Name VMware.VimAutomation.Core -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null + +try +{ + if ([String]::IsNullOrEmpty($Username) -or [String]::IsNullOrEmpty($Password)) + { + $vcCredential = Get-Credential + Connect-VIServer -Server $vCenter -Credential $vcCredential -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } + else + { + Connect-VIServer -Server $vCenter -User $Username -Password $Password -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } +} +catch +{ + Write-Error("Error connecting to vCenter: {0}" -f $vCenter) + exit +} + + +$rDms = Get-HardDisk -DiskType rawPhysical -Vm (Get-VM -Name $VirtualMachine) +$clusterHosts = Get-Cluster -VM $VirtualMachine | Get-VMHost + +$menu = @{ } + +for ($i = 1; $i -le $rDms.count; $i++) +{ + Write-Host("{0}) {1}[{2}]: {3}" -f ($i, $rDms[$i - 1].Name, $rDms[$i - 1].CapacityGB, $rDms[$i - 1].ScsiCanonicalName)) + $menu.Add($i, ($rDms[$i - 1].ScsiCanonicalName)) +} + +[int]$ans = Read-Host 'Which Disk you want to configure?' +$selection = $menu.Item($ans) +write-host("Choosed Disk: {0}" -f $selection) + +$current = 0 +foreach ($vmHost in $clusterHosts) +{ + Write-Progress -Activity "Processing Cluster." -CurrentOperation $vmHost.Name -PercentComplete (($counter / $clusterHosts.count) * 100) + $esxcli = Get-EsxCli -V2 -VMHost $vmHost + $deviceListArgs = $esxcli.storage.core.device.list.CreateArgs() + $deviceListArgs.device = $selection + $esxcli.storage.core.device.list.Invoke($deviceListArgs) | Select-Object Device, IsPerenniallyReserved + $deviceSetArgs = $esxcli.storage.core.device.setconfig.CreateArgs() + $deviceSetArgs.device = $selection + $deviceSetArgs.perenniallyreserved = $true + $esxcli.storage.core.device.setconfig.Invoke($deviceSetArgs) + $counter++ +} + +Disconnect-VIServer -WarningAction SilentlyContinue -Server $vCenter -Force -Confirm:$false + From fe738ba17ad835491ccba2ebf711751b8369c6d5 Mon Sep 17 00:00:00 2001 From: Ray Heffer Date: Tue, 18 Apr 2017 11:19:03 -0400 Subject: [PATCH 72/90] Create Horizon-UsageStats.ps1 This is a sample script that retrieves the Horizon usage statistics. --- Scripts/Horizon-UsageStats.ps1 | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Scripts/Horizon-UsageStats.ps1 diff --git a/Scripts/Horizon-UsageStats.ps1 b/Scripts/Horizon-UsageStats.ps1 new file mode 100644 index 0000000..7c389bb --- /dev/null +++ b/Scripts/Horizon-UsageStats.ps1 @@ -0,0 +1,38 @@ +<# +.NOTES +Script name: Horizon-UsageStats.ps1 +Author: Ray Heffer, @rayheffer +Last Edited on: 04/18/2017 +Dependencies: PowerCLI 6.5 R1 or later, Horizon 7.0.2 or later +.DESCRIPTION +This is a sample script that retrieves the Horizon usage statistics. This produces the same metrics as listed under View Configuration > Product Licensing and Usage. Service providers can use this script or incorporate it with their existing scripts to automate the reporting of Horizon usage. + +Example Output: +NumConnections : 180 +NumConnectionsHigh : 250 +NumViewComposerConnections : 0 +NumViewComposerConnectionsHigh : 0 +NumTunneledSessions : 0 +NumPSGSessions : 180 +#> + +# User Configuration +$hzUser = "Administrator" +$hzPass = "VMware1!" +$hzDomain = "vmw.lab" +$hzConn = "connect01.vmw.lab" + +# Import the Horizon module +Import-Module VMware.VimAutomation.HorizonView + +# Establish connection to Connection Server +$hvServer = Connect-HVServer -server $hzConn -User $hzUser -Password $hzPass -Domain $hzDomain + +# Assign a variable to obtain the API Extension Data +$hvServices = $Global:DefaultHVServers.ExtensionData + +# Retrieve Connection Server Health metrics +$hvHealth =$hvServices.ConnectionServerHealth.ConnectionServerHealth_List() + +# Display ConnectionData (Usage stats) +$hvHealth.ConnectionData From bd2edd6bd935a3659fd81250fdf4082bcf483c84 Mon Sep 17 00:00:00 2001 From: Ray Heffer Date: Tue, 18 Apr 2017 16:55:21 -0400 Subject: [PATCH 73/90] Rename Horizon-UsageStats.ps1 to Horizon-GetUsageStats.ps1 --- Scripts/{Horizon-UsageStats.ps1 => Horizon-GetUsageStats.ps1} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Scripts/{Horizon-UsageStats.ps1 => Horizon-GetUsageStats.ps1} (100%) diff --git a/Scripts/Horizon-UsageStats.ps1 b/Scripts/Horizon-GetUsageStats.ps1 similarity index 100% rename from Scripts/Horizon-UsageStats.ps1 rename to Scripts/Horizon-GetUsageStats.ps1 From 5aee669b4cbcb6feb058d2ca1b88fa92661e6d38 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Tue, 18 Apr 2017 21:03:53 -0700 Subject: [PATCH 74/90] create modules.sh added modules file for copying to powerclicore correct locations --- Scripts/modules.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Scripts/modules.sh diff --git a/Scripts/modules.sh b/Scripts/modules.sh new file mode 100644 index 0000000..ea84779 --- /dev/null +++ b/Scripts/modules.sh @@ -0,0 +1,6 @@ +#!/bin/bash +for file in $( ls /powershell/PowerCLI-Example-Scripts/Modules/ ) +do + mkdir "/root/.local/share/powershell/Modules/${file%.*}/" + mv "/powershell/PowerCLI-Example-Scripts/Modules/$file" "/root/.local/share/powershell/Modules/${file%.*}/$file" +done From 3c18981280755af61aac7820d5667576724f8f0e Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Thu, 20 Apr 2017 11:59:08 -0700 Subject: [PATCH 75/90] updated pester tests and uncluded new ones for CIS cmdlets --- ...nnect-CISServer Connection to VC.Tests.ps1 | 37 ++++++++++++++++ ...onnect-VIServer Connection to VC.Tests.ps1 | 36 +++++++++++++++ Pester/Test Connection to VC.ps1 | 44 ------------------- .../Test Disconnect-CISServer to VC.Tests.ps1 | 20 +++++++++ .../Test Disconnect-VIServer to VC.Tests.ps1 | 20 +++++++++ 5 files changed, 113 insertions(+), 44 deletions(-) create mode 100644 Pester/Test Connect-CISServer Connection to VC.Tests.ps1 create mode 100644 Pester/Test Connect-VIServer Connection to VC.Tests.ps1 delete mode 100644 Pester/Test Connection to VC.ps1 create mode 100644 Pester/Test Disconnect-CISServer to VC.Tests.ps1 create mode 100644 Pester/Test Disconnect-VIServer to VC.Tests.ps1 diff --git a/Pester/Test Connect-CISServer Connection to VC.Tests.ps1 b/Pester/Test Connect-CISServer Connection to VC.Tests.ps1 new file mode 100644 index 0000000..e9fb3c9 --- /dev/null +++ b/Pester/Test Connect-CISServer Connection to VC.Tests.ps1 @@ -0,0 +1,37 @@ +<# +Script name: Test Connect-CISServer to VC.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection can be made to a vCenter for the CIS Service +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Connect-CISServer to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } + +#> + +$VCUSER = $Parameters.Get_Item("VCUSER") +$VCPASS = $Parameters.Get_Item("VCPASS") +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Connect-CISServer" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Connect-CISServer Tests" { + + $connection = Connect-CISServer $VCName -User $VCUser -password $VCPass + It "Connection is active" { + $Global:DefaultCISServers[0].isconnected | Should Be $true + } + + It "Checking connected server name is $VCName" { + $Global:DefaultCISServers[0] | Select * + $Global:DefaultCISServers[0].name | Should Be $VCName + } +} \ No newline at end of file diff --git a/Pester/Test Connect-VIServer Connection to VC.Tests.ps1 b/Pester/Test Connect-VIServer Connection to VC.Tests.ps1 new file mode 100644 index 0000000..2428458 --- /dev/null +++ b/Pester/Test Connect-VIServer Connection to VC.Tests.ps1 @@ -0,0 +1,36 @@ +<# +Script name: Test Connection to VC.ps1 +Created on: 07/15/2016 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection can be made to a vCenter +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Connection to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } + +#> + +$VCUSER = $Parameters.Get_Item("VCUSER") +$VCPASS = $Parameters.Get_Item("VCPASS") +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Connect-VIServer" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Connect-VIServer Tests" { + + $connection = Connect-VIServer $VCName -User $VCUser -password $VCPass + It "Connection is active" { + $Global:DefaultVIServer[0].isconnected | Should Be $true + } + + It "Checking connected server name is $VCName" { + $Global:DefaultVIServer[0].name | Should Be $VCName + } +} \ No newline at end of file diff --git a/Pester/Test Connection to VC.ps1 b/Pester/Test Connection to VC.ps1 deleted file mode 100644 index 006825a..0000000 --- a/Pester/Test Connection to VC.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -<# -Script name: Test Connection to VC.ps1 -Created on: 07/15/2016 -Author: Alan Renouf, @alanrenouf -Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection and disconnection can be made to a vCenter -Dependencies: Pester Module -Example run: - -Invoke-Pester -Script @{ Path = '.\Test Connection to VC.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } - -#> - -$VCUSER = $Parameters.Get_Item("VCUSER") -$VCPASS = $Parameters.Get_Item("VCPASS") -$VCNAME = $Parameters.Get_Item("VCNAME") - -Describe "PowerCLI Tests" { - It "Importing PowerCLI Modules" { - Get-Module VMware* | Foreach { - Write-Host "Importing Module $($_.name) Version $($_.Version)" - $_ | Import-Module - Get-Module $_ | Should Be $true - } - } -} - -Describe "Connect-VIServer Tests" { - - $connection = Connect-VIServer $VCName -User $VCUser -password $VCPass - It "Connection is active" { - $Global:DefaultVIServer[0].isconnected | Should Be $true - } - - It "Checking connected server name is $VCName" { - $Global:DefaultVIServer[0].name | Should Be $VCName - } -} - -Describe "Disconnect-VIServer Tests" { - It "Disconnect from $VCName" { - Disconnect-VIServer $VCName -confirm:$false - $Global:DefaultVIServer | Should Be $null - } -} \ No newline at end of file diff --git a/Pester/Test Disconnect-CISServer to VC.Tests.ps1 b/Pester/Test Disconnect-CISServer to VC.Tests.ps1 new file mode 100644 index 0000000..63f12e4 --- /dev/null +++ b/Pester/Test Disconnect-CISServer to VC.Tests.ps1 @@ -0,0 +1,20 @@ +<# +Script name: Test Disconnect-CISServer to VC.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the Disconnect-CISServer cmdlet disconnects +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Disconnect-CISServer to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local" } } + +#> + +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Disconnect-CISServer Tests" { + It "Disconnect from $VCName" { + Disconnect-CISServer $VCName -confirm:$false + $Global:DefaultCISServers | Should Be $null + } +} \ No newline at end of file diff --git a/Pester/Test Disconnect-VIServer to VC.Tests.ps1 b/Pester/Test Disconnect-VIServer to VC.Tests.ps1 new file mode 100644 index 0000000..8a51fb9 --- /dev/null +++ b/Pester/Test Disconnect-VIServer to VC.Tests.ps1 @@ -0,0 +1,20 @@ +<# +Script name: Test Disconnect-VIServer to VC.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the Disconnect-VIServer cmdlet disconnects +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Disconnect-VISServer to VC.ps1'; Parameters = @{ VCNAME="VC01.local" } } + +#> + +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Disconnect-VIServer Tests" { + It "Disconnect from $VCName" { + Disconnect-VIServer $VCName -confirm:$false + $Global:DefaultVIServer | Should Be $null + } +} \ No newline at end of file From 29578c6305c8cc7c7f381798f22553f8bd9d7b65 Mon Sep 17 00:00:00 2001 From: Alan Renouf Date: Thu, 20 Apr 2017 16:24:40 -0700 Subject: [PATCH 76/90] updated pester tests for CIS cmdlets --- ...nect-CISServer Connection to VC.Tests.ps1} | 0 ...nnect-VIServer Connection to VC.Tests.ps1} | 0 Pester/Test Get-CISService.Tests.ps1 | 49 +++++++++++++++++++ ...Test Disconnect-CISServer to VC.Tests.ps1} | 0 ... Test Disconnect-VIServer to VC.Tests.ps1} | 0 5 files changed, 49 insertions(+) rename Pester/{Test Connect-CISServer Connection to VC.Tests.ps1 => 00 Test Connect-CISServer Connection to VC.Tests.ps1} (100%) rename Pester/{Test Connect-VIServer Connection to VC.Tests.ps1 => 00 Test Connect-VIServer Connection to VC.Tests.ps1} (100%) create mode 100644 Pester/Test Get-CISService.Tests.ps1 rename Pester/{Test Disconnect-CISServer to VC.Tests.ps1 => ZZ Test Disconnect-CISServer to VC.Tests.ps1} (100%) rename Pester/{Test Disconnect-VIServer to VC.Tests.ps1 => ZZ Test Disconnect-VIServer to VC.Tests.ps1} (100%) diff --git a/Pester/Test Connect-CISServer Connection to VC.Tests.ps1 b/Pester/00 Test Connect-CISServer Connection to VC.Tests.ps1 similarity index 100% rename from Pester/Test Connect-CISServer Connection to VC.Tests.ps1 rename to Pester/00 Test Connect-CISServer Connection to VC.Tests.ps1 diff --git a/Pester/Test Connect-VIServer Connection to VC.Tests.ps1 b/Pester/00 Test Connect-VIServer Connection to VC.Tests.ps1 similarity index 100% rename from Pester/Test Connect-VIServer Connection to VC.Tests.ps1 rename to Pester/00 Test Connect-VIServer Connection to VC.Tests.ps1 diff --git a/Pester/Test Get-CISService.Tests.ps1 b/Pester/Test Get-CISService.Tests.ps1 new file mode 100644 index 0000000..17d18b5 --- /dev/null +++ b/Pester/Test Get-CISService.Tests.ps1 @@ -0,0 +1,49 @@ +<# +Script name: Test Connect-CISService.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the CIS Service cmdlet works correctly +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Get-CISService.ps1' } + +#> + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Get-CISService" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Get-CISService Tests for services" { + + It "Checking CIS connection is active" { + $Global:DefaultCISServers[0].isconnected | Should Be $true + } + + It "Checking Get-CISService returns services" { + Get-CISService | Should Be $true + } + + # Checking some known services which have a Get Method + $servicestocheck = "com.vmware.appliance.system.version", "com.vmware.appliance.health.system" + Foreach ($service in $servicestocheck) { + It "Checking $service get method returns data" { + Get-CisService -Name $service | Should Be $true + (Get-CisService -Name $service).get() | Should Be $true + } + } + + # Checking some known services which have a List Method + $servicestocheck = "com.vmware.vcenter.folder", "com.vmware.vcenter.vm" + Foreach ($service in $servicestocheck) { + It "Checking $service list method returns data" { + Get-CisService -Name $service | Should Be $true + (Get-CisService -Name $service).list() | Should Be $true + } + } +} \ No newline at end of file diff --git a/Pester/Test Disconnect-CISServer to VC.Tests.ps1 b/Pester/ZZ Test Disconnect-CISServer to VC.Tests.ps1 similarity index 100% rename from Pester/Test Disconnect-CISServer to VC.Tests.ps1 rename to Pester/ZZ Test Disconnect-CISServer to VC.Tests.ps1 diff --git a/Pester/Test Disconnect-VIServer to VC.Tests.ps1 b/Pester/ZZ Test Disconnect-VIServer to VC.Tests.ps1 similarity index 100% rename from Pester/Test Disconnect-VIServer to VC.Tests.ps1 rename to Pester/ZZ Test Disconnect-VIServer to VC.Tests.ps1 From a3c91c6376b77ce274cd5aae2e02c501a37e0023 Mon Sep 17 00:00:00 2001 From: William Lam Date: Thu, 20 Apr 2017 17:47:31 -0700 Subject: [PATCH 77/90] Adding vSAN Mgmt 6.x PowerCLI samples --- Scripts/VSANSmartsData.ps1 | 59 ++++++++++++++++++++++++++++++++++++++ Scripts/VSANVersion.ps1 | 26 +++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 Scripts/VSANSmartsData.ps1 create mode 100644 Scripts/VSANVersion.ps1 diff --git a/Scripts/VSANSmartsData.ps1 b/Scripts/VSANSmartsData.ps1 new file mode 100644 index 0000000..61ef427 --- /dev/null +++ b/Scripts/VSANSmartsData.ps1 @@ -0,0 +1,59 @@ +Function Get-VSANSmartsData { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function retreives SMART drive data using new vSAN + Management 6.6 API. This can also be used outside of vSAN + to query existing SSD devices not being used for vSAN. + .PARAMETER Cluster + The name of a vSAN Cluster + .EXAMPLE + Get-VSANSmartsData -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$false)][String]$Cluster + ) + + if($global:DefaultVIServer.ExtensionData.Content.About.ApiType -eq "VirtualCenter") { + if(!$cluster) { + Write-Host "Cluster property is required when connecting to vCenter Server" + break + } + + $vchs = Get-VSANView -Id "VsanVcClusterHealthSystem-vsan-cluster-health-system" + $cluster_view = (Get-Cluster -Name $Cluster).ExtensionData.MoRef + $result = $vchs.VsanQueryVcClusterSmartStatsSummary($cluster_view) + } else { + $vhs = Get-VSANView -Id "HostVsanHealthSystem-ha-vsan-health-system" + $result = $vhs.VsanHostQuerySmartStats($null,$true) + } + + $vmhost = $result.Hostname + $smartsData = $result.SmartStats + + Write-Host "`nESXi Host: $vmhost`n" + foreach ($data in $smartsData) { + if($data.stats) { + $stats = $data.stats + Write-Host $data.disk + + $smartsResults = @() + foreach ($stat in $stats) { + $statResult = [pscustomobject] @{ + Parameter = $stat.Parameter; + Value =$stat.Value; + Threshold = $stat.Threshold; + Worst = $stat.Worst + } + $smartsResults+=$statResult + } + $smartsResults | Format-Table + } + } +} \ No newline at end of file diff --git a/Scripts/VSANVersion.ps1 b/Scripts/VSANVersion.ps1 new file mode 100644 index 0000000..45fa7a7 --- /dev/null +++ b/Scripts/VSANVersion.ps1 @@ -0,0 +1,26 @@ +Function Get-VSANVersion { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function retreives the vSAN software version for both VC/ESXi + .PARAMETER Cluster + The name of a vSAN Cluster + .EXAMPLE + Get-VSANVersion -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + $vchs = Get-VSANView -Id "VsanVcClusterHealthSystem-vsan-cluster-health-system" + $cluster_view = (Get-Cluster -Name $Cluster).ExtensionData.MoRef + $results = $vchs.VsanVcClusterQueryVerifyHealthSystemVersions($cluster_view) + + Write-Host "`nVC Version:"$results.VcVersion + $results.HostResults | Select Hostname, Version +} From 720595989a9af1fc2aed1cff6c46923483a5044b Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 21 Apr 2017 20:58:12 +0530 Subject: [PATCH 78/90] Add readme file Add readme file, versions for "VMware.Hv.Helper" module. --- Modules/VMware.Hv.Helper/README.md | 20 ++ .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 198 +++++++++--------- 2 files changed, 119 insertions(+), 99 deletions(-) create mode 100644 Modules/VMware.Hv.Helper/README.md diff --git a/Modules/VMware.Hv.Helper/README.md b/Modules/VMware.Hv.Helper/README.md new file mode 100644 index 0000000..ae6e144 --- /dev/null +++ b/Modules/VMware.Hv.Helper/README.md @@ -0,0 +1,20 @@ +Prerequisites/Steps to use this module: + +1. This module only works for Horizon product E.g. Horizon 7.0.2 and later. +2. Install the latest version of Powershell, PowerCLI(6.5) or (later version via psgallary). +3. Import HorizonView module by running: Import-Module VMware.VimAutomation.HorizonView. +4. Import "VMware.Hv.Helper" module by running: Import-Module -Name "location of this module" or Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module. +5. Get-Command -Module "This module Name" to list all available functions or Get-Command -Module 'VMware.Hv.Helper'. + +# Example script to connect view API service of Connection Server: + +Import-Module VMware.VimAutomation.HorizonView +# Connection to view API service +$hvServer = Connect-HVServer -server +$hvServices = $hvserver.ExtensionData +$csList = $hvServices.ConnectionServer.ConnectionServer_List() +# Load this module +Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module +Get-Command -Module 'VMware.Hv.Helper' +# Use advanced functions of this module +New-HVPool -spec 'path to InstantClone.json file' diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index a4a46d4..2e4118f 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -204,12 +204,12 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 Dependencies : Make sure pool already exists before adding VMs to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -386,14 +386,14 @@ function Add-HVRDSServer { None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 Dependencies : Make sure farm already exists before adding RDSServers to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding( @@ -505,11 +505,11 @@ function Connect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding()] @@ -623,11 +623,11 @@ function Disconnect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -720,11 +720,11 @@ function Get-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -954,13 +954,13 @@ function Get-HVFarm { Returns the list of FarmInfo object matching the query criteria. .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1071,11 +1071,11 @@ function Get-HVFarmSummary { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1259,11 +1259,11 @@ function Get-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1398,11 +1398,11 @@ function Get-HVPoolSummary { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1614,11 +1614,11 @@ function Get-HVQueryFilter { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding()] @@ -1758,11 +1758,11 @@ function Get-HVQueryResult { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -2006,13 +2006,13 @@ function New-HVFarm { None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -3468,11 +3468,11 @@ function New-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5283,13 +5283,13 @@ function Remove-HVFarm { None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5402,11 +5402,11 @@ function Remove-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5567,13 +5567,13 @@ function Set-HVFarm { None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5776,11 +5776,11 @@ function Set-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -6016,13 +6016,13 @@ function Start-HVFarm { None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -6398,11 +6398,11 @@ function Start-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -6815,19 +6815,19 @@ function Get-HVMachine { first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE - Get-HVDesktop -PoolName 'ManualPool' + Get-HVMachine -PoolName 'ManualPool' Queries VM(s) with given parameter poolName .EXAMPLE - Get-HVDesktop -MachineName 'PowerCLIVM' + Get-HVMachine -MachineName 'PowerCLIVM' Queries VM(s) with given parameter machineName .EXAMPLE - Get-HVDesktop -State CUSTOMIZING + Get-HVMachine -State CUSTOMIZING Queries VM(s) with given parameter vm state .EXAMPLE - Get-HVDesktop -DnsName 'powercli-*' + Get-HVMachine -DnsName 'powercli-*' Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS @@ -6839,8 +6839,8 @@ function Get-HVMachine { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -6939,19 +6939,19 @@ function Get-HVMachineSummary { first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE - Get-HVDesktopSummary -PoolName 'ManualPool' + Get-HVMachineSummary -PoolName 'ManualPool' Queries VM(s) with given parameter poolName .EXAMPLE - Get-HVDesktopSummary -MachineName 'PowerCLIVM' + Get-HVMachineSummary -MachineName 'PowerCLIVM' Queries VM(s) with given parameter machineName .EXAMPLE - Get-HVDesktopSummary -State CUSTOMIZING + Get-HVMachineSummary -State CUSTOMIZING Queries VM(s) with given parameter vm state .EXAMPLE - Get-HVDesktopSummary -DnsName 'powercli-*' + Get-HVMachineSummary -DnsName 'powercli-*' Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS @@ -6963,8 +6963,8 @@ function Get-HVMachineSummary { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -7053,8 +7053,8 @@ function Get-HVPoolSpec { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding( @@ -7285,8 +7285,8 @@ function Get-HVInternalName { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding( @@ -7454,8 +7454,8 @@ function New-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding( @@ -7691,8 +7691,8 @@ function Get-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -7900,8 +7900,8 @@ function Remove-HVEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -8143,8 +8143,8 @@ PARAMETER Key Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -8313,8 +8313,8 @@ function New-HVGlobalEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -8543,8 +8543,8 @@ function Get-HVGlobalEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -8629,8 +8629,8 @@ function Remove-HVGlobalEntitlement { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> From 2769f885f3c62cbbe8b13af645ea6b523837002a Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Fri, 21 Apr 2017 21:17:27 +0530 Subject: [PATCH 79/90] update readme file update readme file --- Modules/VMware.Hv.Helper/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/README.md b/Modules/VMware.Hv.Helper/README.md index ae6e144..fe87153 100644 --- a/Modules/VMware.Hv.Helper/README.md +++ b/Modules/VMware.Hv.Helper/README.md @@ -1,7 +1,7 @@ Prerequisites/Steps to use this module: 1. This module only works for Horizon product E.g. Horizon 7.0.2 and later. -2. Install the latest version of Powershell, PowerCLI(6.5) or (later version via psgallary). +2. Install the latest version of Powershell, PowerCLI(6.5) or (later version via psgallery). 3. Import HorizonView module by running: Import-Module VMware.VimAutomation.HorizonView. 4. Import "VMware.Hv.Helper" module by running: Import-Module -Name "location of this module" or Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module. 5. Get-Command -Module "This module Name" to list all available functions or Get-Command -Module 'VMware.Hv.Helper'. From 6580d1b08530d4b3e129101b7fe5faee94182f40 Mon Sep 17 00:00:00 2001 From: Kevin Kirkpatrick Date: Mon, 24 Apr 2017 12:06:39 -0400 Subject: [PATCH 80/90] Added .git files to root of repo Signed-off-by: Kevin Kirkpatrick --- .gitattributes | 0 .gitignore | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ac9d8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# PowerShell Studio Files +*.temppoint.* +*.psproj.psbuild +*.psbuild + +#VS Code Files +*.vscode + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk \ No newline at end of file From e808a91fc5e45423727b4e6e7c1ed7d30f1e4b82 Mon Sep 17 00:00:00 2001 From: Kevin Kirkpatrick Date: Mon, 24 Apr 2017 12:07:25 -0400 Subject: [PATCH 81/90] Added folders for all .psm1 Files in /Modules Signed-off-by: Kevin Kirkpatrick --- Modules/Backup-VCSA/Backup-VCSA.psm1 | 202 +++++ Modules/Get-NICDetails/Get-NICDetails.psm1 | 93 +++ .../Get-NewAndRemovedVMs.psm1 | 131 ++++ Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 | 114 +++ Modules/Konfig-ESXi/Konfig-ESXi.psm1 | 234 ++++++ Modules/PSvLIMessage/PSvLIMessage.psm1 | 123 +++ Modules/ProactiveHA/ProactiveHA.psm1 | 468 ++++++++++++ .../Recommend-Sizing/Recommend-Sizing.psm1 | 227 ++++++ Modules/Set-CBT/Set-CBT.psm1 | 111 +++ Modules/Start-UNMAP/Start-UNMAP.psm1 | 99 +++ Modules/VAMI/VAMI.psm1 | 716 ++++++++++++++++++ Modules/VCHA/VCHA.psm1 | 413 ++++++++++ Modules/VMCPFunctions/VMCPFunctions.psm1 | 322 ++++++++ Modules/apply-hardening/apply-hardening.psm1 | 93 +++ .../vSphere_Hardening_Assess_VM_v1a.psm1 | 372 +++++++++ 15 files changed, 3718 insertions(+) create mode 100644 Modules/Backup-VCSA/Backup-VCSA.psm1 create mode 100644 Modules/Get-NICDetails/Get-NICDetails.psm1 create mode 100644 Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 create mode 100644 Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 create mode 100644 Modules/Konfig-ESXi/Konfig-ESXi.psm1 create mode 100644 Modules/PSvLIMessage/PSvLIMessage.psm1 create mode 100644 Modules/ProactiveHA/ProactiveHA.psm1 create mode 100644 Modules/Recommend-Sizing/Recommend-Sizing.psm1 create mode 100644 Modules/Set-CBT/Set-CBT.psm1 create mode 100644 Modules/Start-UNMAP/Start-UNMAP.psm1 create mode 100755 Modules/VAMI/VAMI.psm1 create mode 100644 Modules/VCHA/VCHA.psm1 create mode 100644 Modules/VMCPFunctions/VMCPFunctions.psm1 create mode 100644 Modules/apply-hardening/apply-hardening.psm1 create mode 100644 Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 diff --git a/Modules/Backup-VCSA/Backup-VCSA.psm1 b/Modules/Backup-VCSA/Backup-VCSA.psm1 new file mode 100644 index 0000000..271a7ff --- /dev/null +++ b/Modules/Backup-VCSA/Backup-VCSA.psm1 @@ -0,0 +1,202 @@ +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 + 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) + -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 {} +} diff --git a/Modules/Get-NICDetails/Get-NICDetails.psm1 b/Modules/Get-NICDetails/Get-NICDetails.psm1 new file mode 100644 index 0000000..30f1440 --- /dev/null +++ b/Modules/Get-NICDetails/Get-NICDetails.psm1 @@ -0,0 +1,93 @@ +function Get-NICDetails { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2017.02 ver 1.0 Base Release + =========================================================================== + External Code Sources: + - + =========================================================================== + Tested Against Environment: + vSphere Version: ESXi 6.0 U2, ESXi 6.5 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2008 R2, Server 2012 R2 + Keyword: ESXi, NIC, vmnic, Driver, Firmware + =========================================================================== + + .DESCRIPTION + Reports Firmware and Driver Details for your ESXi vmnics. + + .Example + Get-NICDetails -Clustername * + + .PARAMETER Clustername + Name or Wildcard of your vSphere Cluster Name to process. + + +#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)] + [ValidateNotNullorEmpty()] + [String] $Clustername + +) + +Begin { + $Validate = $True + + if (($myCluster = Get-Cluster -Name $Clustername).count -lt 1) { + $Validate = $False + thow "No Cluster '$myCluster' found!" + } + +} + +Process { + + $MyView = @() + if ($Validate -eq $True) { + + foreach ($myVMhost in ($myCluster | Get-VMHost)) { + + $esxcli2 = Get-ESXCLI -VMHost $myVMhost -V2 + $niclist = $esxcli2.network.nic.list.invoke() + + $nicdetails = @() + foreach ($nic in $niclist) { + + $args = $esxcli2.network.nic.get.createargs() + $args.nicname = $nic.name + $nicdetail = $esxcli2.network.nic.get.Invoke($args) + $nicdetails += $nicdetail + + } + ForEach ($nicdetail in $nicdetails){ + $NICReport = [PSCustomObject] @{ + Host = $myVMhost.Name + vmnic = $nicdetail.Name + LinkStatus = $nicdetail.LinkStatus + BusInfo = $nicdetail.driverinfo.BusInfo + Driver = $nicdetail.driverinfo.Driver + FirmwareVersion = $nicdetail.driverinfo.FirmwareVersion + DriverVersion = $nicdetail.driverinfo.Version + } + $MyView += $NICReport + } + + } + + $MyView + + } +} +} \ No newline at end of file diff --git a/Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 b/Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 new file mode 100644 index 0000000..4a2e3ba --- /dev/null +++ b/Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 @@ -0,0 +1,131 @@ +function Get-NewAndRemovedVMs { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.12 ver 1.0 Base Release + =========================================================================== + External Code Sources: + https://github.com/alanrenouf/vCheck-vSphere + =========================================================================== + 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, VM + =========================================================================== + + .DESCRIPTION + This Function report newly created and deleted VMs by Cluster. + + .Example + Get-NewAndRemovedVMs -ClusterName Cluster* | ft -AutoSize + + .Example + Get-NewAndRemovedVMs -ClusterName Cluster01 -Days 90 + + .PARAMETER ClusterName + Name or Wildcard of your vSphere Cluster Name(s) to report. + + .PARAMETER Day + Range in Days to report. + + +#Requires PS -Version 4.0 +#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} +#> + +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0, HelpMessage = "Name or Wildcard of your vSphere Cluster Name to report")] + [ValidateNotNullorEmpty()] + [String]$ClusterName, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, HelpMessage = "Range in Days to report")] + [ValidateNotNullorEmpty()] + [String]$Days = "30" +) +Begin { + function Get-VIEventPlus { + + param( + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity, + [string[]]$EventType, + [DateTime]$Start, + [DateTime]$Finish = (Get-Date), + [switch]$Recurse, + [string[]]$User, + [Switch]$System, + [string]$ScheduledTask, + [switch]$FullMessage = $false, + [switch]$UseUTC = $false + ) + + process { + $eventnumber = 100 + $events = @() + $eventMgr = Get-View EventManager + $eventFilter = New-Object VMware.Vim.EventFilterSpec + $eventFilter.disableFullMessage = ! $FullMessage + $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity + $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} + $eventFilter.eventTypeId = $EventType + if($Start -or $Finish){ + $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime + if($Start){ + $eventFilter.time.beginTime = $Start + } + if($Finish){ + $eventFilter.time.endTime = $Finish + } + } + if($User -or $System){ + $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername + if($User){ + $eventFilter.UserName.userList = $User + } + if($System){ + $eventFilter.UserName.systemUser = $System + } + } + if($ScheduledTask){ + $si = Get-View ServiceInstance + $schTskMgr = Get-View $si.Content.ScheduledTaskManager + $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | + where {$_.Info.Name -match $ScheduledTask} | + Select -First 1 | + Select -ExpandProperty MoRef + } + if(!$Entity){ + $Entity = @(Get-Folder -NoRecursion) + } + $entity | %{ + $eventFilter.entity.entity = $_.ExtensionData.MoRef + $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + while($eventsBuffer){ + $events += $eventsBuffer + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + } + $eventCollector.DestroyCollector() + } + if (-not $UseUTC) + { + $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } + } + + $events + } +} +} + +process { + $result = Get-VIEventPlus -Start ((get-date).adddays(-$Days)) -EventType @("VmCreatedEvent", "VmBeingClonedEvent", "VmBeingDeployedEvent","VmRemovedEvent") + $sortedResult = $result | Select CreatedTime, @{N='Cluster';E={$_.ComputeResource.Name}}, @{Name="VMName";Expression={$_.vm.name}}, UserName, @{N='Type';E={$_.GetType().Name}}, FullFormattedMessage | Sort CreatedTime + $sortedResult | where {$_.Cluster -like $ClusterName} +} +} \ No newline at end of file diff --git a/Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 b/Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 new file mode 100644 index 0000000..27af1ad --- /dev/null +++ b/Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 @@ -0,0 +1,114 @@ +function Get-VMmaxIOPS { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.10 ver 1.0 Base Release + 2016.11 ver 1.1 Added vSphere 6.5 Support, New Counters, More Error Handling + =========================================================================== + 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, 6.5 + PowerCLI Version: PowerCLI 6.3 R1, 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Windows Server 2012 R2 + =========================================================================== + Keywords vSphere, ESXi, VM, Storage + =========================================================================== + + .DESCRIPTION + This Function will Create a VM Disk IOPS Report + + .Example + Get-VM TST* | Get-VMmaxIOPS -Minutes 60 | FT -Autosize + + .Example + $SampleVMs = Get-VM "TST*" + Get-VMmaxIOPS -VMs $SampleVMs -Minutes 60 + + .PARAMETER VMs + Specify the VMs + + .PARAMETER Minutes + Specify the Minutes to report (10080 is one Week) + +#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=$True, Position=0)] + [ValidateNotNullorEmpty()] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $VMs, + [Parameter(Mandatory=$false, Position=1, HelpMessage = "Specify the Minutes to report (10080 is one Week)")] + [ValidateNotNullorEmpty()] + [int] $Minutes = 30 +) +Begin { + # none + } +Process { + if ($_.PowerState -eq "PoweredOn") { + #region: Global Definitions + [int]$TimeRange = "-" + $Minutes + #endregion + + #region: Creating VM Stats + Write-Verbose "$(Get-Date -Format G) Create VM Stats..." + $VMMetrics = "virtualdisk.numberwriteaveraged.average","virtualdisk.numberreadaveraged.average" + $Start = (Get-Date).AddMinutes($TimeRange) + $stats = Get-Stat -Realtime -Stat $VMMetrics -Entity $VMs -Start $Start -Verbose:$False + Write-Verbose "$(Get-Date -Format G) Create VM Stats completed" + #endregion + + #region: Creating HD-Tab + Write-Verbose "$(Get-Date -Format G) Create HD Tab..." + $hdTab = @{} + foreach($hd in (Get-Harddisk -VM $VMs)){ + $controllerKey = $hd.Extensiondata.ControllerKey + $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey} + $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('[') + } + Write-Verbose "$(Get-Date -Format G) Create HD Tab completed" + #endregion + + #region: Creating Reports + Write-Verbose "$(Get-Date -Format G) Create Report..." + $reportPerf = @() + $reportPerf = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{ + New-Object PSObject -Property @{ + VM = $_.Values[0] + Disk = $_.Values[1] + IOPSWriteAvg = [math]::round( ($_.Group | ` + where{$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} | ` + Measure-Object -Property Value -Average).Average,2) + IOPSReadAvg = [math]::round( ($_.Group | ` + where{$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} | ` + Measure-Object -Property Value -Average).Average,2) + Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]] + } + } + Write-Verbose "$(Get-Date -Format G) Create Report completed" + #endregion + + + } + Else { + Write-Error "VM $($_.Name) is Powered Off! Processing Skipped" + } + $reportPerf | Select-Object VM, Disk, Datastore, IOPSWriteAvg, IOPSReadAvg + } + +End { + # none + } + +} \ No newline at end of file diff --git a/Modules/Konfig-ESXi/Konfig-ESXi.psm1 b/Modules/Konfig-ESXi/Konfig-ESXi.psm1 new file mode 100644 index 0000000..f14386a --- /dev/null +++ b/Modules/Konfig-ESXi/Konfig-ESXi.psm1 @@ -0,0 +1,234 @@ +function Konfig-ESXi { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.12 ver 1.0 Base Release + 2016.12 ver 1.1 ESXi 6.5 Tests, Minor enhancements + =========================================================================== + External Code Sources: + Function My-Logger : http://www.virtuallyghetto.com/ + =========================================================================== + Tested Against Environment: + vSphere Version: ESXi 5.5 U2, ESXi 6.5 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2012 R2 + Keyword: ESXi, NTP, SSH, Syslog, SATP, + =========================================================================== + + .DESCRIPTION + This Function sets the Basic settings for a new ESXi. + + * NTP + * SSH + * Syslog + * Power Management + * HP 3PAR SATP/PSP Rule + * ... + + .Example + Konfig-ESXi -VMHost myesxi.lan.local -NTP 192.168.2.1, 192.168.2.2 -syslog "udp://loginsight.lan.local:514" + + .PARAMETER VMHost + Host to configure. + + .PARAMETER NTP + NTP Server(s) to set. + + .PARAMETER Syslog + Syslog Server to set, e.g. "udp://loginsight.lan.local:514" + + DNS Name must be resolvable! + + +#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)] + [String] $VMHost, + [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=1)] + [array]$NTP, + [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=2)] + [String] $syslog + +) + +Begin { + Function My-Logger { + param( + [Parameter(Mandatory=$true)] + [String]$message + ) + + $timeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" + + Write-Host -NoNewline -ForegroundColor White "[$timestamp]" + Write-Host -ForegroundColor Green " $message" + } + function Set-MyESXiOption { + [CmdletBinding()] + param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] + [String] $Name, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] + [String] $Value + ) + process { + $myESXiOption = Get-AdvancedSetting -Entity $ESXiHost -Name $Name + if ($myESXiOption.Value -ne $Value) { + My-Logger " Setting ESXi Option $Name to Value $Value" + $myESXiOption | Set-AdvancedSetting -Value $Value -Confirm:$false | Out-Null + } + else { + My-Logger " ESXi Option $Name already has Value $Value" + } + } + } +} + +Process { + $Validate = $True + + #region: Start vCenter Connection + My-Logger "Starting to Process ESXi Server Connection to $VMHost ..." + if (($global:DefaultVIServers).count -gt 0) { + Disconnect-VIServer -Force -Confirm:$False -ErrorAction SilentlyContinue + } + $VIConnection = Connect-VIServer -Server $VMHost + if (-not $VIConnection.IsConnected) { + Write-Error "ESXi Connection Failed." + $Validate = $False + } + elseif ($VIConnection.ProductLine -ne "EmbeddedEsx") { + Write-Error "Connencted System is not an ESXi." + $Validate = $False + } + else { + $ESXiHost = Get-VMHost + My-Logger "Connected ESXi Version: $($ESXiHost.Version) $($ESXiHost.Build) " + } + #endregion + + if ($Validate -eq $True) { + + #region: Enable SSH and disable SSH Warning + $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} + My-Logger "Starting SSH Service..." + if($SSHService.Running -ne $True){ + Start-VMHostService -HostService $SSHService -Confirm:$false | Out-Null + } + else { + My-Logger " SSH Service is already running" + } + My-Logger "Setting SSH Service to Automatic Start..." + if($SSHService.Policy -ne "automatic"){ + Set-VMHostService -HostService $SSHService -Policy "Automatic" | Out-Null + } + else { + My-Logger " SSH Service is already set to Automatic Start" + } + My-Logger "Disabling SSH Warning..." + Set-MyESXiOption -Name "UserVars.SuppressShellWarning" -Value "1" + #endregion + + #region: Config NTP + My-Logger "Removing existing NTP Server..." + try { + $ESXiHost | Remove-VMHostNtpServer -NtpServer (Get-VMHostNtpServer) -Confirm:$false + } + catch [System.Exception] { + Write-Warning "Error during removing existing NTP Servers." + } + My-Logger "Setting new NTP Servers..." + foreach ($myNTP in $NTP) { + $ESXiHost | Add-VMHostNtpServer -ntpserver $myNTP -confirm:$False | Out-Null + } + + My-Logger "Configure NTP Service..." + $NTPService = $ESXiHost | Get-VMHostService| Where-Object {$_.key -eq "ntpd"} + if($NTPService.Running -eq $True){ + Stop-VMHostService -HostService $NTPService -Confirm:$false | Out-Null + } + if($NTPService.Policy -ne "on"){ + Set-VMHostService -HostService $NTPService -Policy "on" -confirm:$False | Out-Null + } + + My-Logger "Configure Local Time..." + $HostTimeSystem = Get-View $ESXiHost.ExtensionData.ConfigManager.DateTimeSystem + $HostTimeSystem.UpdateDateTime([DateTime]::UtcNow) + + My-Logger "Start NTP Service..." + Start-VMHostService -HostService $NTPService -confirm:$False | Out-Null + #endregion + + #region: Remove default PG + My-Logger "Checking for Default Port Group ..." + if ($defaultPG = $ESXiHost | Get-VirtualSwitch -Name vSwitch0 | Get-VirtualPortGroup -Name "VM Network" -ErrorAction SilentlyContinue ){ + Remove-VirtualPortGroup -VirtualPortGroup $defaultPG -confirm:$False | Out-Null + My-Logger " Default PG Removed" + } + else { + My-Logger " No Default PG found" + } + #endregion + + #region: Configure Static HighPower + My-Logger "Setting PowerProfile to Static HighPower..." + try { + $HostView = ($ESXiHost | Get-View) + (Get-View $HostView.ConfigManager.PowerSystem).ConfigurePowerPolicy(1) + } + catch [System.Exception] { + Write-Warning "Error during Configure Static HighPower. See latest errors..." + } + #endregion + + #region: Conf Syslog + My-Logger "Setting Syslog Firewall Rule ..." + $SyslogFW = ($ESXiHost | Get-VMHostFirewallException | where {$_.Name -eq 'syslog'}) + if ($SyslogFW.Enabled -eq $False ){ + $SyslogFW | Set-VMHostFirewallException -Enabled:$true -Confirm:$false | Out-Null + My-Logger " Syslog Firewall Rule enabled" + } + else { + My-Logger " Syslog Firewall Rule already enabled" + } + My-Logger "Setting Syslog Server..." + Set-MyESXiOption -Name "Syslog.global.logHost" -Value $syslog + #endregion + + #region: Change Disk Scheduler + My-Logger "Changing Disk Scheduler..." + Set-MyESXiOption -Name "Disk.SchedulerWithReservation" -Value "0" + #endregion + + #region: Configure HP 3PAR SATP/PSP Rule + My-Logger "Configure HP 3PAR SATP/PSP Rule" + $esxcli2 = Get-ESXCLI -VMHost $ESXiHost -V2 + $arguments = $esxcli2.storage.nmp.satp.rule.add.CreateArgs() + $arguments.satp = "VMW_SATP_ALUA" + $arguments.psp = "VMW_PSP_RR" + $arguments.pspoption = "iops=100" + $arguments.claimoption = "tpgs_on" + $arguments.vendor = "3PARdata" + $arguments.model = "VV" + $arguments.description = "HP 3PAR custom SATP Claimrule" + try { + $esxcli2.storage.nmp.satp.rule.add.Invoke($arguments) + } + catch { + Write-Warning "Error during Configure HP 3PAR SATP/PSP Rule. See latest errors..." + } + #endregion + + } + } +} diff --git a/Modules/PSvLIMessage/PSvLIMessage.psm1 b/Modules/PSvLIMessage/PSvLIMessage.psm1 new file mode 100644 index 0000000..9cab209 --- /dev/null +++ b/Modules/PSvLIMessage/PSvLIMessage.psm1 @@ -0,0 +1,123 @@ +add-type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult( + ServicePoint srvPoint, X509Certificate certificate, + WebRequest request, int certificateProblem) { + return true; + } + } +"@ +[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Organization: Private + Personal Blog: mycloudrevolution.com + Twitter: @vMarkus_K + =========================================================================== + Tested Against Environment: + vRealize Log Insight 3.3.1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2012 R2 + Keyword: vRealize, RestAPI + + Dependencies: + PowerCLI Version: PowerCLI 6.3 R1 + + .SYNOPSIS + Push Messages to VMware vRealize Log Insight. + + .DESCRIPTION + Creates a Messages in VMware vRealize Log Insight via the Ingestion API + + .EXAMPLE + Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" + + .EXAMPLE + Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" -Hostname MyTEST -FieldName myTest -FieldContent myTest + + .PARAMETER vLIServer + Specify the FQDN of your vRealize Log Insight Appliance + + .PARAMETER vLIAgentID + Specify the vRealize Log Insight Agent ID, e.g. "12862842-5A6D-679C-0E38-0E2BE888BB28" + + .PARAMETER Text + Specify the Event Text + + .PARAMETER Hostname + Specify the Hostanme displayed in vRealize Log Insight + + .PARAMETER FieldName + Specify the a Optional Field Name for vRealize Log Insight + + .PARAMETER FieldContent + Specify the a Optional FieldContent for the Field in -FieldName for vRealize Log Insight + If FielName is missing and FieldContent is given, it will be ignored + + #Requires PS -Version 3.0 + + #> +function Push-vLIMessage { + + [cmdletbinding()] + param ( + [parameter(Mandatory=$true)] + [string]$Text, + [parameter(Mandatory=$true)] + [string]$vLIServer, + [parameter(Mandatory=$true)] + [string]$vLIAgentID, + [parameter(Mandatory=$false)] + [string]$Hostname = $env:computername, + [parameter(Mandatory=$false)] + [string]$FieldName, + [parameter(Mandatory=$false)] + [string]$FieldContent = "" + ) + Process { + $Field_vLI = [ordered]@{ + name = "PS_vLIMessage" + content = "true" + } + $Field_HostName = [ordered]@{ + name = "hostname" + content = $Hostname + } + + $Fields = @($Field_vLI, $Field_HostName) + + if ($FieldName) { + $Field_Custom = [ordered]@{ + name = $FieldName + content = $FieldContent + } + $Fields += @($Field_Custom) + } + + $Restcall = @{ + messages = ([Object[]]([ordered]@{ + text = ($Text) + fields = ([Object[]]$Fields) + })) + } | convertto-json -Depth 4 + + $Resturl = ("http://" + $vLIServer + ":9000/api/v1/messages/ingest/" + $vLIAgentID) + try + { + $Response = Invoke-RestMethod $Resturl -Method Post -Body $Restcall -ContentType 'application/json' -ErrorAction stop + Write-Information "REST Call to Log Insight server successful" + Write-Verbose $Response + } + catch + { + Write-Error "REST Call failed to Log Insight server" + Write-Verbose $error[0] + Write-Verbose $Resturl + } + } +} \ No newline at end of file diff --git a/Modules/ProactiveHA/ProactiveHA.psm1 b/Modules/ProactiveHA/ProactiveHA.psm1 new file mode 100644 index 0000000..ea4e92f --- /dev/null +++ b/Modules/ProactiveHA/ProactiveHA.psm1 @@ -0,0 +1,468 @@ +Function New-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to register a new Proactive HA Provider with vCenter Server + .PARAMETER ProviderName + Name of ProactiveHA Provider + .PARAMETER ComponentType + Name of a supported ComponentType that ProactiveHA supports (Fan, Memory, Network, Power or Storage) + .PARAMETER ComponentDescription + Description of the health check for the given component + .PARAMETER ComponentId + Unique identifier for the given component within a ProactiveHA Provider + .EXAMPLE + New-PHAProvider -ProviderName "virtuallyGhetto" -ComponentType Power -ComponentDescription "Simulated ProactiveHA Provider" -ComponentId "Power" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderName, + [Parameter(Mandatory=$true)][ValidateSet("Fan","Memory","Network","Power","Storage")][String]$ComponentType, + [Parameter(Mandatory=$true)][String]$ComponentDescription, + [Parameter(Mandatory=$true)][String]$ComponentId + ) + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthInfo = [VMware.Vim.HealthUpdateInfo] @{ + ComponentType = $ComponentType + description = $ComponentDescription + Id = $ComponentId + } + + try { + Write-Host "`nRegistering new Proactive HA Provider $ProviderName ..." + $providerId = $healthManager.RegisterHealthUpdateProvider($ProviderName,$healthInfo) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} + +Function Get-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return list of all Proactive HA Providers registered with vCenter Server + .EXAMPLE + Get-PHAProvider +#> + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthProviderResults = @() + $hpIDs = $healthManager.QueryProviderList() + + foreach ($hpID in $hpIDs) { + $hpName = $healthManager.QueryProviderName($hpID) + $hpConfig = $healthManager.QueryHealthUpdateInfos($hpID) + + $hp = [pscustomobject] @{ + ProviderName = $hpName + ProviderID = $hpID + ComponentType = $hpConfig.componentType + ComponentID = $hpConfig.id + Description = $hpConfig.description + } + $healthProviderResults+=$hp + } + $healthProviderResults +} + +Function Remove-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to remove a registered Proactive HA Provider from vCenter Server + .PARAMETER ProviderId + The ProactiveHA provider ID (retrieved from Get-PHAProvider) to unregister + .EXAMPLE + Remove-PHAProvider -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + try { + Write-Host "`nUnregistering Proactive HA Provider $ProviderId ... " + $healthManager.UnregisterHealthUpdateProvider($providerId) + } catch { + if($Error[0].Exception.InnerException.MethodFault.getType().Name -eq "InvalidState") { + Write-host -ForegroundColor Red "The Proactive HA Provider is still in use, please disable it before unregistering" + } else { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Set-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to enable/disable Proactive HA for vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to enable Proactive HA + .PARAMETER ProviderId + Proactive HA Provider ID to enable in vSphere Cluster + .PARAMETER ClusterMode + Whether Proactive HA should be "Automated" or "Manual" for actions it will take + .PARAMETER ModerateRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Moderate issue is observed + .PARAMETER SevereRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Severe issue is observed + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Enabled -ClusterMode Automated -ModerateRemediation QuarantineMode -SevereRemediation QuarantineMode -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Disabled -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$Cluster, + [Parameter(Mandatory=$false)][ValidateSet("Automated","Manual")]$ClusterMode="Manual", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$ModerateRemediation="QuarantineMode", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$SevereRemediation="QuarantineMode", + [Switch]$Enabled, + [Switch]$Disabled + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,Host,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + $vmhosts = $ClusterView.host + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + if($Enabled) { + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if(-not $healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Enabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.AddMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + $healthProviders+=$currentHP + } + if(-not ($healthProviders -contains $ProviderID)) { + $healthProviders+=$ProviderId + } + } else { + $healthProviders+=$ProviderId + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Enabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } + + if($Disabled) { + foreach ($vmhost in $vmhosts) { + if($vmhost.runtime.inQuarantineMode) { + Write-Host -ForegroundColor Red $vmhost.name " is currently still in Quaratine Mode, please remediate this before disabling Proactive HA" + break + } + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + if($currentHP -ne $ProviderId) { + $healthProviders+=$currentHP + } + } + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Disabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + $ClusterView.UpdateViewData() + + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if($healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Disabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.RemoveMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Get-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve Proactive HA configuration for a vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA configuration + .EXAMPLE + Get-PHAConfig -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $phSettings = $ClusterView.ConfigurationEx.InfraUpdateHaConfig + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + $healthProviders = @() + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthProviders+=$providerName + } + + $pHAConfig = [pscustomobject] @{ + Enabled = $phSettings.Enabled + ClusterMode = $phSettings.behavior + ModerateRemediation = $phSettings.ModerateRemediation + SevereRemediation = $phSettings.SevereRemediation + HealthProviders = $healthProviders + } + $pHAConfig + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function Get-PHAHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve the Proactive HA health info for all ESXi hosts in vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA health information + .EXAMPLE + Get-PHAHealth -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthUpdates = $healthManager.QueryHealthUpdates($provider) + + $healthResults = @() + Write-Host -NoNewline -ForegroundColor Magenta "Health summary for Proactive HA Provider $providerName`:`n" + foreach ($healthUpdate in $healthUpdates) { + $vmhost = Get-View $healthUpdate.Entity + + $hr = [PSCustomObject] @{ + Entity = $vmhost.name + Status = $healthUpdate.status + HealthComponentId = $healthUpdate.HealthUpdateInfoId + HealthUpdateId = $healthUpdate.Id + Remediation = $healthUpdate.Remediation + } + $healthResults+=$hr + } + $healthResults + } + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function New-PHASimulation { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return VCHA Configuration + .PARAMETER ProviderId + The Proactive HA Provider ID that you like to simulate a health update from + .PARAMETER EsxiHost + The name of ESXi host to update the health on + .PARAMETER Component + The name of the matching component ID from Proactive HA Provider to simulate a health update from + .PARAMETER HealthStatus + The health value (green, yellow or red) for the given simulated health Update + .PARAMETER Remediation + The remediation message associated with simulated health update + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus green -Remediation "" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus red -Remediation "Please replace my virtual PSU" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$EsxiHost, + [Parameter(Mandatory=$true)][String]$Component, + [Parameter(Mandatory=$true)][ValidateSet("green","red","yellow")][String]$HealthStatus, + [Parameter(Mandatory=$false)][String]$Remediation + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $vmhost = Get-View -ViewType HostSystem -Property Name -Filter @{"name" = $EsxiHost} + + if($vmhost -eq $null) { + Write-Host -ForegroundColor Red "`nUnable to find ESXi host $EsxiHost ..." + break + } + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + # Randomly generating an ID for Health Update + # In general, you would want to generate a specific ID + # which can be referenced between ProactiveHA Provider + # and VMware logs for troubleshooting purposes + $HealthUpdateID = "vghetto-" + (Get-Random -Minimum 1 -Maximum 100000) + + # All other Health Status can have a remediation message + # but for green, it must be an empty string or API call will fail + if($HealthStatus -eq "green") { + $Remediation = "" + } + + $healthUpdate = [VMware.Vim.HealthUpdate] @{ + Entity = $vmhost.moref + HealthUpdateInfoId = $Component + Id = $HealthUpdateId + Status = $HealthStatus + Remediation = $Remediation + } + + try { + Write-Host "`nSimulating Proactive HA Health Update to ..." + Write-Host "`tHost: $EsxiHost " + Write-Host -NoNewline "`tStatus: " + Write-Host -ForegroundColor $HealthStatus "$HealthStatus" + Write-Host "`tRemediation Messsage: $Remediation" + $healthManager.PostHealthUpdates($providerId,$healthUpdate) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} \ No newline at end of file diff --git a/Modules/Recommend-Sizing/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing/Recommend-Sizing.psm1 new file mode 100644 index 0000000..0075e43 --- /dev/null +++ b/Modules/Recommend-Sizing/Recommend-Sizing.psm1 @@ -0,0 +1,227 @@ +function Recommend-Sizing { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.11 ver 1.0 Base Release + 2016.11 ver 1.1 Optional Stats Collection + 2016.11 ver 1.2 VM Stats from Realtime Data and new Counters + =========================================================================== + 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, 6.0 + 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 Recommandation. Focus is in Compute Ressources. + + .Example + Recommend-Sizing -ClusterNames Cluster01, Cluster02 -Stats -StatsRange 60 -Verbose + + .Example + Recommend-Sizing -ClusterNames Cluster01, Cluster02 + + .Example + Recommend-Sizing -ClusterNames Cluster01 + + .PARAMETER ClusterNames + List of your vSphere Cluser Names to process. + + .PARAMETER Stats + Enables Stats Collection. + + Warning: At the moment this is only fully tested with vSphere 5.5 and vSphere 6.5! + + .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, ParameterSetName = "Stats")] + [switch] $Stats, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2, ParameterSetName = "Stats")] + [int] $StatsRange = 1440 + +) +Begin { + if ($Stats) { + Write-Warning "Stats Collection requested.`nAt the moment this is only fully tested with vSphere 5.5 and vSphere 6.5" + [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 + + if ($Stats) { + #region: Creating VM Stats + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats..." + $VMMetrics = "disk.numberwrite.summation","disk.numberread.summation","cpu.usage.average", "mem.usage.average" + $Start = (Get-Date).AddMinutes($TimeRange) + $VMStats = Get-Stat -Realtime -Stat $VMMetrics -Entity $ClusterVMsPoweredOn -Start $Start -Verbose:$False + Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats completed" + #endregion + + #region: Creating VM Stats Report + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report..." + $ReportVMPerf = @() + $ReportVMPerf = $VMStats | Group-Object -Property {$_.Entity.Name},Instance | %{ + New-Object PSObject -Property @{ + IOPSWriteAvg = ($_.Group | ` + where{$_.MetricId -eq "disk.numberwrite.summation"} | ` + Measure-Object -Property Value -Average).Average + IOPSReadAvg = ($_.Group | ` + where{$_.MetricId -eq "disk.numberread.summation"} | ` + Measure-Object -Property Value -Average).Average + CPUUsageAvg = ($_.Group | ` + where{$_.MetricId -eq "cpu.usage.average"} | ` + Measure-Object -Property Value -Average).Average + MEMUsageAvg = ($_.Group | ` + where{$_.MetricId -eq "mem.usage.average"} | ` + Measure-Object -Property Value -Average).Average + } + } + Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report completed" + #endregion + } + else { + Write-Verbose "$(Get-Date -Format G) Stats Collection skipped..." + } + + #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 %" + 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 ) + AverageVMIOPSWriteAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSWriteAvg).Average,1 ) + AverageVMIOPSReadAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSReadAvg).Average,1 ) + AverageVMCPUUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property CPUUsageAvg).Average,1 )) %" + AverageVMMEMUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property MEMUsageAvg).Average,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 diff --git a/Modules/Set-CBT/Set-CBT.psm1 b/Modules/Set-CBT/Set-CBT.psm1 new file mode 100644 index 0000000..784aebb --- /dev/null +++ b/Modules/Set-CBT/Set-CBT.psm1 @@ -0,0 +1,111 @@ +function Set-CBT { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.11 ver 1.0 Base Release + =========================================================================== + External Code Sources: + http://wahlnetwork.com/2015/12/01/change-block-tracking-cbt-powercli/ + =========================================================================== + Tested Against Environment: + vSphere Version: 5.5 U2 + PowerCLI Version: PowerCLI 6.3 R1 + PowerShell Version: 4.0 + OS Version: Windows Server 2012 R2 + =========================================================================== + Keywords vSphere, ESXi, VM, Storage, CBT, Backup + =========================================================================== + + .DESCRIPTION + This Function enables or disables CBT. + + .Example + Get-VN TST* | Set-CBT -DisableCBT + + .Example + Get-VN TST* | Set-CBT -EnableCBT + + .PARAMETER DisableCBT + Disables CBT for any VMs found with it enabled + + .PARAMETER EnableCBT + Enables CBT for any VMs found with it disabled + +#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=$True, Position=0, HelpMessage = "VMs to process")] + [ValidateNotNullorEmpty()] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $myVMs, + [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Enables CBT for any VMs found with it disabled", ParameterSetName = "EnableCBT")] + [ValidateNotNullorEmpty()] + [Switch]$EnableCBT, + [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Disables CBT for any VMs found with it enabled", ParameterSetName = "DisableCBT")] + [ValidateNotNullorEmpty()] + [Switch]$DisableCBT + ) +Process { + + $vmconfigspec = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec + Write-Verbose -Message "Walking through given VMs" + foreach($myVM in $myVMs) + { + if ($DisableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $true -and $myVM.ExtensionData.Snapshot -eq $null) + { + try + { + Write-Verbose -Message "Reconfiguring $($myVM.name) to disable CBT" -Verbose + $vmconfigspec.ChangeTrackingEnabled = $false + $myVM.ExtensionData.ReconfigVM($vmconfigspec) + + if ($myVM.PowerState -eq "PoweredOn" ) { + Write-Verbose -Message "Creating a snapshot on $($myVM.name) to clear CBT file" -Verbose + $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" + + Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose + $SnapShot| Remove-Snapshot -Confirm:$false + } + + } + catch + { + throw $myVM + } + } + elseif ($EnableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $false -and $myVM.ExtensionData.Snapshot -eq $null) + { + Write-Verbose -Message "Reconfiguring $($myVM.name) to enable CBT" -Verbose + $vmconfigspec.ChangeTrackingEnabled = $true + $myVM.ExtensionData.ReconfigVM($vmconfigspec) + + if ($myVM.PowerState -eq "PoweredOn" ) { + Write-Verbose -Message "Creating a snapshot on $($myVM.name) to Create CBT file" -Verbose + $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" + + Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose + $SnapShot | Remove-Snapshot -Confirm:$false + } + } + else + { + if ($myVM.ExtensionData.Snapshot -ne $null -and $EnableCBT) + { + Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" + } + elseif ($myVM.ExtensionData.Snapshot -ne $null -and $DisableCBT) + { + Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" + } + } + } + + } +} diff --git a/Modules/Start-UNMAP/Start-UNMAP.psm1 b/Modules/Start-UNMAP/Start-UNMAP.psm1 new file mode 100644 index 0000000..a8e9896 --- /dev/null +++ b/Modules/Start-UNMAP/Start-UNMAP.psm1 @@ -0,0 +1,99 @@ +function Start-UNMAP { +<# + .SYNOPSIS + Process SCSI UNMAP on VMware Datastores + + .DESCRIPTION + This Function will process SCSI UNMAP on VMware Datastores via ESXCLI -V2 + + .Example + Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* + + .Example + Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* -Verbose -WhatIf + + .Notes + NAME: Start-UNMAP.psm1 + AUTHOR: Markus Kraus + LASTEDIT: 23.09.2016 + VERSION: 1.0 + KEYWORDS: VMware, vSphere, ESXi, SCSI, VAAI, UNMAP + + .Link + http://mycloudrevolution.com/ + + #Requires PS -Version 4.0 + #Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} + #> + + [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact='High')] + param( + [Parameter(Mandatory=$true, Position=0)] + [String]$ClusterName, + [Parameter(Mandatory=$true, Position=1)] + [String]$DSWildcard + ) + Process { + $Validate = $true + #region: PowerCLI Session Timeout + Write-Verbose "Set Session Timeout ..." + $initialTimeout = (Get-PowerCLIConfiguration -Scope Session).WebOperationTimeoutSeconds + Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds -1 -Confirm:$False | Out-Null + #endregion + + #region: Get Cluster + $Cluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue + Write-Verbose "vSphere Cluster: $Cluster" + if (!$Cluster){Write-Error "No Cluster found!"; $Validate = $false} + #endregion + + #region: Get Hosts + $ClusterHosts = $Cluster | Get-VMHost -ErrorAction SilentlyContinue | where {$_.ConnectionState -eq "Connected" -and $_.PowerState -eq "PoweredOn"} + Write-Verbose "vSphere Cluster Hosts: $ClusterHosts" + if (!$ClusterHosts){Write-Error "No Hosts found!"; $Validate = $false} + #endregion + + #region: Get Datastores + $ClusterDataStores = $Cluster | Get-Datastore -ErrorAction SilentlyContinue | where {$_.Name -like $DSWildcard -and $_.State -eq "Available" -and $_.Accessible -eq "True"} + Write-Verbose "vSphere Cluster Datastores: $ClusterDataStores" + if (!$ClusterDataStores){Write-Error "No Datastores found!"; $Validate = $false} + #endregion + + #region: Process Datastores + if ($Validate -eq $true) { + Write-Verbose "Starting Loop..." + foreach ($ClusterDataStore in $ClusterDataStores) { + Write-Verbose "vSphere Datastore to Process: $ClusterDataStore" + $myHost = $ClusterHosts[(Get-Random -Maximum ($ClusterHosts).count)] + Write-Verbose "vSphere Host to Process: $myHost" + $esxcli2 = $myHost | Get-ESXCLI -V2 + $arguments = $esxcli2.storage.vmfs.unmap.CreateArgs() + $arguments.volumelabel = $ClusterDataStore + $arguments.reclaimunit = "256" + if ($PSCmdlet.ShouldProcess( $ClusterDataStore,"Starting UNMAP on $myHost")) { + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + try { + Write-Output "Starting UNMAP for $ClusterDataStore on $myHost..." + $esxcli2.storage.vmfs.unmap.Invoke($arguments) + } + catch { + Write-Output "A Error occured: " "" $error[0] "" + } + $stopwatch.Stop() + Write-Output "UNMAP duration: $($stopwatch.Elapsed.Minutes)" + } + + } + } + else { + Write-Error "Validation Failed. Processing Loop Skipped!" + } + #endregion + + #region: Revert PowerCLI Session Timeout + Write-Verbose "Revert Session Timeout ..." + Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds $initialTimeout -Confirm:$False | Out-Null + #endregion + } + +} diff --git a/Modules/VAMI/VAMI.psm1 b/Modules/VAMI/VAMI.psm1 new file mode 100755 index 0000000..92c5d5f --- /dev/null +++ b/Modules/VAMI/VAMI.psm1 @@ -0,0 +1,716 @@ +Function Get-VAMISummary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves some basic information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return basic VAMI summary info + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMISummary +#> + $systemVersionAPI = Get-CisService -Name 'com.vmware.appliance.system.version' + $results = $systemVersionAPI.get() | select product, type, version, build, install_time + + $systemUptimeAPI = Get-CisService -Name 'com.vmware.appliance.system.uptime' + $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) + $uptime = $ts.ToString("hh\:mm\:ss\,fff") + + $summaryResult = [pscustomobject] @{ + Product = $results.product; + Type = $results.type; + Version = $results.version; + Build = $results.build; + InstallTime = $results.install_time; + Uptime = $uptime + } + $summaryResult +} + +Function Get-VAMIHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves health information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI health + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIHealth +#> + $healthOverall = (Get-CisService -Name 'com.vmware.appliance.health.system').get() + $healthLastCheck = (Get-CisService -Name 'com.vmware.appliance.health.system').lastcheck() + $healthCPU = (Get-CisService -Name 'com.vmware.appliance.health.load').get() + $healthMem = (Get-CisService -Name 'com.vmware.appliance.health.mem').get() + $healthSwap = (Get-CisService -Name 'com.vmware.appliance.health.swap').get() + $healthStorage = (Get-CisService -Name 'com.vmware.appliance.health.storage').get() + + # DB health only applicable for Embedded/External VCSA Node + $vami = (Get-CisService -Name 'com.vmware.appliance.system.version').get() + + if($vami.type -eq "vCenter Server with an embedded Platform Services Controller" -or $vami.type -eq "vCenter Server with an external Platform Services Controller") { + $healthVCDB = (Get-CisService -Name 'com.vmware.appliance.health.databasestorage').get() + } else { + $healthVCDB = "N/A" + } + $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() + + $healthResult = [pscustomobject] @{ + HealthOverall = $healthOverall; + HealthLastCheck = $healthLastCheck; + HealthCPU = $healthCPU; + HealthMem = $healthMem; + HealthSwap = $healthSwap; + HealthStorage = $healthStorage; + HealthVCDB = $healthVCDB; + HealthSoftware = $healthSoftwareUpdates + } + $healthResult +} + +Function Get-VAMIAccess { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves access information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + $consoleAccess = (Get-CisService -Name 'com.vmware.appliance.access.consolecli').get() + $dcuiAccess = (Get-CisService -Name 'com.vmware.appliance.access.dcui').get() + $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() + $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() + + $accessResult = New-Object PSObject -Property @{ + Console = $consoleAccess; + DCUI = $dcuiAccess; + BashShell = $shellAccess.enabled; + SSH = $sshAccess + } + $accessResult +} + +Function Get-VAMITime { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the time and NTP info from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return current Time and NTP information + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMITime +#> + $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' + $timeResults = $systemTimeAPI.get() + + $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() + $timeSyncMode = $timeSync.mode + + $timeResult = [pscustomobject] @{ + Timezone = $timeResults.timezone; + Date = $timeResults.date; + CurrentTime = $timeResults.time; + Mode = $timeSyncMode; + NTPServers = "N/A"; + NTPStatus = "N/A"; + } + + if($timeSyncMode -eq "NTP") { + $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() + $timeResult.NTPServers = $ntpServers.servers + $timeResult.NTPStatus = $ntpServers.status + } + $timeResult +} + +Function Get-VAMINetwork { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves network information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return networking information including details for each interface + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMINetwork +#> + $netResults = @() + + $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() + $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() + + Write-Host "Hostname: " $hostname + Write-Host "DNS Servers: " $dns.servers + + $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() + foreach ($interface in $interfaces) { + $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') + $spec = $ipv4API.Help.get.interfaces.CreateExample() + $spec+= $interface.name + $ipv4result = $ipv4API.get($spec) + + $interfaceResult = [pscustomobject] @{ + Inteface = $interface.name; + MAC = $interface.mac; + Status = $interface.status; + Mode = $ipv4result.mode; + IP = $ipv4result.address; + Prefix = $ipv4result.prefix; + Gateway = $ipv4result.default_gateway; + Updateable = $ipv4result.updateable + } + $netResults += $interfaceResult + } + $netResults +} + +Function Get-VAMIDisks { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VMDK disk number to partition mapping VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VMDK disk number to OS partition mapping + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIDisks +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + $disks = $storageAPI.list() + + foreach ($disk in $disks | sort {[int]$_.disk.toString()}) { + $disk | Select Disk, Partition + } +} + +Function Start-VAMIDiskResize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function triggers an OS partition resize after adding additional disk capacity + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function triggers OS partition resize operation + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIDiskResize +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + Write-Host "Initiated OS partition resize operation ..." + $storageAPI.resize() +} + +Function Get-VAMIStatsList { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list avialable monitoring metrics in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of available monitoring metrics that can be queried + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStatsList +#> + $monitoringAPI = Get-CisService -Name 'com.vmware.appliance.monitoring' + $ids = $monitoringAPI.list() | Select id | Sort-Object -Property id + + foreach ($id in $ids) { + $id + } +} + +Function Get-VAMIStorageUsed { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the individaul OS partition storage utilization + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return individual OS partition storage utilization + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStorageUsed +#> + $monitoringAPI = Get-CisService 'com.vmware.appliance.monitoring' + $querySpec = $monitoringAPI.help.query.item.CreateExample() + + # List of IDs from Get-VAMIStatsList to query + $querySpec.Names = @( + "storage.used.filesystem.autodeploy", + "storage.used.filesystem.boot", + "storage.used.filesystem.coredump", + "storage.used.filesystem.imagebuilder", + "storage.used.filesystem.invsvc", + "storage.used.filesystem.log", + "storage.used.filesystem.netdump", + "storage.used.filesystem.root", + "storage.used.filesystem.updatemgr", + "storage.used.filesystem.vcdb_core_inventory", + "storage.used.filesystem.vcdb_seat", + "storage.used.filesystem.vcdb_transaction_log", + "storage.totalsize.filesystem.autodeploy", + "storage.totalsize.filesystem.boot", + "storage.totalsize.filesystem.coredump", + "storage.totalsize.filesystem.imagebuilder", + "storage.totalsize.filesystem.invsvc", + "storage.totalsize.filesystem.log", + "storage.totalsize.filesystem.netdump", + "storage.totalsize.filesystem.root", + "storage.totalsize.filesystem.updatemgr", + "storage.totalsize.filesystem.vcdb_core_inventory", + "storage.totalsize.filesystem.vcdb_seat", + "storage.totalsize.filesystem.vcdb_transaction_log" + ) + + # Tuple (Filesystem Name, Used, Total) to store results + $storageStats = @{ + "autodeploy"=@{"name"="/storage/autodeploy";"used"=0;"total"=0}; + "boot"=@{"name"="/boot";"used"=0;"total"=0}; + "coredump"=@{"name"="/storage/core";"used"=0;"total"=0}; + "imagebuilder"=@{"name"="/storage/imagebuilder";"used"=0;"total"=0}; + "invsvc"=@{"name"="/storage/invsvc";"used"=0;"total"=0}; + "log"=@{"name"="/storage/log";"used"=0;"total"=0}; + "netdump"=@{"name"="/storage/netdump";"used"=0;"total"=0}; + "root"=@{"name"="/";"used"=0;"total"=0}; + "updatemgr"=@{"name"="/storage/updatemgr";"used"=0;"total"=0}; + "vcdb_core_inventory"=@{"name"="/storage/db";"used"=0;"total"=0}; + "vcdb_seat"=@{"name"="/storage/seat";"used"=0;"total"=0}; + "vcdb_transaction_log"=@{"name"="/storage/dblog";"used"=0;"total"=0} + } + + $querySpec.interval = "DAY1" + $querySpec.function = "MAX" + $querySpec.start_time = ((get-date).AddDays(-1)) + $querySpec.end_time = (Get-Date) + $queryResults = $monitoringAPI.query($querySpec) | Select * -ExcludeProperty Help + + foreach ($queryResult in $queryResults) { + # Update hash if its used storage results + if($queryResult.name -match "used") { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["used"] = $value + # Update hash if its total storage results + } else { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["total"] = $value + } + } + + $storageResults = @() + foreach ($key in $storageStats.keys | Sort-Object -Property name) { + $statResult = [pscustomobject] @{ + Filesystem = $storageStats[$key].name; + Used = $storageStats[$key].used; + Total = $storageStats[$key].total + } + $storageResults += $statResult + } + $storageResults +} + +Function Get-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIService + .EXAMPLE + Get-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + if($Name -ne "") { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + $serviceStatus = $vMonAPI.get($name,0) + $serviceString = [pscustomobject] @{ + Name = $name; + State = $serviceStatus.state; + Health = ""; + Startup = $serviceStatus.startup_type + } + if($serviceStatus.health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $serviceStatus.health } + $serviceString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + $services = $vMonAPI.list_details() + + $serviceResult = @() + foreach ($key in $services.keys | Sort-Object -Property Value) { + $serviceString = [pscustomobject] @{ + Name = $key; + State = $services[$key].state; + Health = "N/A"; + Startup = $services[$key].Startup_type + } + if($services[$key].health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $services[$key].health } + + $serviceResult += $serviceString + } + $serviceResult + } +} + +Function Start-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Starting $name service ..." + $vMonAPI.start($name) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Stop-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Stop-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Stopping $name service ..." + $vMonAPI.stop($name) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Get-VAMIBackupSize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the backup size of the VCSA from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return the current backup size of the VCSA (common and core data) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIBackupSize +#> + $recoveryAPI = Get-CisService 'com.vmware.appliance.recovery.backup.parts' + $backupParts = $recoveryAPI.list() | select id + + $estimateBackupSize = 0 + $backupPartSizes = "" + foreach ($backupPart in $backupParts) { + $partId = $backupPart.id.value + $partSize = $recoveryAPI.get($partId) + $estimateBackupSize += $partSize + $backupPartSizes += $partId + " data is " + $partSize + " MB`n" + } + + Write-Host "Estimated Backup Size: $estimateBackupSize MB" + Write-Host $backupPartSizes +} + +Function Get-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VAMI local users using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to retrieve VAMI local users + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIUser +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + $userResults = @() + + if($Name -ne "") { + try { + $user = $userAPI.get($name) + + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $users = $userAPI.list() + + foreach ($user in $users) { + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } + } + $userResults +} + +Function New-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to create new VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to create a new VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + New-VAMIUser -name lamw -fullname "William Lam" -role "operator" -email "lamw@virtuallyghetto.com" -password "VMware1!" +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$true) + ] + [String]$fullname, + [Parameter( + Mandatory=$true) + ] + [ValidateSet("admin","operator","superAdmin")][String]$role, + [Parameter( + Mandatory=$false) + ] + [String]$email="", + [Parameter( + Mandatory=$true) + ] + [String]$password + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + $createSpec = $userAPI.Help.add.config.CreateExample() + + $createSpec.username = $name + $createSpec.fullname = $fullname + $createSpec.role = $role + $createSpec.email = $email + $createSpec.password = [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$password + + try { + Write-Host "Creating new user $name ..." + $userAPI.add($createSpec) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Remove-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to remove VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to remove VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$false) + ] + [boolean]$confirm=$false + ) + + if(!$confirm) { + $answer = Read-Host -Prompt "Do you want to delete user $name (Y or N)" + if($answer -eq "Y" -or $answer -eq "y") { + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + try { + Write-Host "Deleting user $name ..." + $userAPI.delete($name) + } catch { + Write-Error $Error[0].exception.Message + } + } + } +} \ No newline at end of file diff --git a/Modules/VCHA/VCHA.psm1 b/Modules/VCHA/VCHA.psm1 new file mode 100644 index 0000000..160f0e7 --- /dev/null +++ b/Modules/VCHA/VCHA.psm1 @@ -0,0 +1,413 @@ +Function Get-VCHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the VCHA Configuration which provides you with + the current state, mode as well as the IP Addresses of the Active, + Passive & Witness Node. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to return VCHA Configuration + .EXAMPLE + Get-VCHAConfig +#> + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + + $vcHAState = $vcHAConfig.State + switch($vcHAState) { + configured { + $activeIp = $vcHAConfig.FailoverNodeInfo1.ClusterIpSettings.Ip.IpAddress + $passiveIp = $vcHAConfig.FailoverNodeInfo2.ClusterIpSettings.Ip.IpAddress + $witnessIp = $vcHAConfig.WitnessNodeInfo.IpSettings.Ip.IpAddress + + $vcHAClusterManager = Get-View failoverClusterManager + $vcHAMode = $vcHAClusterManager.getClusterMode() + + Write-Host "" + Write-Host -NoNewline -ForegroundColor Green "VCHA State: " + Write-Host -ForegroundColor White "$vcHAState" + Write-Host -NoNewline -ForegroundColor Green " VCHA Mode: " + Write-Host -ForegroundColor White "$vcHAMode" + Write-Host -NoNewline -ForegroundColor Green " ActiveIP: " + Write-Host -ForegroundColor White "$activeIp" + Write-Host -NoNewline -ForegroundColor Green " PassiveIP: " + Write-Host -ForegroundColor White "$passiveIp" + Write-Host -NoNewline -ForegroundColor Green " WitnessIP: " + Write-Host -ForegroundColor White "$witnessIp`n" + ;break + } + invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} + notConfigured { Write-Host "VCHA is not configured";break} + prepared { Write-Host "VCHA is being prepared, please try again in a little bit ...";break} + } +} + +Function Get-VCHAClusterHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the VCHA Cluster Health which provides more info + on each of the individual. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to return VCHA Cluster Health + .EXAMPLE + Get-VCHAClusterHealth +#> + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + $vcHAState = $vcHAConfig.State + + switch($vcHAState) { + invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} + notConfigured { Write-Host "VCHA is not configured";break} + prepared { Write-Host "VCHA is being prepared ...";break} + configured { + $vcHAClusterManager = Get-View failoverClusterManager + $healthInfo = $vcHAClusterManager.GetVchaClusterHealth() + + $vcClusterState = $healthInfo.RuntimeInfo.ClusterState + $nodeState = $healthInfo.RuntimeInfo.NodeInfo + + Write-Host "" + Write-Host -NoNewline -ForegroundColor Green "VCHA Cluster State: " + Write-Host -ForegroundColor White "$vcClusterState" + Write-Host -NoNewline -ForegroundColor Green "VCHA Node Information: " + $nodeState | Select NodeIp, NodeRole, NodeState + ;break + } + } +} + +Function Set-VCHAClusterMode { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you to set the mode of the VCHA Cluster whether + that is Enabled, Disabled or in Maintenance Mode. This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to set VCHA Cluster Mode + .EXAMPLE + Set-VCHAClusterMode -Enabled $true + .EXAMPLE + Set-VCHAClusterMode -Disabled $true + .EXAMPLE + Set-VCHAClusterMode -Maintenance $true +#> + param( + [Switch]$Enabled, + [Switch]$Disabled, + [Switch]$Maintenance + ) + + $vcHAClusterManager = Get-View failoverClusterManager + + if($Enabled) { + Write-Host "Setting VCHA Cluster to Enabled ..." + $task = $vcHAClusterManager.setClusterMode_Task("enabled") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } elseIf($Maintenance) { + Write-Host "Setting VCHA Cluster to Maintenance ..." + $task = $vcHAClusterManager.setClusterMode_Task("maintenance") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } elseIf($Disabled) { + Write-Host "`nSetting VCHA Cluster to Disabled ...`n" + $task = $vcHAClusterManager.setClusterMode_Task("disabled") + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + } +} + +Function New-VCHABasicConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you create a new "Basic" VCHA Cluster, it does not + cover the "Advanced" use case. You will need to ensure that you have a + "Self Managed" vCenter Server before attempting this workflow. + This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to create "Basic" VCHA Cluster + .PARAMETER VCSAVM + The name of the vCenter Server Appliance (VCSA) in which you wish to enable VCHA on (must be self-managed) + .PARAMETER HANetwork + The name of the Virtual Portgroup or Distributed Portgroup used for the HA Network + .PARAMETER ActiveHAIp + The IP Address for the Active VCSA node + .PARAMETER ActiveNetmask + The Netmask for the Active VCSA node + .PARAMETER PassiveHAIp + The IP Address for the Passive VCSA node + .PARAMETER PassiveNetmask + The Netmask for the Passive VCSA node + .PARAMETER WitnessHAIp + The IP Address for the Witness VCSA node + .PARAMETER WitnessNetmask + The Netmask for the Witness VCSA node + .PARAMETER PassiveDatastore + The name of the datastore to deploy the Passive node to + .PARAMETER WitnessDatastore + The name of the datastore to deploy the Witness node to + .PARAMETER VCUsername + The VCSA username (e.g. administrator@vghetto.local) + .PARAMETER VCPassword + The VCSA password + .EXAMPLE + New-VCHABasicConfig -VCSAVM "vcenter65-1" -HANetwork "DVPG-VCHA-Network" ` + -ActiveHAIp 192.168.1.70 ` + -ActiveNetmask 255.255.255.0 ` + -PassiveHAIp 192.168.1.71 ` + -PassiveNetmask 255.255.255.0 ` + -WitnessHAIp 192.168.1.72 ` + -WitnessNetmask 255.255.255.0 ` + -PassiveDatastore "vsanDatastore" ` + -WitnessDatastore "vsanDatastore" ` + -VCUsername "administrator@vghetto.local" ` + -VCPassword "VMware1!" +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$VCSAVM, + [String]$HANetwork, + [String]$ActiveHAIp, + [String]$ActiveNetmask, + [String]$PassiveHAIp, + [String]$PassiveNetmask, + [String]$PassiveDatastore, + [String]$WitnessHAIp, + [String]$WitnessNetmask, + [String]$WitnessDatastore, + # Crappy Implementation but need to research more into using PSH Credential + [String]$VCUsername, + [String]$VCPassword + ) + + $VCSAVMView = Get-View -ViewType VirtualMachine -Filter @{"name"=$VCSAVM} + if($VCSAVMView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Virtual Machine $VCSAVM" + return + } + + $HANetworkView = Get-View -ViewType Network -Filter @{"name"=$HANetwork} + if($HANetworkView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Network $HANetwork" + return + } + + $PassiveDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$PassiveDatastore} + if($PassiveDatastoreView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Passive Datastore $PassiveDatastore" + return + } + + $WitnessDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$WitnessDatastore} + if($WitnessDatastoreView -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to find Witness Datastore $WitnessDatastore" + return + } + + $vcIP = $VCSAVMView.Guest.IpAddress + if($vcIP -eq $null) { + Write-Host -ForegroundColor Red "Error: Unable to automatically retrieve the IP Address of $VCSAVM which is needed to use this function" + return + } + + # Retrieve Source VC SSL Thumbprint + $vcurl = "https://$vcIP" +add-type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + + public class IDontCarePolicy : ICertificatePolicy { + public IDontCarePolicy() {} + public bool CheckValidationResult( + ServicePoint sPoint, X509Certificate cert, + WebRequest wRequest, int certProb) { + return true; + } + } +"@ + [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy + # Need to do simple GET connection for this method to work + Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null + + $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") + # Get Thumbprint + add colons for a valid Thumbprint + $vcSSLThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' + + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $spec = New-Object VMware.Vim.VchaClusterDeploymentSpec + + $activeNetworkConfig = New-Object VMware.Vim.ClusterNetworkConfigSpec + $activeNetworkConfig.NetworkPortGroup = $HANetworkView.MoRef + $ipSettings = New-Object Vmware.Vim.CustomizationIPSettings + $ipSettings.SubnetMask = $ActiveNetmask + $activeIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $activeIpSpec.IpAddress = $ActiveHAIp + $ipSettings.Ip = $activeIpSpec + $activeNetworkConfig.IpSettings = $ipSettings + $spec.ActiveVcNetworkConfig = $activeNetworkConfig + + $activeVCConfig = New-Object Vmware.Vim.SourceNodeSpec + $activeVCConfig.ActiveVc = $VCSAVMView.MoRef + $serviceLocator = New-Object Vmware.Vim.ServiceLocator + $credential = New-Object VMware.Vim.ServiceLocatorNamePassword + $credential.username = $VCUsername + $credential.password = $VCPassword + $serviceLocator.Credential = $credential + $serviceLocator.InstanceUuid = $global:DefaultVIServer.InstanceUuid + $serviceLocator.Url = $vcurl + $serviceLocator.SslThumbprint = $vcSSLThumbprint + $activeVCConfig.ManagementVc = $serviceLocator + $spec.ActiveVcSpec = $activeVCConfig + + $passiveSpec = New-Object VMware.Vim.PassiveNodeDeploymentSpec + $passiveSpec.Folder = (Get-View (Get-Folder vm)).MoRef + $passiveIpSettings = New-object Vmware.Vim.CustomizationIPSettings + $passiveIpSettings.SubnetMask = $passiveNetmask + $passiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $passiveIpSpec.IpAddress = $passiveHAIp + $passiveIpSettings.Ip = $passiveIpSpec + $passiveSpec.IpSettings = $passiveIpSettings + $passiveSpec.NodeName = $VCSAVMView.Name + "-Passive" + $passiveSpec.datastore = $PassiveDatastoreView.MoRef + $spec.PassiveDeploymentSpec = $passiveSpec + + $witnessSpec = New-Object VMware.Vim.NodeDeploymentSpec + $witnessSpec.Folder = (Get-View (Get-Folder vm)).MoRef + $witnessSpec.NodeName = $VCSAVMView.Name + "-Witness" + $witnessIpSettings = New-object Vmware.Vim.CustomizationIPSettings + $witnessIpSettings.SubnetMask = $witnessNetmask + $witnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $witnessIpSpec.IpAddress = $witnessHAIp + $witnessIpSettings.Ip = $witnessIpSpec + $witnessSpec.IpSettings = $witnessIpSettings + $witnessSpec.datastore = $WitnessDatastoreView.MoRef + $spec.WitnessDeploymentSpec = $witnessSpec + + Write-Host "`nDeploying VCHA Cluster ...`n" + $task = $vcHAClusterConfig.deployVcha_Task($spec) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task -Verbose +} + +Function Remove-VCHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Nov 20, 2016 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function allows you destroy a VCHA Cluster. In addition, you have + the option to specify whether you would like both the Passive & Witness + Virtual Machines be deleted after the VCHA Cluster has been destroyed. + This is only available on VCSA 6.5 (vSphere 6.5 or greater) + .DESCRIPTION + Function to destroy a VCHA Cluster Mode + .EXAMPLE + Remove-VCHAConfig + .EXAMPLE + Remove-VCHAConfig -Confirm:$false + .EXAMPLE + Remove-VCHAConfig -DeleteVM $true -Confirm:$false + .NOTES + Before you can destroy a VCHA Cluster, you must make sure it is first + disabled. Run the Set-VCHAClusterMode -Disabled $true to do so +#> + param( + [Boolean]$Confirm=$true, + [Switch]$DeleteVM=$false + ) + + $Verified = $false + if($Confirm -eq $true) { + Write-Host -ForegroundColor Yellow "`nDo you want to destroy VCHA Cluster?" + $answer = Read-Host -Prompt "Do you accept (Y or N)" + if($answer -eq "Y" -or $answer -eq "y") { + $Verified = $true + } + } else { + $Verified = $true + } + + if($Verified) { + $vcHAClusterManager = Get-View failoverClusterManager + $vcHAMode = $vcHAClusterManager.getClusterMode() + + if($vcHAMode -ne "disabled") { + Write-Host -ForegroundColor Yellow "To destroy VCHA Cluster, you must first set the VCHA Cluster Mode to `"Disabled`"" + Exit + } + + # Query BIOS UUID of the Passive/Witness to be able to delete + if($DeleteVM) { + $vcHAClusterConfig = Get-View failoverClusterConfigurator + $vcHAConfig = $vcHAClusterConfig.getVchaConfig() + $passiveBiosUUID = $vcHAConfig.FailoverNodeInfo2.biosUuid + $witnessBiosUUID = $vcHAConfig.WitnessNodeInfo.biosUuid + } + + $vcHAClusterConfig = Get-View failoverClusterConfigurator + + Write-Host "Destroying VCHA Cluster ..." + $task = $vcHAClusterConfig.destroyVcha_Task() + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task + + # After VCHA Cluster has been destroyed, we can now delete the VMs we had queried earlier + if($DeleteVM) { + if($passiveBiosUUID -ne $null -and $witnessBiosUUID -ne $null) { + $searchIndex = Get-View searchIndex + + $passiveVM = $searchIndex.FindByUuid($null,$passiveBiosUUID,$true,$null) + $witnessVM = $searchIndex.FindByUuid($null,$witnessBiosUUID,$true,$null) + + if($passiveVM -ne $null -and $witnessVM -ne $null) { + Write-Host "Powering off & deleting Passive VM ..." + Stop-VM -VM (Get-View $passiveVM).Name -Confirm:$false | Out-Null + Remove-VM (Get-View $passiveVM).Name -DeletePermanently -Confirm:$false + Write-Host "Powering off & deleting Witness VM ..." + Stop-VM -VM (Get-View $witnessVM).Name -Confirm:$false | Out-Null + Remove-VM (Get-View $witnessVM).Name -DeletePermanently -Confirm:$false + } + } + } + } +} diff --git a/Modules/VMCPFunctions/VMCPFunctions.psm1 b/Modules/VMCPFunctions/VMCPFunctions.psm1 new file mode 100644 index 0000000..4f9b16e --- /dev/null +++ b/Modules/VMCPFunctions/VMCPFunctions.psm1 @@ -0,0 +1,322 @@ +function Get-VMCPSettings { +<# + .NOTES + =========================================================================== + Created on: 10/27/2015 9:25 PM + Created by: Brian Graf + 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 + + .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 + + 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=$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 + } + } + } +} + +function Set-VMCPSettings { +<# + .NOTES + =========================================================================== + Created on: 10/27/2015 9:25 PM + Created by: Brian Graf + 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 + them to configure the additional VMCP settings + For each parameter, users should use the 'Tab' button to auto-fill the + possible values. + + .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 + + 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(SupportsShouldProcess=$true,ConfirmImpact="High")] + param + ( + [Parameter(Mandatory=$true, + ValueFromPipeline=$True, + ValueFromPipelineByPropertyName=$True, + HelpMessage='What is the Cluster Name?')] + $cluster, + + [Parameter(Mandatory=$False, + ValueFromPipeline=$False, + HelpMessage='$True=Enabled $False=Disabled')] + [bool]$enableVMCP, + + [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=$False, + ValueFromPipeline=$False, + HelpMessage='Options available for an APD response')] + [ValidateSet("disabled","restartConservative","restartAggressive","warning")] + [string]$VmStorageProtectionForAPD, + + [Parameter(Mandatory=$False, + ValueFromPipeline=$False, + HelpMessage='Value in seconds')] + [Int]$VmTerminateDelayForAPDSec, + + [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")] + [string]$VmReactionOnAPDCleared, + + [Parameter(Mandatory=$False)] + [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers + ) + + Process { + + Foreach ($Clus in $Cluster) { + + 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} + 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 + + # 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 + + #Storage Protection For PDL + If ($PSBoundParameters.ContainsKey('VmStorageProtectionForPDL')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $VmStorageProtectionForPDL + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $ActualSettings.'Protection For PDL' + } + + #Storage Protection for APD + 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 + If ($PSBoundParameters.ContainsKey('VmReactionOnAPDCleared')) { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" + } else { + $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = $ActualSettings.'Reaction on APD Cleared' + } + + # Execute API Call + If ($pscmdlet.ShouldProcess($CL.Name,"Modify VMCP configuration")) { + $modify = $true + $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server + $Task = $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) + } + + # Wait for the reconfiguration task to finish to show the result + If ($Task) { + $TaskID = "Task-" + $($Task.Value) + Get-Task -Id $TaskID -Server $Server | Wait-Task | Out-Null + Get-VMCPSettings -Cluster $CL -Server $Server + } + } + } + } +} diff --git a/Modules/apply-hardening/apply-hardening.psm1 b/Modules/apply-hardening/apply-hardening.psm1 new file mode 100644 index 0000000..94b1279 --- /dev/null +++ b/Modules/apply-hardening/apply-hardening.psm1 @@ -0,0 +1,93 @@ +function Apply-Hardening { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2016.11 ver 2.0 Base Release + =========================================================================== + External Code Sources: + + =========================================================================== + 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 + Keyword: VM, Hardening, Security + =========================================================================== + + .DESCRIPTION + Applys a set of Hardening options to your VMs + + .Example + Get-VM TST* | Apply-Hardening + + .Example + $SampleVMs = Get-VM "TST*" + Apply-Hardening -VMs $SampleVMs + + .PARAMETER VMs + Specify the VMs + + +#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=$True, + Position=0)] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] + $VMs +) + +Process { +#region: Create Options + $ExtraOptions = @{ + "isolation.tools.diskShrink.disable"="true"; + "isolation.tools.diskWiper.disable"="true"; + "isolation.tools.copy.disable"="true"; + "isolation.tools.paste.disable"="true"; + "isolation.tools.dnd.disable"="true"; + "isolation.tools.setGUIOptions.enable"="false"; + "log.keepOld"="10"; + "log.rotateSize"="100000" + "RemoteDisplay.maxConnections"="2"; + "RemoteDisplay.vnc.enabled"="false"; + + } + if ($DebugPreference -eq "Inquire") { + Write-Output "VM Hardening Options:" + $ExtraOptions | Format-Table -AutoSize + } + + $VMConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + + Foreach ($Option in $ExtraOptions.GetEnumerator()) { + $OptionValue = New-Object VMware.Vim.optionvalue + $OptionValue.Key = $Option.Key + $OptionValue.Value = $Option.Value + $VMConfigSpec.extraconfig += $OptionValue + } +#endregion + +#region: Apply Options + ForEach ($VM in $VMs){ + $VMv = Get-VM $VM | Get-View + $state = $VMv.Summary.Runtime.PowerState + Write-Output "...Starting Reconfiguring VM: $VM " + $TaskConf = ($VMv).ReconfigVM_Task($VMConfigSpec) + if ($state -eq "poweredOn") { + Write-Output "...Migrating VM: $VM " + $TaskMig = $VMv.MigrateVM_Task($null, $_.Runtime.Host, 'highPriority', $null) + } + } + } +#endregion +} \ No newline at end of file diff --git a/Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 b/Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 new file mode 100644 index 0000000..ad6227c --- /dev/null +++ b/Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 @@ -0,0 +1,372 @@ +<# + .NOTES + =========================================================================== + Created on: 5/27/2015 3:24 PM + Created by: Brian Graf + Twitter: @vBrianGraf + Blog: http://www.vtagion.com + =========================================================================== +#> + +#Encoded Hardening Guide +$Global:Base64 = "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" + +Function Import-Xls { + +<# +.SYNOPSIS +Import an Excel file. + +.DESCRIPTION +Import an excel file. Since Excel files can have multiple worksheets, you can specify the worksheet you want to import. You can specify it by number (1, 2, 3) or by name (Sheet1, Sheet2, Sheet3). Imports Worksheet 1 by default. + +.PARAMETER Path +Specifies the path to the Excel file to import. You can also pipe a path to Import-Xls. + +.PARAMETER Worksheet +Specifies the worksheet to import in the Excel file. You can specify it by name or by number. The default is 1. +Note: Charts don't count as worksheets, so they don't affect the Worksheet numbers. + +.INPUTS +System.String + +.OUTPUTS +Object + +.EXAMPLE +".\employees.xlsx" | Import-Xls -Worksheet 1 +Import Worksheet 1 from employees.xlsx + +.EXAMPLE +".\employees.xlsx" | Import-Xls -Worksheet "Sheet2" +Import Worksheet "Sheet2" from employees.xlsx + +.EXAMPLE +".\deptA.xslx", ".\deptB.xlsx" | Import-Xls -Worksheet 3 +Import Worksheet 3 from deptA.xlsx and deptB.xlsx. +Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. + +.EXAMPLE +Get-ChildItem *.xlsx | Import-Xls -Worksheet "Employees" +Import Worksheet "Employees" from all .xlsx files in the current directory. +Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. + +.LINK +Import-Xls +http://gallery.technet.microsoft.com/scriptcenter/17bcabe7-322a-43d3-9a27-f3f96618c74b +Export-Xls +http://gallery.technet.microsoft.com/scriptcenter/d41565f1-37ef-43cb-9462-a08cd5a610e2 +Import-Csv +Export-Csv + +.NOTES +Author: Francis de la Cerna +Created: 2011-03-27 +Modified: 2011-04-09 +#Requires –Version 2.0 + +#> + + [CmdletBinding(SupportsShouldProcess = $true)] + Param ( + [parameter( + mandatory = $true, + position = 1, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [String[]]$Path, + [parameter(mandatory = $false)] + $Worksheet = 1, + [parameter(mandatory = $false)] + [switch]$Force + ) + + Begin { + function GetTempFileName($extension) { + $temp = [io.path]::GetTempFileName(); + $params = @{ + Path = $temp; + Destination = $temp + $extension; + Confirm = $false; + Verbose = $VerbosePreference; + } + Move-Item @params; + $temp += $extension; + return $temp; + } + + # since an extension like .xls can have multiple formats, this + # will need to be changed + # + $xlFileFormats = @{ + # single worksheet formats + '.csv' = 6; # 6, 22, 23, 24 + '.dbf' = 11; # 7, 8, 11 + '.dif' = 9; # + '.prn' = 36; # + '.slk' = 2; # 2, 10 + '.wk1' = 31; # 5, 30, 31 + '.wk3' = 32; # 15, 32 + '.wk4' = 38; # + '.wks' = 4; # + '.xlw' = 35; # + + # multiple worksheet formats + '.xls' = -4143; # -4143, 1, 16, 18, 29, 33, 39, 43 + '.xlsb' = 50; # + '.xlsm' = 52; # + '.xlsx' = 51; # + '.xml' = 46; # + '.ods' = 60; # + } + + $xl = New-Object -ComObject Excel.Application; + $xl.DisplayAlerts = $false; + $xl.Visible = $false; + } + + Process { + $Path | ForEach-Object { + + if ($Force -or $psCmdlet.ShouldProcess($_)) { + + $fileExist = Test-Path $_ + + if (-not $fileExist) { + Write-Error "Error: $_ does not exist" -Category ResourceUnavailable; + } else { + # create temporary .csv file from excel file and import .csv + # + $_ = (Resolve-Path $_).toString(); + $wb = $xl.Workbooks.Add($_); + if ($?) { + $csvTemp = GetTempFileName(".csv"); + $ws = $wb.Worksheets.Item($Worksheet); + $ws.SaveAs($csvTemp, $xlFileFormats[".csv"]); + $wb.Close($false); + Remove-Variable -Name ('ws', 'wb') -Confirm:$false; + Import-Csv $csvTemp; + Remove-Item $csvTemp -Confirm:$false -Verbose:$VerbosePreference; + } + } + } + } + } + + End { + $xl.Quit(); + Remove-Variable -name xl -Confirm:$false; + [gc]::Collect(); + } +} +Function Show-HardeningGuide { + $Global:XLS = Import-XLS $env:temp\HG.XLSX + $ShowGuide = $XLS | Out-GridView +} +Function Assess-VM { +<# + .DESCRIPTION + This cmdlet will allow you to assess the security of the VM's in your environment + .EXAMPLE + # Specify Virtual Machines to Assess + $VM = Get-VM MGMT-AD, MGMT-VCO, WebCommander, Exchange01 + + # Assess against Risk Profile 2 + Assess-VM -VM $VM -RiskProfile 2 + + .EXAMPLE + # Assess all VM's against specific ID's + $VM = Get-VM + Assess-VM -VM $VM -ID 9,10,12,22,33,49 + + .EXAMPLE + # Assess All VM's against All ID's + Assess-VM -VM (Get-VM) +#> + [CmdletBinding( + DefaultParameterSetName = ”ID” + )] + Param ( + #Object receiving the hardening + [Parameter( + Position = 0, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'What object are you running this against?')] + #[string]$VMs, + [PSObject[]] + $VM, + + #Hardening Guide Profile + [Parameter(Mandatory = $false, + ValueFromPipeline = $false, + HelpMessage = "You must choose which Hardening Guide Risk Profile to use (1-3).", + ParameterSetName = 'RiskProfile')] + #[ValidateRange(1,3)] + [int]$RiskProfile, + + #Hardening Guide Guideline ID + [Parameter(Mandatory = $false, + ValueFromPipeline = $false, + ParameterSetName = 'ID')] + [string[]]$ID, + + [parameter(ParameterSetName = "ID", + ValueFromPipeline = $false)] + $AllIDs = "1" + ) + + BEGIN { + # Validate VM Parameter + # This ensures the object being passed is an object rather than a string + [int]$Err = 0 + foreach ($object in $vm) { if (($object.GetType().FullName) -ne "VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl") { $Err += 1 } } + if ($Err -gt 0) { + Write-Error "One or more objects specified in the `$VM parameter is not a VM object" + Break + } + + + # Get the Date, used for folder creation and report + $Date = (Get-Date -Format MM.dd.yy) + + # Create array of ID's + if (-not $ID) { + $IDs = $null + } else { + $IDs = $ID.Replace(' ', '').split(',') + } + + # Manage Risk Profile Parameter value + $RiskArray = @("1","2","3") + + + ### HTML CSS CODE ### + $head = @' + +'@ + ##################### + + #C heck to see if HG exists in the $Env:Temp folder, if not, place it there + if (!(Test-Path -Path "$env:Temp\HG.XLSX")) { + + # Decode Hardening Guide from script + $Content = [System.Convert]::FromBase64String($Global:Base64) + Set-Content -Path $env:temp\HG.XLSX -Value $Content -Encoding Byte + } + + #Save Imported Hardening Guide to $XLS + $Global:XLS = Import-XLS $env:temp\HG.XLSX + + } + + PROCESS { + + # If risk profile is provided + if ($RiskArray -contains $RiskProfile) { + + $Guidelines = ($XLS | ?{ $_."Guideline ID" -like "VM*" -and $_."Risk Profile" -match $RiskProfile }) + foreach ($Guideline in $Guidelines) { + + $ID = $Guideline.ID + $GuidelineID = $Guideline."Guideline ID" + $Name = ($GuidelineID.Split("."))[1] + $Assessment = $Guideline."PowerCLI Command Assessment" + $Assessment = $Assessment -replace "Get-VM", "`$VM" + $Description = $Guideline."Description" + + Write-Host "Processing: $ID - $Name" -ForegroundColor CYAN + if ($assessment -ne "") { + $run = iex ($Assessment) + if ($run -eq $null) { + $run = New-Object System.object + $run | Add-Member -type NoteProperty -name Name -value "Clear" + $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" + } + $Task = $run | ConvertTo-Html -Fragment -PreContent "

$ID - $Name

$Description

" -PostContent "
" | Out-String + $Tasks += $task + } + } + } elseif ($AllIDs -eq "1" -and $ID -eq $null) { + + $Guidelines = ($XLS | ?{ $_."Guideline ID" -like "VM*" }) + foreach ($Guideline in $Guidelines) { + $ID = $Guideline.ID + $GuidelineID = $Guideline."Guideline ID" + $Name = ($GuidelineID.Split("."))[1] + $Assessment = $Guideline."PowerCLI Command Assessment" + $Assessment = $Assessment -replace "Get-VM", "`$VM" + $Description = $Guideline."Description" + Write-Host "Processing: $ID - $Name" -ForegroundColor CYAN + if ($assessment -ne "") { + $run = iex ($Assessment) + if ($run -eq $null) { + $run = New-Object System.object + $run | Add-Member -type NoteProperty -name Name -value "Clear" + $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" + } + $Task = $run | ConvertTo-Html -Fragment -PreContent "

$ID - $Name


$Description

" | Out-String + $Tasks += $task + } + } + + } else { + # If Guideline IDs are provided + foreach ($line in $ID) { + $Guideline = ($XLS | ?{ $_."Guideline ID" -like "VM*" -and $_."ID" -eq $Line }) + $GuidelineID = $Guideline."Guideline ID" + if ($GuidelineID -eq $null) { + Write-Host "$line is an invalid ID for this object... moving to next ID" -ForegroundColor 'Red' + Continue + } + $Name = ($GuidelineID.Split("."))[1] + + $Description = $Guideline."Description" + $Assessment = $Guideline."PowerCLI Command Assessment" + $Assessment = $Assessment -replace "Get-VM", "`$VM" + Write-Host "Processing: $line - $Name" -ForegroundColor CYAN + if ($assessment -ne "") { + $run = iex ($Assessment) + if ($run -eq $null) { + $run = New-Object System.object + $run| Add-Member -type NoteProperty -name Name -value "Clear" + $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" + } + $Task = $run | ConvertTo-Html -Fragment -PreContent "

$line - $Name


$Description

" -PostContent "
" | Out-String + $Tasks += $task + } + } + } + } + + END { + # if no tasks were generated, end function and do NOT create empty report + if ($tasks -eq $null) {Break } + + # Get's end time Hours/Minutes for creating the HTML report + $time = (get-date -Format HH-mm) + + # Checks to see if a folder has been created for that date. If not, it will create one. + if (!(Test-Path c:\Temp\$Date)) { + Write-Host "Folder does not exist... Creating" -ForegroundColor 'Yellow' + New-item "C:\Temp\$date" -type Directory + } + + # HTML Report is then generated + $HTML = ConvertTo-HTML -head $head -PostContent "
$Tasks
" -PreContent “

vSphere 6 VM Hardening Report

” | Out-File "c:\Temp\$Date\$Time-VMAssessment.html" -Force + + # Report is opened for user to see + invoke-item "c:\Temp\$Date\$Time-VMAssessment.html" + } +} + From 81f0299bce6ca2975d22812a879e3fa3087eaec1 Mon Sep 17 00:00:00 2001 From: Kevin Kirkpatrick Date: Mon, 24 Apr 2017 12:09:00 -0400 Subject: [PATCH 82/90] Removed standalone .psm1 files from /Modules Signed-off-by: Kevin Kirkpatrick --- Modules/Backup-VCSA.psm1 | 202 ------ Modules/Get-NICDetails.psm1 | 93 --- Modules/Get-NewAndRemovedVMs.psm1 | 131 ---- Modules/Get-VMmaxIOPS.psm1 | 114 --- Modules/Konfig-ESXi.psm1 | 234 ------ Modules/PSvLIMessage.psm1 | 123 ---- Modules/ProactiveHA.psm1 | 468 ------------ Modules/Recommend-Sizing.psm1 | 227 ------ Modules/Set-CBT.psm1 | 111 --- Modules/Start-UNMAP.psm1 | 99 --- Modules/VAMI.psm1 | 716 ------------------- Modules/VCHA.psm1 | 413 ----------- Modules/VMCPFunctions.psm1 | 322 --------- Modules/apply-hardening.psm1 | 93 --- Modules/vSphere_Hardening_Assess_VM_v1a.psm1 | 372 ---------- 15 files changed, 3718 deletions(-) delete mode 100644 Modules/Backup-VCSA.psm1 delete mode 100644 Modules/Get-NICDetails.psm1 delete mode 100644 Modules/Get-NewAndRemovedVMs.psm1 delete mode 100644 Modules/Get-VMmaxIOPS.psm1 delete mode 100644 Modules/Konfig-ESXi.psm1 delete mode 100644 Modules/PSvLIMessage.psm1 delete mode 100644 Modules/ProactiveHA.psm1 delete mode 100644 Modules/Recommend-Sizing.psm1 delete mode 100644 Modules/Set-CBT.psm1 delete mode 100644 Modules/Start-UNMAP.psm1 delete mode 100755 Modules/VAMI.psm1 delete mode 100644 Modules/VCHA.psm1 delete mode 100644 Modules/VMCPFunctions.psm1 delete mode 100644 Modules/apply-hardening.psm1 delete mode 100644 Modules/vSphere_Hardening_Assess_VM_v1a.psm1 diff --git a/Modules/Backup-VCSA.psm1 b/Modules/Backup-VCSA.psm1 deleted file mode 100644 index 271a7ff..0000000 --- a/Modules/Backup-VCSA.psm1 +++ /dev/null @@ -1,202 +0,0 @@ -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 - 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) - -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 {} -} diff --git a/Modules/Get-NICDetails.psm1 b/Modules/Get-NICDetails.psm1 deleted file mode 100644 index 30f1440..0000000 --- a/Modules/Get-NICDetails.psm1 +++ /dev/null @@ -1,93 +0,0 @@ -function Get-NICDetails { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2017.02 ver 1.0 Base Release - =========================================================================== - External Code Sources: - - - =========================================================================== - Tested Against Environment: - vSphere Version: ESXi 6.0 U2, ESXi 6.5 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2008 R2, Server 2012 R2 - Keyword: ESXi, NIC, vmnic, Driver, Firmware - =========================================================================== - - .DESCRIPTION - Reports Firmware and Driver Details for your ESXi vmnics. - - .Example - Get-NICDetails -Clustername * - - .PARAMETER Clustername - Name or Wildcard of your vSphere Cluster Name to process. - - -#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)] - [ValidateNotNullorEmpty()] - [String] $Clustername - -) - -Begin { - $Validate = $True - - if (($myCluster = Get-Cluster -Name $Clustername).count -lt 1) { - $Validate = $False - thow "No Cluster '$myCluster' found!" - } - -} - -Process { - - $MyView = @() - if ($Validate -eq $True) { - - foreach ($myVMhost in ($myCluster | Get-VMHost)) { - - $esxcli2 = Get-ESXCLI -VMHost $myVMhost -V2 - $niclist = $esxcli2.network.nic.list.invoke() - - $nicdetails = @() - foreach ($nic in $niclist) { - - $args = $esxcli2.network.nic.get.createargs() - $args.nicname = $nic.name - $nicdetail = $esxcli2.network.nic.get.Invoke($args) - $nicdetails += $nicdetail - - } - ForEach ($nicdetail in $nicdetails){ - $NICReport = [PSCustomObject] @{ - Host = $myVMhost.Name - vmnic = $nicdetail.Name - LinkStatus = $nicdetail.LinkStatus - BusInfo = $nicdetail.driverinfo.BusInfo - Driver = $nicdetail.driverinfo.Driver - FirmwareVersion = $nicdetail.driverinfo.FirmwareVersion - DriverVersion = $nicdetail.driverinfo.Version - } - $MyView += $NICReport - } - - } - - $MyView - - } -} -} \ No newline at end of file diff --git a/Modules/Get-NewAndRemovedVMs.psm1 b/Modules/Get-NewAndRemovedVMs.psm1 deleted file mode 100644 index 4a2e3ba..0000000 --- a/Modules/Get-NewAndRemovedVMs.psm1 +++ /dev/null @@ -1,131 +0,0 @@ -function Get-NewAndRemovedVMs { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.12 ver 1.0 Base Release - =========================================================================== - External Code Sources: - https://github.com/alanrenouf/vCheck-vSphere - =========================================================================== - 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, VM - =========================================================================== - - .DESCRIPTION - This Function report newly created and deleted VMs by Cluster. - - .Example - Get-NewAndRemovedVMs -ClusterName Cluster* | ft -AutoSize - - .Example - Get-NewAndRemovedVMs -ClusterName Cluster01 -Days 90 - - .PARAMETER ClusterName - Name or Wildcard of your vSphere Cluster Name(s) to report. - - .PARAMETER Day - Range in Days to report. - - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0, HelpMessage = "Name or Wildcard of your vSphere Cluster Name to report")] - [ValidateNotNullorEmpty()] - [String]$ClusterName, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, HelpMessage = "Range in Days to report")] - [ValidateNotNullorEmpty()] - [String]$Days = "30" -) -Begin { - function Get-VIEventPlus { - - param( - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity, - [string[]]$EventType, - [DateTime]$Start, - [DateTime]$Finish = (Get-Date), - [switch]$Recurse, - [string[]]$User, - [Switch]$System, - [string]$ScheduledTask, - [switch]$FullMessage = $false, - [switch]$UseUTC = $false - ) - - process { - $eventnumber = 100 - $events = @() - $eventMgr = Get-View EventManager - $eventFilter = New-Object VMware.Vim.EventFilterSpec - $eventFilter.disableFullMessage = ! $FullMessage - $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity - $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} - $eventFilter.eventTypeId = $EventType - if($Start -or $Finish){ - $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime - if($Start){ - $eventFilter.time.beginTime = $Start - } - if($Finish){ - $eventFilter.time.endTime = $Finish - } - } - if($User -or $System){ - $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername - if($User){ - $eventFilter.UserName.userList = $User - } - if($System){ - $eventFilter.UserName.systemUser = $System - } - } - if($ScheduledTask){ - $si = Get-View ServiceInstance - $schTskMgr = Get-View $si.Content.ScheduledTaskManager - $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | - where {$_.Info.Name -match $ScheduledTask} | - Select -First 1 | - Select -ExpandProperty MoRef - } - if(!$Entity){ - $Entity = @(Get-Folder -NoRecursion) - } - $entity | %{ - $eventFilter.entity.entity = $_.ExtensionData.MoRef - $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - while($eventsBuffer){ - $events += $eventsBuffer - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - } - $eventCollector.DestroyCollector() - } - if (-not $UseUTC) - { - $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } - } - - $events - } -} -} - -process { - $result = Get-VIEventPlus -Start ((get-date).adddays(-$Days)) -EventType @("VmCreatedEvent", "VmBeingClonedEvent", "VmBeingDeployedEvent","VmRemovedEvent") - $sortedResult = $result | Select CreatedTime, @{N='Cluster';E={$_.ComputeResource.Name}}, @{Name="VMName";Expression={$_.vm.name}}, UserName, @{N='Type';E={$_.GetType().Name}}, FullFormattedMessage | Sort CreatedTime - $sortedResult | where {$_.Cluster -like $ClusterName} -} -} \ No newline at end of file diff --git a/Modules/Get-VMmaxIOPS.psm1 b/Modules/Get-VMmaxIOPS.psm1 deleted file mode 100644 index 27af1ad..0000000 --- a/Modules/Get-VMmaxIOPS.psm1 +++ /dev/null @@ -1,114 +0,0 @@ -function Get-VMmaxIOPS { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.10 ver 1.0 Base Release - 2016.11 ver 1.1 Added vSphere 6.5 Support, New Counters, More Error Handling - =========================================================================== - 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, 6.5 - PowerCLI Version: PowerCLI 6.3 R1, 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Windows Server 2012 R2 - =========================================================================== - Keywords vSphere, ESXi, VM, Storage - =========================================================================== - - .DESCRIPTION - This Function will Create a VM Disk IOPS Report - - .Example - Get-VM TST* | Get-VMmaxIOPS -Minutes 60 | FT -Autosize - - .Example - $SampleVMs = Get-VM "TST*" - Get-VMmaxIOPS -VMs $SampleVMs -Minutes 60 - - .PARAMETER VMs - Specify the VMs - - .PARAMETER Minutes - Specify the Minutes to report (10080 is one Week) - -#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=$True, Position=0)] - [ValidateNotNullorEmpty()] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $VMs, - [Parameter(Mandatory=$false, Position=1, HelpMessage = "Specify the Minutes to report (10080 is one Week)")] - [ValidateNotNullorEmpty()] - [int] $Minutes = 30 -) -Begin { - # none - } -Process { - if ($_.PowerState -eq "PoweredOn") { - #region: Global Definitions - [int]$TimeRange = "-" + $Minutes - #endregion - - #region: Creating VM Stats - Write-Verbose "$(Get-Date -Format G) Create VM Stats..." - $VMMetrics = "virtualdisk.numberwriteaveraged.average","virtualdisk.numberreadaveraged.average" - $Start = (Get-Date).AddMinutes($TimeRange) - $stats = Get-Stat -Realtime -Stat $VMMetrics -Entity $VMs -Start $Start -Verbose:$False - Write-Verbose "$(Get-Date -Format G) Create VM Stats completed" - #endregion - - #region: Creating HD-Tab - Write-Verbose "$(Get-Date -Format G) Create HD Tab..." - $hdTab = @{} - foreach($hd in (Get-Harddisk -VM $VMs)){ - $controllerKey = $hd.Extensiondata.ControllerKey - $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey} - $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('[') - } - Write-Verbose "$(Get-Date -Format G) Create HD Tab completed" - #endregion - - #region: Creating Reports - Write-Verbose "$(Get-Date -Format G) Create Report..." - $reportPerf = @() - $reportPerf = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - VM = $_.Values[0] - Disk = $_.Values[1] - IOPSWriteAvg = [math]::round( ($_.Group | ` - where{$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} | ` - Measure-Object -Property Value -Average).Average,2) - IOPSReadAvg = [math]::round( ($_.Group | ` - where{$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} | ` - Measure-Object -Property Value -Average).Average,2) - Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]] - } - } - Write-Verbose "$(Get-Date -Format G) Create Report completed" - #endregion - - - } - Else { - Write-Error "VM $($_.Name) is Powered Off! Processing Skipped" - } - $reportPerf | Select-Object VM, Disk, Datastore, IOPSWriteAvg, IOPSReadAvg - } - -End { - # none - } - -} \ No newline at end of file diff --git a/Modules/Konfig-ESXi.psm1 b/Modules/Konfig-ESXi.psm1 deleted file mode 100644 index f14386a..0000000 --- a/Modules/Konfig-ESXi.psm1 +++ /dev/null @@ -1,234 +0,0 @@ -function Konfig-ESXi { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.12 ver 1.0 Base Release - 2016.12 ver 1.1 ESXi 6.5 Tests, Minor enhancements - =========================================================================== - External Code Sources: - Function My-Logger : http://www.virtuallyghetto.com/ - =========================================================================== - Tested Against Environment: - vSphere Version: ESXi 5.5 U2, ESXi 6.5 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - Keyword: ESXi, NTP, SSH, Syslog, SATP, - =========================================================================== - - .DESCRIPTION - This Function sets the Basic settings for a new ESXi. - - * NTP - * SSH - * Syslog - * Power Management - * HP 3PAR SATP/PSP Rule - * ... - - .Example - Konfig-ESXi -VMHost myesxi.lan.local -NTP 192.168.2.1, 192.168.2.2 -syslog "udp://loginsight.lan.local:514" - - .PARAMETER VMHost - Host to configure. - - .PARAMETER NTP - NTP Server(s) to set. - - .PARAMETER Syslog - Syslog Server to set, e.g. "udp://loginsight.lan.local:514" - - DNS Name must be resolvable! - - -#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)] - [String] $VMHost, - [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=1)] - [array]$NTP, - [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=2)] - [String] $syslog - -) - -Begin { - Function My-Logger { - param( - [Parameter(Mandatory=$true)] - [String]$message - ) - - $timeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" - - Write-Host -NoNewline -ForegroundColor White "[$timestamp]" - Write-Host -ForegroundColor Green " $message" - } - function Set-MyESXiOption { - [CmdletBinding()] - param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] - [String] $Name, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] - [String] $Value - ) - process { - $myESXiOption = Get-AdvancedSetting -Entity $ESXiHost -Name $Name - if ($myESXiOption.Value -ne $Value) { - My-Logger " Setting ESXi Option $Name to Value $Value" - $myESXiOption | Set-AdvancedSetting -Value $Value -Confirm:$false | Out-Null - } - else { - My-Logger " ESXi Option $Name already has Value $Value" - } - } - } -} - -Process { - $Validate = $True - - #region: Start vCenter Connection - My-Logger "Starting to Process ESXi Server Connection to $VMHost ..." - if (($global:DefaultVIServers).count -gt 0) { - Disconnect-VIServer -Force -Confirm:$False -ErrorAction SilentlyContinue - } - $VIConnection = Connect-VIServer -Server $VMHost - if (-not $VIConnection.IsConnected) { - Write-Error "ESXi Connection Failed." - $Validate = $False - } - elseif ($VIConnection.ProductLine -ne "EmbeddedEsx") { - Write-Error "Connencted System is not an ESXi." - $Validate = $False - } - else { - $ESXiHost = Get-VMHost - My-Logger "Connected ESXi Version: $($ESXiHost.Version) $($ESXiHost.Build) " - } - #endregion - - if ($Validate -eq $True) { - - #region: Enable SSH and disable SSH Warning - $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} - My-Logger "Starting SSH Service..." - if($SSHService.Running -ne $True){ - Start-VMHostService -HostService $SSHService -Confirm:$false | Out-Null - } - else { - My-Logger " SSH Service is already running" - } - My-Logger "Setting SSH Service to Automatic Start..." - if($SSHService.Policy -ne "automatic"){ - Set-VMHostService -HostService $SSHService -Policy "Automatic" | Out-Null - } - else { - My-Logger " SSH Service is already set to Automatic Start" - } - My-Logger "Disabling SSH Warning..." - Set-MyESXiOption -Name "UserVars.SuppressShellWarning" -Value "1" - #endregion - - #region: Config NTP - My-Logger "Removing existing NTP Server..." - try { - $ESXiHost | Remove-VMHostNtpServer -NtpServer (Get-VMHostNtpServer) -Confirm:$false - } - catch [System.Exception] { - Write-Warning "Error during removing existing NTP Servers." - } - My-Logger "Setting new NTP Servers..." - foreach ($myNTP in $NTP) { - $ESXiHost | Add-VMHostNtpServer -ntpserver $myNTP -confirm:$False | Out-Null - } - - My-Logger "Configure NTP Service..." - $NTPService = $ESXiHost | Get-VMHostService| Where-Object {$_.key -eq "ntpd"} - if($NTPService.Running -eq $True){ - Stop-VMHostService -HostService $NTPService -Confirm:$false | Out-Null - } - if($NTPService.Policy -ne "on"){ - Set-VMHostService -HostService $NTPService -Policy "on" -confirm:$False | Out-Null - } - - My-Logger "Configure Local Time..." - $HostTimeSystem = Get-View $ESXiHost.ExtensionData.ConfigManager.DateTimeSystem - $HostTimeSystem.UpdateDateTime([DateTime]::UtcNow) - - My-Logger "Start NTP Service..." - Start-VMHostService -HostService $NTPService -confirm:$False | Out-Null - #endregion - - #region: Remove default PG - My-Logger "Checking for Default Port Group ..." - if ($defaultPG = $ESXiHost | Get-VirtualSwitch -Name vSwitch0 | Get-VirtualPortGroup -Name "VM Network" -ErrorAction SilentlyContinue ){ - Remove-VirtualPortGroup -VirtualPortGroup $defaultPG -confirm:$False | Out-Null - My-Logger " Default PG Removed" - } - else { - My-Logger " No Default PG found" - } - #endregion - - #region: Configure Static HighPower - My-Logger "Setting PowerProfile to Static HighPower..." - try { - $HostView = ($ESXiHost | Get-View) - (Get-View $HostView.ConfigManager.PowerSystem).ConfigurePowerPolicy(1) - } - catch [System.Exception] { - Write-Warning "Error during Configure Static HighPower. See latest errors..." - } - #endregion - - #region: Conf Syslog - My-Logger "Setting Syslog Firewall Rule ..." - $SyslogFW = ($ESXiHost | Get-VMHostFirewallException | where {$_.Name -eq 'syslog'}) - if ($SyslogFW.Enabled -eq $False ){ - $SyslogFW | Set-VMHostFirewallException -Enabled:$true -Confirm:$false | Out-Null - My-Logger " Syslog Firewall Rule enabled" - } - else { - My-Logger " Syslog Firewall Rule already enabled" - } - My-Logger "Setting Syslog Server..." - Set-MyESXiOption -Name "Syslog.global.logHost" -Value $syslog - #endregion - - #region: Change Disk Scheduler - My-Logger "Changing Disk Scheduler..." - Set-MyESXiOption -Name "Disk.SchedulerWithReservation" -Value "0" - #endregion - - #region: Configure HP 3PAR SATP/PSP Rule - My-Logger "Configure HP 3PAR SATP/PSP Rule" - $esxcli2 = Get-ESXCLI -VMHost $ESXiHost -V2 - $arguments = $esxcli2.storage.nmp.satp.rule.add.CreateArgs() - $arguments.satp = "VMW_SATP_ALUA" - $arguments.psp = "VMW_PSP_RR" - $arguments.pspoption = "iops=100" - $arguments.claimoption = "tpgs_on" - $arguments.vendor = "3PARdata" - $arguments.model = "VV" - $arguments.description = "HP 3PAR custom SATP Claimrule" - try { - $esxcli2.storage.nmp.satp.rule.add.Invoke($arguments) - } - catch { - Write-Warning "Error during Configure HP 3PAR SATP/PSP Rule. See latest errors..." - } - #endregion - - } - } -} diff --git a/Modules/PSvLIMessage.psm1 b/Modules/PSvLIMessage.psm1 deleted file mode 100644 index 9cab209..0000000 --- a/Modules/PSvLIMessage.psm1 +++ /dev/null @@ -1,123 +0,0 @@ -add-type @" - using System.Net; - using System.Security.Cryptography.X509Certificates; - public class TrustAllCertsPolicy : ICertificatePolicy { - public bool CheckValidationResult( - ServicePoint srvPoint, X509Certificate certificate, - WebRequest request, int certificateProblem) { - return true; - } - } -"@ -[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy - -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Organization: Private - Personal Blog: mycloudrevolution.com - Twitter: @vMarkus_K - =========================================================================== - Tested Against Environment: - vRealize Log Insight 3.3.1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - Keyword: vRealize, RestAPI - - Dependencies: - PowerCLI Version: PowerCLI 6.3 R1 - - .SYNOPSIS - Push Messages to VMware vRealize Log Insight. - - .DESCRIPTION - Creates a Messages in VMware vRealize Log Insight via the Ingestion API - - .EXAMPLE - Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" - - .EXAMPLE - Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" -Hostname MyTEST -FieldName myTest -FieldContent myTest - - .PARAMETER vLIServer - Specify the FQDN of your vRealize Log Insight Appliance - - .PARAMETER vLIAgentID - Specify the vRealize Log Insight Agent ID, e.g. "12862842-5A6D-679C-0E38-0E2BE888BB28" - - .PARAMETER Text - Specify the Event Text - - .PARAMETER Hostname - Specify the Hostanme displayed in vRealize Log Insight - - .PARAMETER FieldName - Specify the a Optional Field Name for vRealize Log Insight - - .PARAMETER FieldContent - Specify the a Optional FieldContent for the Field in -FieldName for vRealize Log Insight - If FielName is missing and FieldContent is given, it will be ignored - - #Requires PS -Version 3.0 - - #> -function Push-vLIMessage { - - [cmdletbinding()] - param ( - [parameter(Mandatory=$true)] - [string]$Text, - [parameter(Mandatory=$true)] - [string]$vLIServer, - [parameter(Mandatory=$true)] - [string]$vLIAgentID, - [parameter(Mandatory=$false)] - [string]$Hostname = $env:computername, - [parameter(Mandatory=$false)] - [string]$FieldName, - [parameter(Mandatory=$false)] - [string]$FieldContent = "" - ) - Process { - $Field_vLI = [ordered]@{ - name = "PS_vLIMessage" - content = "true" - } - $Field_HostName = [ordered]@{ - name = "hostname" - content = $Hostname - } - - $Fields = @($Field_vLI, $Field_HostName) - - if ($FieldName) { - $Field_Custom = [ordered]@{ - name = $FieldName - content = $FieldContent - } - $Fields += @($Field_Custom) - } - - $Restcall = @{ - messages = ([Object[]]([ordered]@{ - text = ($Text) - fields = ([Object[]]$Fields) - })) - } | convertto-json -Depth 4 - - $Resturl = ("http://" + $vLIServer + ":9000/api/v1/messages/ingest/" + $vLIAgentID) - try - { - $Response = Invoke-RestMethod $Resturl -Method Post -Body $Restcall -ContentType 'application/json' -ErrorAction stop - Write-Information "REST Call to Log Insight server successful" - Write-Verbose $Response - } - catch - { - Write-Error "REST Call failed to Log Insight server" - Write-Verbose $error[0] - Write-Verbose $Resturl - } - } -} \ No newline at end of file diff --git a/Modules/ProactiveHA.psm1 b/Modules/ProactiveHA.psm1 deleted file mode 100644 index ea4e92f..0000000 --- a/Modules/ProactiveHA.psm1 +++ /dev/null @@ -1,468 +0,0 @@ -Function New-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to register a new Proactive HA Provider with vCenter Server - .PARAMETER ProviderName - Name of ProactiveHA Provider - .PARAMETER ComponentType - Name of a supported ComponentType that ProactiveHA supports (Fan, Memory, Network, Power or Storage) - .PARAMETER ComponentDescription - Description of the health check for the given component - .PARAMETER ComponentId - Unique identifier for the given component within a ProactiveHA Provider - .EXAMPLE - New-PHAProvider -ProviderName "virtuallyGhetto" -ComponentType Power -ComponentDescription "Simulated ProactiveHA Provider" -ComponentId "Power" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderName, - [Parameter(Mandatory=$true)][ValidateSet("Fan","Memory","Network","Power","Storage")][String]$ComponentType, - [Parameter(Mandatory=$true)][String]$ComponentDescription, - [Parameter(Mandatory=$true)][String]$ComponentId - ) - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $healthInfo = [VMware.Vim.HealthUpdateInfo] @{ - ComponentType = $ComponentType - description = $ComponentDescription - Id = $ComponentId - } - - try { - Write-Host "`nRegistering new Proactive HA Provider $ProviderName ..." - $providerId = $healthManager.RegisterHealthUpdateProvider($ProviderName,$healthInfo) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } -} - -Function Get-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to return list of all Proactive HA Providers registered with vCenter Server - .EXAMPLE - Get-PHAProvider -#> - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $healthProviderResults = @() - $hpIDs = $healthManager.QueryProviderList() - - foreach ($hpID in $hpIDs) { - $hpName = $healthManager.QueryProviderName($hpID) - $hpConfig = $healthManager.QueryHealthUpdateInfos($hpID) - - $hp = [pscustomobject] @{ - ProviderName = $hpName - ProviderID = $hpID - ComponentType = $hpConfig.componentType - ComponentID = $hpConfig.id - Description = $hpConfig.description - } - $healthProviderResults+=$hp - } - $healthProviderResults -} - -Function Remove-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to remove a registered Proactive HA Provider from vCenter Server - .PARAMETER ProviderId - The ProactiveHA provider ID (retrieved from Get-PHAProvider) to unregister - .EXAMPLE - Remove-PHAProvider -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId - ) - - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - try { - Write-Host "`nUnregistering Proactive HA Provider $ProviderId ... " - $healthManager.UnregisterHealthUpdateProvider($providerId) - } catch { - if($Error[0].Exception.InnerException.MethodFault.getType().Name -eq "InvalidState") { - Write-host -ForegroundColor Red "The Proactive HA Provider is still in use, please disable it before unregistering" - } else { - Write-host -ForegroundColor Red $Error[0].Exception - } - } -} - -Function Set-PHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to enable/disable Proactive HA for vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to enable Proactive HA - .PARAMETER ProviderId - Proactive HA Provider ID to enable in vSphere Cluster - .PARAMETER ClusterMode - Whether Proactive HA should be "Automated" or "Manual" for actions it will take - .PARAMETER ModerateRemediation - Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Moderate issue is observed - .PARAMETER SevereRemediation - Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Severe issue is observed - .EXAMPLE - Set-PHAConfig -Cluster VSAN-Cluster -Enabled -ClusterMode Automated -ModerateRemediation QuarantineMode -SevereRemediation QuarantineMode -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" - .EXAMPLE - Set-PHAConfig -Cluster VSAN-Cluster -Disabled -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId, - [Parameter(Mandatory=$true)][String]$Cluster, - [Parameter(Mandatory=$false)][ValidateSet("Automated","Manual")]$ClusterMode="Manual", - [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$ModerateRemediation="QuarantineMode", - [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$SevereRemediation="QuarantineMode", - [Switch]$Enabled, - [Switch]$Disabled - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,Host,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - $vmhosts = $ClusterView.host - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - if($Enabled) { - try { - $entities = @() - foreach ($vmhost in $vmhosts) { - if(-not $healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { - $entities += $vmhost - } - } - - Write-Host "Enabling Proactive HA monitoring for all ESXi hosts in cluster ..." - $healthManager.AddMonitoredEntities($ProviderId,$entities) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - - try { - $healthProviders = @() - - # Make sure not to remove existing ProactiveHA providers - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers - foreach ($currentHP in $currentHPs) { - $healthProviders+=$currentHP - } - if(-not ($healthProviders -contains $ProviderID)) { - $healthProviders+=$ProviderId - } - } else { - $healthProviders+=$ProviderId - } - - $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ - enabled = $true - behavior = $ClusterMode - moderateRemediation = $ModerateRemediation - severeRemediation = $SevereRemediation - providers = $healthProviders - } - - $spec = [VMware.Vim.ClusterConfigSpecEx] @{ - infraUpdateHaConfig = $PHASpec - } - - Write-Host "Enabling Proactive HA Provider $ProviderId on $Cluster ..." - $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task | Out-Null - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - } - - if($Disabled) { - foreach ($vmhost in $vmhosts) { - if($vmhost.runtime.inQuarantineMode) { - Write-Host -ForegroundColor Red $vmhost.name " is currently still in Quaratine Mode, please remediate this before disabling Proactive HA" - break - } - } - - try { - $healthProviders = @() - - # Make sure not to remove existing ProactiveHA providers - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers - foreach ($currentHP in $currentHPs) { - if($currentHP -ne $ProviderId) { - $healthProviders+=$currentHP - } - } - } - - $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ - enabled = $true - behavior = $ClusterMode - moderateRemediation = $ModerateRemediation - severeRemediation = $SevereRemediation - providers = $healthProviders - } - - $spec = [VMware.Vim.ClusterConfigSpecEx] @{ - infraUpdateHaConfig = $PHASpec - } - - Write-Host "Disabling Proactive HA Provider $ProviderId on $Cluster ..." - $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task | Out-Null - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - - $ClusterView.UpdateViewData() - - try { - $entities = @() - foreach ($vmhost in $vmhosts) { - if($healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { - $entities += $vmhost - } - } - - Write-Host "Disabling Proactive HA monitoring for all ESXi hosts in cluster ..." - $healthManager.RemoveMonitoredEntities($ProviderId,$entities) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - } -} - -Function Get-PHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to retrieve Proactive HA configuration for a vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to check Proactive HA configuration - .EXAMPLE - Get-PHAConfig -Cluster VSAN-Cluster -#> - param( - [Parameter(Mandatory=$true)][String]$Cluster - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $phSettings = $ClusterView.ConfigurationEx.InfraUpdateHaConfig - $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers - $healthProviders = @() - foreach ($provider in $providers) { - $providerName = $healthManager.QueryProviderName($provider) - $healthProviders+=$providerName - } - - $pHAConfig = [pscustomobject] @{ - Enabled = $phSettings.Enabled - ClusterMode = $phSettings.behavior - ModerateRemediation = $phSettings.ModerateRemediation - SevereRemediation = $phSettings.SevereRemediation - HealthProviders = $healthProviders - } - $pHAConfig - } else { - Write-Host "Proactive HA has not been configured on this vSphere Cluster" - } -} - -Function Get-PHAHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to retrieve the Proactive HA health info for all ESXi hosts in vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to check Proactive HA health information - .EXAMPLE - Get-PHAHealth -Cluster VSAN-Cluster -#> - param( - [Parameter(Mandatory=$true)][String]$Cluster - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers - - foreach ($provider in $providers) { - $providerName = $healthManager.QueryProviderName($provider) - $healthUpdates = $healthManager.QueryHealthUpdates($provider) - - $healthResults = @() - Write-Host -NoNewline -ForegroundColor Magenta "Health summary for Proactive HA Provider $providerName`:`n" - foreach ($healthUpdate in $healthUpdates) { - $vmhost = Get-View $healthUpdate.Entity - - $hr = [PSCustomObject] @{ - Entity = $vmhost.name - Status = $healthUpdate.status - HealthComponentId = $healthUpdate.HealthUpdateInfoId - HealthUpdateId = $healthUpdate.Id - Remediation = $healthUpdate.Remediation - } - $healthResults+=$hr - } - $healthResults - } - } else { - Write-Host "Proactive HA has not been configured on this vSphere Cluster" - } -} - -Function New-PHASimulation { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to return VCHA Configuration - .PARAMETER ProviderId - The Proactive HA Provider ID that you like to simulate a health update from - .PARAMETER EsxiHost - The name of ESXi host to update the health on - .PARAMETER Component - The name of the matching component ID from Proactive HA Provider to simulate a health update from - .PARAMETER HealthStatus - The health value (green, yellow or red) for the given simulated health Update - .PARAMETER Remediation - The remediation message associated with simulated health update - .EXAMPLE - New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus green -Remediation "" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" - .EXAMPLE - New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus red -Remediation "Please replace my virtual PSU" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId, - [Parameter(Mandatory=$true)][String]$EsxiHost, - [Parameter(Mandatory=$true)][String]$Component, - [Parameter(Mandatory=$true)][ValidateSet("green","red","yellow")][String]$HealthStatus, - [Parameter(Mandatory=$false)][String]$Remediation - ) - - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $vmhost = Get-View -ViewType HostSystem -Property Name -Filter @{"name" = $EsxiHost} - - if($vmhost -eq $null) { - Write-Host -ForegroundColor Red "`nUnable to find ESXi host $EsxiHost ..." - break - } - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - # Randomly generating an ID for Health Update - # In general, you would want to generate a specific ID - # which can be referenced between ProactiveHA Provider - # and VMware logs for troubleshooting purposes - $HealthUpdateID = "vghetto-" + (Get-Random -Minimum 1 -Maximum 100000) - - # All other Health Status can have a remediation message - # but for green, it must be an empty string or API call will fail - if($HealthStatus -eq "green") { - $Remediation = "" - } - - $healthUpdate = [VMware.Vim.HealthUpdate] @{ - Entity = $vmhost.moref - HealthUpdateInfoId = $Component - Id = $HealthUpdateId - Status = $HealthStatus - Remediation = $Remediation - } - - try { - Write-Host "`nSimulating Proactive HA Health Update to ..." - Write-Host "`tHost: $EsxiHost " - Write-Host -NoNewline "`tStatus: " - Write-Host -ForegroundColor $HealthStatus "$HealthStatus" - Write-Host "`tRemediation Messsage: $Remediation" - $healthManager.PostHealthUpdates($providerId,$healthUpdate) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } -} \ No newline at end of file diff --git a/Modules/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing.psm1 deleted file mode 100644 index 0075e43..0000000 --- a/Modules/Recommend-Sizing.psm1 +++ /dev/null @@ -1,227 +0,0 @@ -function Recommend-Sizing { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 1.0 Base Release - 2016.11 ver 1.1 Optional Stats Collection - 2016.11 ver 1.2 VM Stats from Realtime Data and new Counters - =========================================================================== - 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, 6.0 - 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 Recommandation. Focus is in Compute Ressources. - - .Example - Recommend-Sizing -ClusterNames Cluster01, Cluster02 -Stats -StatsRange 60 -Verbose - - .Example - Recommend-Sizing -ClusterNames Cluster01, Cluster02 - - .Example - Recommend-Sizing -ClusterNames Cluster01 - - .PARAMETER ClusterNames - List of your vSphere Cluser Names to process. - - .PARAMETER Stats - Enables Stats Collection. - - Warning: At the moment this is only fully tested with vSphere 5.5 and vSphere 6.5! - - .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, ParameterSetName = "Stats")] - [switch] $Stats, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2, ParameterSetName = "Stats")] - [int] $StatsRange = 1440 - -) -Begin { - if ($Stats) { - Write-Warning "Stats Collection requested.`nAt the moment this is only fully tested with vSphere 5.5 and vSphere 6.5" - [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 - - if ($Stats) { - #region: Creating VM Stats - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats..." - $VMMetrics = "disk.numberwrite.summation","disk.numberread.summation","cpu.usage.average", "mem.usage.average" - $Start = (Get-Date).AddMinutes($TimeRange) - $VMStats = Get-Stat -Realtime -Stat $VMMetrics -Entity $ClusterVMsPoweredOn -Start $Start -Verbose:$False - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats completed" - #endregion - - #region: Creating VM Stats Report - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report..." - $ReportVMPerf = @() - $ReportVMPerf = $VMStats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - IOPSWriteAvg = ($_.Group | ` - where{$_.MetricId -eq "disk.numberwrite.summation"} | ` - Measure-Object -Property Value -Average).Average - IOPSReadAvg = ($_.Group | ` - where{$_.MetricId -eq "disk.numberread.summation"} | ` - Measure-Object -Property Value -Average).Average - CPUUsageAvg = ($_.Group | ` - where{$_.MetricId -eq "cpu.usage.average"} | ` - Measure-Object -Property Value -Average).Average - MEMUsageAvg = ($_.Group | ` - where{$_.MetricId -eq "mem.usage.average"} | ` - Measure-Object -Property Value -Average).Average - } - } - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report completed" - #endregion - } - else { - Write-Verbose "$(Get-Date -Format G) Stats Collection skipped..." - } - - #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 %" - 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 ) - AverageVMIOPSWriteAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSWriteAvg).Average,1 ) - AverageVMIOPSReadAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSReadAvg).Average,1 ) - AverageVMCPUUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property CPUUsageAvg).Average,1 )) %" - AverageVMMEMUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property MEMUsageAvg).Average,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 diff --git a/Modules/Set-CBT.psm1 b/Modules/Set-CBT.psm1 deleted file mode 100644 index 784aebb..0000000 --- a/Modules/Set-CBT.psm1 +++ /dev/null @@ -1,111 +0,0 @@ -function Set-CBT { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 1.0 Base Release - =========================================================================== - External Code Sources: - http://wahlnetwork.com/2015/12/01/change-block-tracking-cbt-powercli/ - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2 - PowerCLI Version: PowerCLI 6.3 R1 - PowerShell Version: 4.0 - OS Version: Windows Server 2012 R2 - =========================================================================== - Keywords vSphere, ESXi, VM, Storage, CBT, Backup - =========================================================================== - - .DESCRIPTION - This Function enables or disables CBT. - - .Example - Get-VN TST* | Set-CBT -DisableCBT - - .Example - Get-VN TST* | Set-CBT -EnableCBT - - .PARAMETER DisableCBT - Disables CBT for any VMs found with it enabled - - .PARAMETER EnableCBT - Enables CBT for any VMs found with it disabled - -#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=$True, Position=0, HelpMessage = "VMs to process")] - [ValidateNotNullorEmpty()] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $myVMs, - [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Enables CBT for any VMs found with it disabled", ParameterSetName = "EnableCBT")] - [ValidateNotNullorEmpty()] - [Switch]$EnableCBT, - [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Disables CBT for any VMs found with it enabled", ParameterSetName = "DisableCBT")] - [ValidateNotNullorEmpty()] - [Switch]$DisableCBT - ) -Process { - - $vmconfigspec = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec - Write-Verbose -Message "Walking through given VMs" - foreach($myVM in $myVMs) - { - if ($DisableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $true -and $myVM.ExtensionData.Snapshot -eq $null) - { - try - { - Write-Verbose -Message "Reconfiguring $($myVM.name) to disable CBT" -Verbose - $vmconfigspec.ChangeTrackingEnabled = $false - $myVM.ExtensionData.ReconfigVM($vmconfigspec) - - if ($myVM.PowerState -eq "PoweredOn" ) { - Write-Verbose -Message "Creating a snapshot on $($myVM.name) to clear CBT file" -Verbose - $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" - - Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose - $SnapShot| Remove-Snapshot -Confirm:$false - } - - } - catch - { - throw $myVM - } - } - elseif ($EnableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $false -and $myVM.ExtensionData.Snapshot -eq $null) - { - Write-Verbose -Message "Reconfiguring $($myVM.name) to enable CBT" -Verbose - $vmconfigspec.ChangeTrackingEnabled = $true - $myVM.ExtensionData.ReconfigVM($vmconfigspec) - - if ($myVM.PowerState -eq "PoweredOn" ) { - Write-Verbose -Message "Creating a snapshot on $($myVM.name) to Create CBT file" -Verbose - $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" - - Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose - $SnapShot | Remove-Snapshot -Confirm:$false - } - } - else - { - if ($myVM.ExtensionData.Snapshot -ne $null -and $EnableCBT) - { - Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" - } - elseif ($myVM.ExtensionData.Snapshot -ne $null -and $DisableCBT) - { - Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" - } - } - } - - } -} diff --git a/Modules/Start-UNMAP.psm1 b/Modules/Start-UNMAP.psm1 deleted file mode 100644 index a8e9896..0000000 --- a/Modules/Start-UNMAP.psm1 +++ /dev/null @@ -1,99 +0,0 @@ -function Start-UNMAP { -<# - .SYNOPSIS - Process SCSI UNMAP on VMware Datastores - - .DESCRIPTION - This Function will process SCSI UNMAP on VMware Datastores via ESXCLI -V2 - - .Example - Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* - - .Example - Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* -Verbose -WhatIf - - .Notes - NAME: Start-UNMAP.psm1 - AUTHOR: Markus Kraus - LASTEDIT: 23.09.2016 - VERSION: 1.0 - KEYWORDS: VMware, vSphere, ESXi, SCSI, VAAI, UNMAP - - .Link - http://mycloudrevolution.com/ - - #Requires PS -Version 4.0 - #Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} - #> - - [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact='High')] - param( - [Parameter(Mandatory=$true, Position=0)] - [String]$ClusterName, - [Parameter(Mandatory=$true, Position=1)] - [String]$DSWildcard - ) - Process { - $Validate = $true - #region: PowerCLI Session Timeout - Write-Verbose "Set Session Timeout ..." - $initialTimeout = (Get-PowerCLIConfiguration -Scope Session).WebOperationTimeoutSeconds - Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds -1 -Confirm:$False | Out-Null - #endregion - - #region: Get Cluster - $Cluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue - Write-Verbose "vSphere Cluster: $Cluster" - if (!$Cluster){Write-Error "No Cluster found!"; $Validate = $false} - #endregion - - #region: Get Hosts - $ClusterHosts = $Cluster | Get-VMHost -ErrorAction SilentlyContinue | where {$_.ConnectionState -eq "Connected" -and $_.PowerState -eq "PoweredOn"} - Write-Verbose "vSphere Cluster Hosts: $ClusterHosts" - if (!$ClusterHosts){Write-Error "No Hosts found!"; $Validate = $false} - #endregion - - #region: Get Datastores - $ClusterDataStores = $Cluster | Get-Datastore -ErrorAction SilentlyContinue | where {$_.Name -like $DSWildcard -and $_.State -eq "Available" -and $_.Accessible -eq "True"} - Write-Verbose "vSphere Cluster Datastores: $ClusterDataStores" - if (!$ClusterDataStores){Write-Error "No Datastores found!"; $Validate = $false} - #endregion - - #region: Process Datastores - if ($Validate -eq $true) { - Write-Verbose "Starting Loop..." - foreach ($ClusterDataStore in $ClusterDataStores) { - Write-Verbose "vSphere Datastore to Process: $ClusterDataStore" - $myHost = $ClusterHosts[(Get-Random -Maximum ($ClusterHosts).count)] - Write-Verbose "vSphere Host to Process: $myHost" - $esxcli2 = $myHost | Get-ESXCLI -V2 - $arguments = $esxcli2.storage.vmfs.unmap.CreateArgs() - $arguments.volumelabel = $ClusterDataStore - $arguments.reclaimunit = "256" - if ($PSCmdlet.ShouldProcess( $ClusterDataStore,"Starting UNMAP on $myHost")) { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - try { - Write-Output "Starting UNMAP for $ClusterDataStore on $myHost..." - $esxcli2.storage.vmfs.unmap.Invoke($arguments) - } - catch { - Write-Output "A Error occured: " "" $error[0] "" - } - $stopwatch.Stop() - Write-Output "UNMAP duration: $($stopwatch.Elapsed.Minutes)" - } - - } - } - else { - Write-Error "Validation Failed. Processing Loop Skipped!" - } - #endregion - - #region: Revert PowerCLI Session Timeout - Write-Verbose "Revert Session Timeout ..." - Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds $initialTimeout -Confirm:$False | Out-Null - #endregion - } - -} diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 deleted file mode 100755 index 92c5d5f..0000000 --- a/Modules/VAMI.psm1 +++ /dev/null @@ -1,716 +0,0 @@ -Function Get-VAMISummary { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves some basic information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return basic VAMI summary info - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMISummary -#> - $systemVersionAPI = Get-CisService -Name 'com.vmware.appliance.system.version' - $results = $systemVersionAPI.get() | select product, type, version, build, install_time - - $systemUptimeAPI = Get-CisService -Name 'com.vmware.appliance.system.uptime' - $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) - $uptime = $ts.ToString("hh\:mm\:ss\,fff") - - $summaryResult = [pscustomobject] @{ - Product = $results.product; - Type = $results.type; - Version = $results.version; - Build = $results.build; - InstallTime = $results.install_time; - Uptime = $uptime - } - $summaryResult -} - -Function Get-VAMIHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves health information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VAMI health - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIHealth -#> - $healthOverall = (Get-CisService -Name 'com.vmware.appliance.health.system').get() - $healthLastCheck = (Get-CisService -Name 'com.vmware.appliance.health.system').lastcheck() - $healthCPU = (Get-CisService -Name 'com.vmware.appliance.health.load').get() - $healthMem = (Get-CisService -Name 'com.vmware.appliance.health.mem').get() - $healthSwap = (Get-CisService -Name 'com.vmware.appliance.health.swap').get() - $healthStorage = (Get-CisService -Name 'com.vmware.appliance.health.storage').get() - - # DB health only applicable for Embedded/External VCSA Node - $vami = (Get-CisService -Name 'com.vmware.appliance.system.version').get() - - if($vami.type -eq "vCenter Server with an embedded Platform Services Controller" -or $vami.type -eq "vCenter Server with an external Platform Services Controller") { - $healthVCDB = (Get-CisService -Name 'com.vmware.appliance.health.databasestorage').get() - } else { - $healthVCDB = "N/A" - } - $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() - - $healthResult = [pscustomobject] @{ - HealthOverall = $healthOverall; - HealthLastCheck = $healthLastCheck; - HealthCPU = $healthCPU; - HealthMem = $healthMem; - HealthSwap = $healthSwap; - HealthStorage = $healthStorage; - HealthVCDB = $healthVCDB; - HealthSoftware = $healthSoftwareUpdates - } - $healthResult -} - -Function Get-VAMIAccess { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves access information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIAccess -#> - $consoleAccess = (Get-CisService -Name 'com.vmware.appliance.access.consolecli').get() - $dcuiAccess = (Get-CisService -Name 'com.vmware.appliance.access.dcui').get() - $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() - $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() - - $accessResult = New-Object PSObject -Property @{ - Console = $consoleAccess; - DCUI = $dcuiAccess; - BashShell = $shellAccess.enabled; - SSH = $sshAccess - } - $accessResult -} - -Function Get-VAMITime { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the time and NTP info from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return current Time and NTP information - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMITime -#> - $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' - $timeResults = $systemTimeAPI.get() - - $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() - $timeSyncMode = $timeSync.mode - - $timeResult = [pscustomobject] @{ - Timezone = $timeResults.timezone; - Date = $timeResults.date; - CurrentTime = $timeResults.time; - Mode = $timeSyncMode; - NTPServers = "N/A"; - NTPStatus = "N/A"; - } - - if($timeSyncMode -eq "NTP") { - $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() - $timeResult.NTPServers = $ntpServers.servers - $timeResult.NTPStatus = $ntpServers.status - } - $timeResult -} - -Function Get-VAMINetwork { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves network information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return networking information including details for each interface - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMINetwork -#> - $netResults = @() - - $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() - $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() - - Write-Host "Hostname: " $hostname - Write-Host "DNS Servers: " $dns.servers - - $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() - foreach ($interface in $interfaces) { - $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') - $spec = $ipv4API.Help.get.interfaces.CreateExample() - $spec+= $interface.name - $ipv4result = $ipv4API.get($spec) - - $interfaceResult = [pscustomobject] @{ - Inteface = $interface.name; - MAC = $interface.mac; - Status = $interface.status; - Mode = $ipv4result.mode; - IP = $ipv4result.address; - Prefix = $ipv4result.prefix; - Gateway = $ipv4result.default_gateway; - Updateable = $ipv4result.updateable - } - $netResults += $interfaceResult - } - $netResults -} - -Function Get-VAMIDisks { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves VMDK disk number to partition mapping VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VMDK disk number to OS partition mapping - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIDisks -#> - $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' - $disks = $storageAPI.list() - - foreach ($disk in $disks | sort {[int]$_.disk.toString()}) { - $disk | Select Disk, Partition - } -} - -Function Start-VAMIDiskResize { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function triggers an OS partition resize after adding additional disk capacity - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function triggers OS partition resize operation - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Start-VAMIDiskResize -#> - $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' - Write-Host "Initiated OS partition resize operation ..." - $storageAPI.resize() -} - -Function Get-VAMIStatsList { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list avialable monitoring metrics in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of available monitoring metrics that can be queried - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIStatsList -#> - $monitoringAPI = Get-CisService -Name 'com.vmware.appliance.monitoring' - $ids = $monitoringAPI.list() | Select id | Sort-Object -Property id - - foreach ($id in $ids) { - $id - } -} - -Function Get-VAMIStorageUsed { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the individaul OS partition storage utilization - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return individual OS partition storage utilization - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIStorageUsed -#> - $monitoringAPI = Get-CisService 'com.vmware.appliance.monitoring' - $querySpec = $monitoringAPI.help.query.item.CreateExample() - - # List of IDs from Get-VAMIStatsList to query - $querySpec.Names = @( - "storage.used.filesystem.autodeploy", - "storage.used.filesystem.boot", - "storage.used.filesystem.coredump", - "storage.used.filesystem.imagebuilder", - "storage.used.filesystem.invsvc", - "storage.used.filesystem.log", - "storage.used.filesystem.netdump", - "storage.used.filesystem.root", - "storage.used.filesystem.updatemgr", - "storage.used.filesystem.vcdb_core_inventory", - "storage.used.filesystem.vcdb_seat", - "storage.used.filesystem.vcdb_transaction_log", - "storage.totalsize.filesystem.autodeploy", - "storage.totalsize.filesystem.boot", - "storage.totalsize.filesystem.coredump", - "storage.totalsize.filesystem.imagebuilder", - "storage.totalsize.filesystem.invsvc", - "storage.totalsize.filesystem.log", - "storage.totalsize.filesystem.netdump", - "storage.totalsize.filesystem.root", - "storage.totalsize.filesystem.updatemgr", - "storage.totalsize.filesystem.vcdb_core_inventory", - "storage.totalsize.filesystem.vcdb_seat", - "storage.totalsize.filesystem.vcdb_transaction_log" - ) - - # Tuple (Filesystem Name, Used, Total) to store results - $storageStats = @{ - "autodeploy"=@{"name"="/storage/autodeploy";"used"=0;"total"=0}; - "boot"=@{"name"="/boot";"used"=0;"total"=0}; - "coredump"=@{"name"="/storage/core";"used"=0;"total"=0}; - "imagebuilder"=@{"name"="/storage/imagebuilder";"used"=0;"total"=0}; - "invsvc"=@{"name"="/storage/invsvc";"used"=0;"total"=0}; - "log"=@{"name"="/storage/log";"used"=0;"total"=0}; - "netdump"=@{"name"="/storage/netdump";"used"=0;"total"=0}; - "root"=@{"name"="/";"used"=0;"total"=0}; - "updatemgr"=@{"name"="/storage/updatemgr";"used"=0;"total"=0}; - "vcdb_core_inventory"=@{"name"="/storage/db";"used"=0;"total"=0}; - "vcdb_seat"=@{"name"="/storage/seat";"used"=0;"total"=0}; - "vcdb_transaction_log"=@{"name"="/storage/dblog";"used"=0;"total"=0} - } - - $querySpec.interval = "DAY1" - $querySpec.function = "MAX" - $querySpec.start_time = ((get-date).AddDays(-1)) - $querySpec.end_time = (Get-Date) - $queryResults = $monitoringAPI.query($querySpec) | Select * -ExcludeProperty Help - - foreach ($queryResult in $queryResults) { - # Update hash if its used storage results - if($queryResult.name -match "used") { - $key = (($queryResult.name).toString()).split(".")[-1] - $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) - $storageStats[$key]["used"] = $value - # Update hash if its total storage results - } else { - $key = (($queryResult.name).toString()).split(".")[-1] - $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) - $storageStats[$key]["total"] = $value - } - } - - $storageResults = @() - foreach ($key in $storageStats.keys | Sort-Object -Property name) { - $statResult = [pscustomobject] @{ - Filesystem = $storageStats[$key].name; - Used = $storageStats[$key].used; - Total = $storageStats[$key].total - } - $storageResults += $statResult - } - $storageResults -} - -Function Get-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIService - .EXAMPLE - Get-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$false, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - if($Name -ne "") { - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - $serviceStatus = $vMonAPI.get($name,0) - $serviceString = [pscustomobject] @{ - Name = $name; - State = $serviceStatus.state; - Health = ""; - Startup = $serviceStatus.startup_type - } - if($serviceStatus.health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $serviceStatus.health } - $serviceString - } catch { - Write-Error $Error[0].exception.Message - } - } else { - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - $services = $vMonAPI.list_details() - - $serviceResult = @() - foreach ($key in $services.keys | Sort-Object -Property Value) { - $serviceString = [pscustomobject] @{ - Name = $key; - State = $services[$key].state; - Health = "N/A"; - Startup = $services[$key].Startup_type - } - if($services[$key].health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $services[$key].health } - - $serviceResult += $serviceString - } - $serviceResult - } -} - -Function Start-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Start-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - Write-Host "Starting $name service ..." - $vMonAPI.start($name) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Stop-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Stop-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - Write-Host "Stopping $name service ..." - $vMonAPI.stop($name) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Get-VAMIBackupSize { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the backup size of the VCSA from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return the current backup size of the VCSA (common and core data) - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIBackupSize -#> - $recoveryAPI = Get-CisService 'com.vmware.appliance.recovery.backup.parts' - $backupParts = $recoveryAPI.list() | select id - - $estimateBackupSize = 0 - $backupPartSizes = "" - foreach ($backupPart in $backupParts) { - $partId = $backupPart.id.value - $partSize = $recoveryAPI.get($partId) - $estimateBackupSize += $partSize - $backupPartSizes += $partId + " data is " + $partSize + " MB`n" - } - - Write-Host "Estimated Backup Size: $estimateBackupSize MB" - Write-Host $backupPartSizes -} - -Function Get-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves VAMI local users using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to retrieve VAMI local users - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIUser -#> - param( - [Parameter( - Mandatory=$false, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - - $userResults = @() - - if($Name -ne "") { - try { - $user = $userAPI.get($name) - - $userString = [pscustomobject] @{ - User = $user.username - Name = $user.fullname - Email = $user.email - Status = $user.status - PasswordStatus = $user.passwordstatus - Role = $user.role - } - $userResults += $userString - } catch { - Write-Error $Error[0].exception.Message - } - } else { - $users = $userAPI.list() - - foreach ($user in $users) { - $userString = [pscustomobject] @{ - User = $user.username - Name = $user.fullname - Email = $user.email - Status = $user.status - PasswordStatus = $user.passwordstatus - Role = $user.role - } - $userResults += $userString - } - } - $userResults -} - -Function New-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function to create new VAMI local user using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to create a new VAMI local user - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - New-VAMIUser -name lamw -fullname "William Lam" -role "operator" -email "lamw@virtuallyghetto.com" -password "VMware1!" -#> - param( - [Parameter( - Mandatory=$true) - ] - [String]$name, - [Parameter( - Mandatory=$true) - ] - [String]$fullname, - [Parameter( - Mandatory=$true) - ] - [ValidateSet("admin","operator","superAdmin")][String]$role, - [Parameter( - Mandatory=$false) - ] - [String]$email="", - [Parameter( - Mandatory=$true) - ] - [String]$password - ) - - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - $createSpec = $userAPI.Help.add.config.CreateExample() - - $createSpec.username = $name - $createSpec.fullname = $fullname - $createSpec.role = $role - $createSpec.email = $email - $createSpec.password = [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$password - - try { - Write-Host "Creating new user $name ..." - $userAPI.add($createSpec) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Remove-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function to remove VAMI local user using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to remove VAMI local user - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIAccess -#> - param( - [Parameter( - Mandatory=$true) - ] - [String]$name, - [Parameter( - Mandatory=$false) - ] - [boolean]$confirm=$false - ) - - if(!$confirm) { - $answer = Read-Host -Prompt "Do you want to delete user $name (Y or N)" - if($answer -eq "Y" -or $answer -eq "y") { - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - - try { - Write-Host "Deleting user $name ..." - $userAPI.delete($name) - } catch { - Write-Error $Error[0].exception.Message - } - } - } -} \ No newline at end of file diff --git a/Modules/VCHA.psm1 b/Modules/VCHA.psm1 deleted file mode 100644 index 160f0e7..0000000 --- a/Modules/VCHA.psm1 +++ /dev/null @@ -1,413 +0,0 @@ -Function Get-VCHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the VCHA Configuration which provides you with - the current state, mode as well as the IP Addresses of the Active, - Passive & Witness Node. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to return VCHA Configuration - .EXAMPLE - Get-VCHAConfig -#> - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - - $vcHAState = $vcHAConfig.State - switch($vcHAState) { - configured { - $activeIp = $vcHAConfig.FailoverNodeInfo1.ClusterIpSettings.Ip.IpAddress - $passiveIp = $vcHAConfig.FailoverNodeInfo2.ClusterIpSettings.Ip.IpAddress - $witnessIp = $vcHAConfig.WitnessNodeInfo.IpSettings.Ip.IpAddress - - $vcHAClusterManager = Get-View failoverClusterManager - $vcHAMode = $vcHAClusterManager.getClusterMode() - - Write-Host "" - Write-Host -NoNewline -ForegroundColor Green "VCHA State: " - Write-Host -ForegroundColor White "$vcHAState" - Write-Host -NoNewline -ForegroundColor Green " VCHA Mode: " - Write-Host -ForegroundColor White "$vcHAMode" - Write-Host -NoNewline -ForegroundColor Green " ActiveIP: " - Write-Host -ForegroundColor White "$activeIp" - Write-Host -NoNewline -ForegroundColor Green " PassiveIP: " - Write-Host -ForegroundColor White "$passiveIp" - Write-Host -NoNewline -ForegroundColor Green " WitnessIP: " - Write-Host -ForegroundColor White "$witnessIp`n" - ;break - } - invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} - notConfigured { Write-Host "VCHA is not configured";break} - prepared { Write-Host "VCHA is being prepared, please try again in a little bit ...";break} - } -} - -Function Get-VCHAClusterHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the VCHA Cluster Health which provides more info - on each of the individual. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to return VCHA Cluster Health - .EXAMPLE - Get-VCHAClusterHealth -#> - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - $vcHAState = $vcHAConfig.State - - switch($vcHAState) { - invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} - notConfigured { Write-Host "VCHA is not configured";break} - prepared { Write-Host "VCHA is being prepared ...";break} - configured { - $vcHAClusterManager = Get-View failoverClusterManager - $healthInfo = $vcHAClusterManager.GetVchaClusterHealth() - - $vcClusterState = $healthInfo.RuntimeInfo.ClusterState - $nodeState = $healthInfo.RuntimeInfo.NodeInfo - - Write-Host "" - Write-Host -NoNewline -ForegroundColor Green "VCHA Cluster State: " - Write-Host -ForegroundColor White "$vcClusterState" - Write-Host -NoNewline -ForegroundColor Green "VCHA Node Information: " - $nodeState | Select NodeIp, NodeRole, NodeState - ;break - } - } -} - -Function Set-VCHAClusterMode { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you to set the mode of the VCHA Cluster whether - that is Enabled, Disabled or in Maintenance Mode. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to set VCHA Cluster Mode - .EXAMPLE - Set-VCHAClusterMode -Enabled $true - .EXAMPLE - Set-VCHAClusterMode -Disabled $true - .EXAMPLE - Set-VCHAClusterMode -Maintenance $true -#> - param( - [Switch]$Enabled, - [Switch]$Disabled, - [Switch]$Maintenance - ) - - $vcHAClusterManager = Get-View failoverClusterManager - - if($Enabled) { - Write-Host "Setting VCHA Cluster to Enabled ..." - $task = $vcHAClusterManager.setClusterMode_Task("enabled") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } elseIf($Maintenance) { - Write-Host "Setting VCHA Cluster to Maintenance ..." - $task = $vcHAClusterManager.setClusterMode_Task("maintenance") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } elseIf($Disabled) { - Write-Host "`nSetting VCHA Cluster to Disabled ...`n" - $task = $vcHAClusterManager.setClusterMode_Task("disabled") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } -} - -Function New-VCHABasicConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you create a new "Basic" VCHA Cluster, it does not - cover the "Advanced" use case. You will need to ensure that you have a - "Self Managed" vCenter Server before attempting this workflow. - This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to create "Basic" VCHA Cluster - .PARAMETER VCSAVM - The name of the vCenter Server Appliance (VCSA) in which you wish to enable VCHA on (must be self-managed) - .PARAMETER HANetwork - The name of the Virtual Portgroup or Distributed Portgroup used for the HA Network - .PARAMETER ActiveHAIp - The IP Address for the Active VCSA node - .PARAMETER ActiveNetmask - The Netmask for the Active VCSA node - .PARAMETER PassiveHAIp - The IP Address for the Passive VCSA node - .PARAMETER PassiveNetmask - The Netmask for the Passive VCSA node - .PARAMETER WitnessHAIp - The IP Address for the Witness VCSA node - .PARAMETER WitnessNetmask - The Netmask for the Witness VCSA node - .PARAMETER PassiveDatastore - The name of the datastore to deploy the Passive node to - .PARAMETER WitnessDatastore - The name of the datastore to deploy the Witness node to - .PARAMETER VCUsername - The VCSA username (e.g. administrator@vghetto.local) - .PARAMETER VCPassword - The VCSA password - .EXAMPLE - New-VCHABasicConfig -VCSAVM "vcenter65-1" -HANetwork "DVPG-VCHA-Network" ` - -ActiveHAIp 192.168.1.70 ` - -ActiveNetmask 255.255.255.0 ` - -PassiveHAIp 192.168.1.71 ` - -PassiveNetmask 255.255.255.0 ` - -WitnessHAIp 192.168.1.72 ` - -WitnessNetmask 255.255.255.0 ` - -PassiveDatastore "vsanDatastore" ` - -WitnessDatastore "vsanDatastore" ` - -VCUsername "administrator@vghetto.local" ` - -VCPassword "VMware1!" -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$VCSAVM, - [String]$HANetwork, - [String]$ActiveHAIp, - [String]$ActiveNetmask, - [String]$PassiveHAIp, - [String]$PassiveNetmask, - [String]$PassiveDatastore, - [String]$WitnessHAIp, - [String]$WitnessNetmask, - [String]$WitnessDatastore, - # Crappy Implementation but need to research more into using PSH Credential - [String]$VCUsername, - [String]$VCPassword - ) - - $VCSAVMView = Get-View -ViewType VirtualMachine -Filter @{"name"=$VCSAVM} - if($VCSAVMView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Virtual Machine $VCSAVM" - return - } - - $HANetworkView = Get-View -ViewType Network -Filter @{"name"=$HANetwork} - if($HANetworkView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Network $HANetwork" - return - } - - $PassiveDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$PassiveDatastore} - if($PassiveDatastoreView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Passive Datastore $PassiveDatastore" - return - } - - $WitnessDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$WitnessDatastore} - if($WitnessDatastoreView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Witness Datastore $WitnessDatastore" - return - } - - $vcIP = $VCSAVMView.Guest.IpAddress - if($vcIP -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to automatically retrieve the IP Address of $VCSAVM which is needed to use this function" - return - } - - # Retrieve Source VC SSL Thumbprint - $vcurl = "https://$vcIP" -add-type @" - using System.Net; - using System.Security.Cryptography.X509Certificates; - - public class IDontCarePolicy : ICertificatePolicy { - public IDontCarePolicy() {} - public bool CheckValidationResult( - ServicePoint sPoint, X509Certificate cert, - WebRequest wRequest, int certProb) { - return true; - } - } -"@ - [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy - # Need to do simple GET connection for this method to work - Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null - - $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") - # Get Thumbprint + add colons for a valid Thumbprint - $vcSSLThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $spec = New-Object VMware.Vim.VchaClusterDeploymentSpec - - $activeNetworkConfig = New-Object VMware.Vim.ClusterNetworkConfigSpec - $activeNetworkConfig.NetworkPortGroup = $HANetworkView.MoRef - $ipSettings = New-Object Vmware.Vim.CustomizationIPSettings - $ipSettings.SubnetMask = $ActiveNetmask - $activeIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $activeIpSpec.IpAddress = $ActiveHAIp - $ipSettings.Ip = $activeIpSpec - $activeNetworkConfig.IpSettings = $ipSettings - $spec.ActiveVcNetworkConfig = $activeNetworkConfig - - $activeVCConfig = New-Object Vmware.Vim.SourceNodeSpec - $activeVCConfig.ActiveVc = $VCSAVMView.MoRef - $serviceLocator = New-Object Vmware.Vim.ServiceLocator - $credential = New-Object VMware.Vim.ServiceLocatorNamePassword - $credential.username = $VCUsername - $credential.password = $VCPassword - $serviceLocator.Credential = $credential - $serviceLocator.InstanceUuid = $global:DefaultVIServer.InstanceUuid - $serviceLocator.Url = $vcurl - $serviceLocator.SslThumbprint = $vcSSLThumbprint - $activeVCConfig.ManagementVc = $serviceLocator - $spec.ActiveVcSpec = $activeVCConfig - - $passiveSpec = New-Object VMware.Vim.PassiveNodeDeploymentSpec - $passiveSpec.Folder = (Get-View (Get-Folder vm)).MoRef - $passiveIpSettings = New-object Vmware.Vim.CustomizationIPSettings - $passiveIpSettings.SubnetMask = $passiveNetmask - $passiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $passiveIpSpec.IpAddress = $passiveHAIp - $passiveIpSettings.Ip = $passiveIpSpec - $passiveSpec.IpSettings = $passiveIpSettings - $passiveSpec.NodeName = $VCSAVMView.Name + "-Passive" - $passiveSpec.datastore = $PassiveDatastoreView.MoRef - $spec.PassiveDeploymentSpec = $passiveSpec - - $witnessSpec = New-Object VMware.Vim.NodeDeploymentSpec - $witnessSpec.Folder = (Get-View (Get-Folder vm)).MoRef - $witnessSpec.NodeName = $VCSAVMView.Name + "-Witness" - $witnessIpSettings = New-object Vmware.Vim.CustomizationIPSettings - $witnessIpSettings.SubnetMask = $witnessNetmask - $witnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $witnessIpSpec.IpAddress = $witnessHAIp - $witnessIpSettings.Ip = $witnessIpSpec - $witnessSpec.IpSettings = $witnessIpSettings - $witnessSpec.datastore = $WitnessDatastoreView.MoRef - $spec.WitnessDeploymentSpec = $witnessSpec - - Write-Host "`nDeploying VCHA Cluster ...`n" - $task = $vcHAClusterConfig.deployVcha_Task($spec) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task -Verbose -} - -Function Remove-VCHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you destroy a VCHA Cluster. In addition, you have - the option to specify whether you would like both the Passive & Witness - Virtual Machines be deleted after the VCHA Cluster has been destroyed. - This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to destroy a VCHA Cluster Mode - .EXAMPLE - Remove-VCHAConfig - .EXAMPLE - Remove-VCHAConfig -Confirm:$false - .EXAMPLE - Remove-VCHAConfig -DeleteVM $true -Confirm:$false - .NOTES - Before you can destroy a VCHA Cluster, you must make sure it is first - disabled. Run the Set-VCHAClusterMode -Disabled $true to do so -#> - param( - [Boolean]$Confirm=$true, - [Switch]$DeleteVM=$false - ) - - $Verified = $false - if($Confirm -eq $true) { - Write-Host -ForegroundColor Yellow "`nDo you want to destroy VCHA Cluster?" - $answer = Read-Host -Prompt "Do you accept (Y or N)" - if($answer -eq "Y" -or $answer -eq "y") { - $Verified = $true - } - } else { - $Verified = $true - } - - if($Verified) { - $vcHAClusterManager = Get-View failoverClusterManager - $vcHAMode = $vcHAClusterManager.getClusterMode() - - if($vcHAMode -ne "disabled") { - Write-Host -ForegroundColor Yellow "To destroy VCHA Cluster, you must first set the VCHA Cluster Mode to `"Disabled`"" - Exit - } - - # Query BIOS UUID of the Passive/Witness to be able to delete - if($DeleteVM) { - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - $passiveBiosUUID = $vcHAConfig.FailoverNodeInfo2.biosUuid - $witnessBiosUUID = $vcHAConfig.WitnessNodeInfo.biosUuid - } - - $vcHAClusterConfig = Get-View failoverClusterConfigurator - - Write-Host "Destroying VCHA Cluster ..." - $task = $vcHAClusterConfig.destroyVcha_Task() - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - - # After VCHA Cluster has been destroyed, we can now delete the VMs we had queried earlier - if($DeleteVM) { - if($passiveBiosUUID -ne $null -and $witnessBiosUUID -ne $null) { - $searchIndex = Get-View searchIndex - - $passiveVM = $searchIndex.FindByUuid($null,$passiveBiosUUID,$true,$null) - $witnessVM = $searchIndex.FindByUuid($null,$witnessBiosUUID,$true,$null) - - if($passiveVM -ne $null -and $witnessVM -ne $null) { - Write-Host "Powering off & deleting Passive VM ..." - Stop-VM -VM (Get-View $passiveVM).Name -Confirm:$false | Out-Null - Remove-VM (Get-View $passiveVM).Name -DeletePermanently -Confirm:$false - Write-Host "Powering off & deleting Witness VM ..." - Stop-VM -VM (Get-View $witnessVM).Name -Confirm:$false | Out-Null - Remove-VM (Get-View $witnessVM).Name -DeletePermanently -Confirm:$false - } - } - } - } -} diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 deleted file mode 100644 index 4f9b16e..0000000 --- a/Modules/VMCPFunctions.psm1 +++ /dev/null @@ -1,322 +0,0 @@ -function Get-VMCPSettings { -<# - .NOTES - =========================================================================== - Created on: 10/27/2015 9:25 PM - Created by: Brian Graf - 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 - - .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 - - 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=$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 - } - } - } -} - -function Set-VMCPSettings { -<# - .NOTES - =========================================================================== - Created on: 10/27/2015 9:25 PM - Created by: Brian Graf - 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 - them to configure the additional VMCP settings - For each parameter, users should use the 'Tab' button to auto-fill the - possible values. - - .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 - - 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(SupportsShouldProcess=$true,ConfirmImpact="High")] - param - ( - [Parameter(Mandatory=$true, - ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True, - HelpMessage='What is the Cluster Name?')] - $cluster, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='$True=Enabled $False=Disabled')] - [bool]$enableVMCP, - - [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=$False, - ValueFromPipeline=$False, - HelpMessage='Options available for an APD response')] - [ValidateSet("disabled","restartConservative","restartAggressive","warning")] - [string]$VmStorageProtectionForAPD, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='Value in seconds')] - [Int]$VmTerminateDelayForAPDSec, - - [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")] - [string]$VmReactionOnAPDCleared, - - [Parameter(Mandatory=$False)] - [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers - ) - - Process { - - Foreach ($Clus in $Cluster) { - - 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} - 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 - - # 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 - - #Storage Protection For PDL - If ($PSBoundParameters.ContainsKey('VmStorageProtectionForPDL')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $VmStorageProtectionForPDL - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $ActualSettings.'Protection For PDL' - } - - #Storage Protection for APD - 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 - If ($PSBoundParameters.ContainsKey('VmReactionOnAPDCleared')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = $ActualSettings.'Reaction on APD Cleared' - } - - # Execute API Call - If ($pscmdlet.ShouldProcess($CL.Name,"Modify VMCP configuration")) { - $modify = $true - $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server - $Task = $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) - } - - # Wait for the reconfiguration task to finish to show the result - If ($Task) { - $TaskID = "Task-" + $($Task.Value) - Get-Task -Id $TaskID -Server $Server | Wait-Task | Out-Null - Get-VMCPSettings -Cluster $CL -Server $Server - } - } - } - } -} diff --git a/Modules/apply-hardening.psm1 b/Modules/apply-hardening.psm1 deleted file mode 100644 index 94b1279..0000000 --- a/Modules/apply-hardening.psm1 +++ /dev/null @@ -1,93 +0,0 @@ -function Apply-Hardening { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 2.0 Base Release - =========================================================================== - External Code Sources: - - =========================================================================== - 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 - Keyword: VM, Hardening, Security - =========================================================================== - - .DESCRIPTION - Applys a set of Hardening options to your VMs - - .Example - Get-VM TST* | Apply-Hardening - - .Example - $SampleVMs = Get-VM "TST*" - Apply-Hardening -VMs $SampleVMs - - .PARAMETER VMs - Specify the VMs - - -#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=$True, - Position=0)] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] - $VMs -) - -Process { -#region: Create Options - $ExtraOptions = @{ - "isolation.tools.diskShrink.disable"="true"; - "isolation.tools.diskWiper.disable"="true"; - "isolation.tools.copy.disable"="true"; - "isolation.tools.paste.disable"="true"; - "isolation.tools.dnd.disable"="true"; - "isolation.tools.setGUIOptions.enable"="false"; - "log.keepOld"="10"; - "log.rotateSize"="100000" - "RemoteDisplay.maxConnections"="2"; - "RemoteDisplay.vnc.enabled"="false"; - - } - if ($DebugPreference -eq "Inquire") { - Write-Output "VM Hardening Options:" - $ExtraOptions | Format-Table -AutoSize - } - - $VMConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec - - Foreach ($Option in $ExtraOptions.GetEnumerator()) { - $OptionValue = New-Object VMware.Vim.optionvalue - $OptionValue.Key = $Option.Key - $OptionValue.Value = $Option.Value - $VMConfigSpec.extraconfig += $OptionValue - } -#endregion - -#region: Apply Options - ForEach ($VM in $VMs){ - $VMv = Get-VM $VM | Get-View - $state = $VMv.Summary.Runtime.PowerState - Write-Output "...Starting Reconfiguring VM: $VM " - $TaskConf = ($VMv).ReconfigVM_Task($VMConfigSpec) - if ($state -eq "poweredOn") { - Write-Output "...Migrating VM: $VM " - $TaskMig = $VMv.MigrateVM_Task($null, $_.Runtime.Host, 'highPriority', $null) - } - } - } -#endregion -} \ No newline at end of file diff --git a/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 b/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 deleted file mode 100644 index ad6227c..0000000 --- a/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 +++ /dev/null @@ -1,372 +0,0 @@ -<# - .NOTES - =========================================================================== - Created on: 5/27/2015 3:24 PM - Created by: Brian Graf - Twitter: @vBrianGraf - Blog: http://www.vtagion.com - =========================================================================== -#> - -#Encoded Hardening Guide -$Global:Base64 = "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" - -Function Import-Xls { - -<# -.SYNOPSIS -Import an Excel file. - -.DESCRIPTION -Import an excel file. Since Excel files can have multiple worksheets, you can specify the worksheet you want to import. You can specify it by number (1, 2, 3) or by name (Sheet1, Sheet2, Sheet3). Imports Worksheet 1 by default. - -.PARAMETER Path -Specifies the path to the Excel file to import. You can also pipe a path to Import-Xls. - -.PARAMETER Worksheet -Specifies the worksheet to import in the Excel file. You can specify it by name or by number. The default is 1. -Note: Charts don't count as worksheets, so they don't affect the Worksheet numbers. - -.INPUTS -System.String - -.OUTPUTS -Object - -.EXAMPLE -".\employees.xlsx" | Import-Xls -Worksheet 1 -Import Worksheet 1 from employees.xlsx - -.EXAMPLE -".\employees.xlsx" | Import-Xls -Worksheet "Sheet2" -Import Worksheet "Sheet2" from employees.xlsx - -.EXAMPLE -".\deptA.xslx", ".\deptB.xlsx" | Import-Xls -Worksheet 3 -Import Worksheet 3 from deptA.xlsx and deptB.xlsx. -Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. - -.EXAMPLE -Get-ChildItem *.xlsx | Import-Xls -Worksheet "Employees" -Import Worksheet "Employees" from all .xlsx files in the current directory. -Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. - -.LINK -Import-Xls -http://gallery.technet.microsoft.com/scriptcenter/17bcabe7-322a-43d3-9a27-f3f96618c74b -Export-Xls -http://gallery.technet.microsoft.com/scriptcenter/d41565f1-37ef-43cb-9462-a08cd5a610e2 -Import-Csv -Export-Csv - -.NOTES -Author: Francis de la Cerna -Created: 2011-03-27 -Modified: 2011-04-09 -#Requires –Version 2.0 - -#> - - [CmdletBinding(SupportsShouldProcess = $true)] - Param ( - [parameter( - mandatory = $true, - position = 1, - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true)] - [String[]]$Path, - [parameter(mandatory = $false)] - $Worksheet = 1, - [parameter(mandatory = $false)] - [switch]$Force - ) - - Begin { - function GetTempFileName($extension) { - $temp = [io.path]::GetTempFileName(); - $params = @{ - Path = $temp; - Destination = $temp + $extension; - Confirm = $false; - Verbose = $VerbosePreference; - } - Move-Item @params; - $temp += $extension; - return $temp; - } - - # since an extension like .xls can have multiple formats, this - # will need to be changed - # - $xlFileFormats = @{ - # single worksheet formats - '.csv' = 6; # 6, 22, 23, 24 - '.dbf' = 11; # 7, 8, 11 - '.dif' = 9; # - '.prn' = 36; # - '.slk' = 2; # 2, 10 - '.wk1' = 31; # 5, 30, 31 - '.wk3' = 32; # 15, 32 - '.wk4' = 38; # - '.wks' = 4; # - '.xlw' = 35; # - - # multiple worksheet formats - '.xls' = -4143; # -4143, 1, 16, 18, 29, 33, 39, 43 - '.xlsb' = 50; # - '.xlsm' = 52; # - '.xlsx' = 51; # - '.xml' = 46; # - '.ods' = 60; # - } - - $xl = New-Object -ComObject Excel.Application; - $xl.DisplayAlerts = $false; - $xl.Visible = $false; - } - - Process { - $Path | ForEach-Object { - - if ($Force -or $psCmdlet.ShouldProcess($_)) { - - $fileExist = Test-Path $_ - - if (-not $fileExist) { - Write-Error "Error: $_ does not exist" -Category ResourceUnavailable; - } else { - # create temporary .csv file from excel file and import .csv - # - $_ = (Resolve-Path $_).toString(); - $wb = $xl.Workbooks.Add($_); - if ($?) { - $csvTemp = GetTempFileName(".csv"); - $ws = $wb.Worksheets.Item($Worksheet); - $ws.SaveAs($csvTemp, $xlFileFormats[".csv"]); - $wb.Close($false); - Remove-Variable -Name ('ws', 'wb') -Confirm:$false; - Import-Csv $csvTemp; - Remove-Item $csvTemp -Confirm:$false -Verbose:$VerbosePreference; - } - } - } - } - } - - End { - $xl.Quit(); - Remove-Variable -name xl -Confirm:$false; - [gc]::Collect(); - } -} -Function Show-HardeningGuide { - $Global:XLS = Import-XLS $env:temp\HG.XLSX - $ShowGuide = $XLS | Out-GridView -} -Function Assess-VM { -<# - .DESCRIPTION - This cmdlet will allow you to assess the security of the VM's in your environment - .EXAMPLE - # Specify Virtual Machines to Assess - $VM = Get-VM MGMT-AD, MGMT-VCO, WebCommander, Exchange01 - - # Assess against Risk Profile 2 - Assess-VM -VM $VM -RiskProfile 2 - - .EXAMPLE - # Assess all VM's against specific ID's - $VM = Get-VM - Assess-VM -VM $VM -ID 9,10,12,22,33,49 - - .EXAMPLE - # Assess All VM's against All ID's - Assess-VM -VM (Get-VM) -#> - [CmdletBinding( - DefaultParameterSetName = ”ID” - )] - Param ( - #Object receiving the hardening - [Parameter( - Position = 0, - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'What object are you running this against?')] - #[string]$VMs, - [PSObject[]] - $VM, - - #Hardening Guide Profile - [Parameter(Mandatory = $false, - ValueFromPipeline = $false, - HelpMessage = "You must choose which Hardening Guide Risk Profile to use (1-3).", - ParameterSetName = 'RiskProfile')] - #[ValidateRange(1,3)] - [int]$RiskProfile, - - #Hardening Guide Guideline ID - [Parameter(Mandatory = $false, - ValueFromPipeline = $false, - ParameterSetName = 'ID')] - [string[]]$ID, - - [parameter(ParameterSetName = "ID", - ValueFromPipeline = $false)] - $AllIDs = "1" - ) - - BEGIN { - # Validate VM Parameter - # This ensures the object being passed is an object rather than a string - [int]$Err = 0 - foreach ($object in $vm) { if (($object.GetType().FullName) -ne "VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl") { $Err += 1 } } - if ($Err -gt 0) { - Write-Error "One or more objects specified in the `$VM parameter is not a VM object" - Break - } - - - # Get the Date, used for folder creation and report - $Date = (Get-Date -Format MM.dd.yy) - - # Create array of ID's - if (-not $ID) { - $IDs = $null - } else { - $IDs = $ID.Replace(' ', '').split(',') - } - - # Manage Risk Profile Parameter value - $RiskArray = @("1","2","3") - - - ### HTML CSS CODE ### - $head = @' - -'@ - ##################### - - #C heck to see if HG exists in the $Env:Temp folder, if not, place it there - if (!(Test-Path -Path "$env:Temp\HG.XLSX")) { - - # Decode Hardening Guide from script - $Content = [System.Convert]::FromBase64String($Global:Base64) - Set-Content -Path $env:temp\HG.XLSX -Value $Content -Encoding Byte - } - - #Save Imported Hardening Guide to $XLS - $Global:XLS = Import-XLS $env:temp\HG.XLSX - - } - - PROCESS { - - # If risk profile is provided - if ($RiskArray -contains $RiskProfile) { - - $Guidelines = ($XLS | ?{ $_."Guideline ID" -like "VM*" -and $_."Risk Profile" -match $RiskProfile }) - foreach ($Guideline in $Guidelines) { - - $ID = $Guideline.ID - $GuidelineID = $Guideline."Guideline ID" - $Name = ($GuidelineID.Split("."))[1] - $Assessment = $Guideline."PowerCLI Command Assessment" - $Assessment = $Assessment -replace "Get-VM", "`$VM" - $Description = $Guideline."Description" - - Write-Host "Processing: $ID - $Name" -ForegroundColor CYAN - if ($assessment -ne "") { - $run = iex ($Assessment) - if ($run -eq $null) { - $run = New-Object System.object - $run | Add-Member -type NoteProperty -name Name -value "Clear" - $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" - } - $Task = $run | ConvertTo-Html -Fragment -PreContent "

$ID - $Name

$Description

" -PostContent "
" | Out-String - $Tasks += $task - } - } - } elseif ($AllIDs -eq "1" -and $ID -eq $null) { - - $Guidelines = ($XLS | ?{ $_."Guideline ID" -like "VM*" }) - foreach ($Guideline in $Guidelines) { - $ID = $Guideline.ID - $GuidelineID = $Guideline."Guideline ID" - $Name = ($GuidelineID.Split("."))[1] - $Assessment = $Guideline."PowerCLI Command Assessment" - $Assessment = $Assessment -replace "Get-VM", "`$VM" - $Description = $Guideline."Description" - Write-Host "Processing: $ID - $Name" -ForegroundColor CYAN - if ($assessment -ne "") { - $run = iex ($Assessment) - if ($run -eq $null) { - $run = New-Object System.object - $run | Add-Member -type NoteProperty -name Name -value "Clear" - $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" - } - $Task = $run | ConvertTo-Html -Fragment -PreContent "

$ID - $Name


$Description

" | Out-String - $Tasks += $task - } - } - - } else { - # If Guideline IDs are provided - foreach ($line in $ID) { - $Guideline = ($XLS | ?{ $_."Guideline ID" -like "VM*" -and $_."ID" -eq $Line }) - $GuidelineID = $Guideline."Guideline ID" - if ($GuidelineID -eq $null) { - Write-Host "$line is an invalid ID for this object... moving to next ID" -ForegroundColor 'Red' - Continue - } - $Name = ($GuidelineID.Split("."))[1] - - $Description = $Guideline."Description" - $Assessment = $Guideline."PowerCLI Command Assessment" - $Assessment = $Assessment -replace "Get-VM", "`$VM" - Write-Host "Processing: $line - $Name" -ForegroundColor CYAN - if ($assessment -ne "") { - $run = iex ($Assessment) - if ($run -eq $null) { - $run = New-Object System.object - $run| Add-Member -type NoteProperty -name Name -value "Clear" - $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" - } - $Task = $run | ConvertTo-Html -Fragment -PreContent "

$line - $Name


$Description

" -PostContent "
" | Out-String - $Tasks += $task - } - } - } - } - - END { - # if no tasks were generated, end function and do NOT create empty report - if ($tasks -eq $null) {Break } - - # Get's end time Hours/Minutes for creating the HTML report - $time = (get-date -Format HH-mm) - - # Checks to see if a folder has been created for that date. If not, it will create one. - if (!(Test-Path c:\Temp\$Date)) { - Write-Host "Folder does not exist... Creating" -ForegroundColor 'Yellow' - New-item "C:\Temp\$date" -type Directory - } - - # HTML Report is then generated - $HTML = ConvertTo-HTML -head $head -PostContent "
$Tasks
" -PreContent “

vSphere 6 VM Hardening Report

” | Out-File "c:\Temp\$Date\$Time-VMAssessment.html" -Force - - # Report is opened for user to see - invoke-item "c:\Temp\$Date\$Time-VMAssessment.html" - } -} - From ae429792ed5b2e2f0855f8c70c428f4052449c1b Mon Sep 17 00:00:00 2001 From: Wouter Kursten Date: Wed, 26 Apr 2017 18:58:32 +0200 Subject: [PATCH 83/90] removed brake for issue #74 --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 7a3f909..c92eb14 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -5138,7 +5138,7 @@ function Get-HVMachine { $machineList = Find-HVMachine -Param $PSBoundParameters if (!$machineList) { Write-Host "No Virtual Machine(s) Found with given search parameters" - break + } $queryResults = @() $desktop_helper = New-Object VMware.Hv.MachineService From 70f7be027070edf229c4f7005ec94689c15da813 Mon Sep 17 00:00:00 2001 From: Alessio Rocchi Date: Wed, 3 May 2017 08:24:43 +0200 Subject: [PATCH 84/90] Add a script to set multiple datastore Tag. This example uses new powershell 5 features, classes and two different Design Patterns (Singleton and Disposable). --- SetDatastoreTag.ps1 | 198 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100755 SetDatastoreTag.ps1 diff --git a/SetDatastoreTag.ps1 b/SetDatastoreTag.ps1 new file mode 100755 index 0000000..23708e5 --- /dev/null +++ b/SetDatastoreTag.ps1 @@ -0,0 +1,198 @@ +<# + .SYNOPSIS + A brief description of the file. + + .DESCRIPTION + Given a list of Datastore Names, this script will assign a Tag to them + + .PARAMETER csvFile + String representing the full path of the file + The file must be structured like this: + ----------------------------- + Tag1,Tag2,Tag3,Tag4 + IPv4-iSCSI-SiteA,Tag1,Tag3 + IPv4-NFS-SiteA,Tag2,Tag4 + ... + ----------------------------- + + .NOTES + =========================================================================== + Created on: 31/03/2017 11:16 + Created by: Alessio Rocchi + Organization: VMware + Filename: SetDatastoreTag.ps1 + =========================================================================== +#> +[CmdletBinding()] +param +( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [System.String]$csvFile, + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [String]$vCenter, + [Parameter(ValueFromPipeline = $true, + Position = 2)] + [AllowNull()] + [String]$Username, + [Parameter(Position = 3)] + [AllowNull()] + [String]$Password +) + +Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + +class vcConnector : System.IDisposable +{ + [String]$Username + [String]$Password + [String]$vCenter + [PSObject]$server + + static [vcConnector]$instance + + vcConnector($Username, $Password, $vCenter) + { + Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + + $this.Username = $Username + $this.Password = $Password + $this.vCenter = $vCenter + $this.connect() + } + + vcConnector($vcCredential, $vCenter) + { + Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + + $this.vcCredential = $vcCredential + $this.vCenter = $vCenter + $this.connect() + } + + [void] hidden connect() + { + try + { + if ([String]::IsNullOrEmpty($this.Username) -or [String]::IsNullOrEmpty($this.Password)) + { + $vcCredential = Get-Credential + Connect-VIServer -Server $this.vCenter -Credential $this.vcCredential -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } + else + { + Connect-VIServer -Server $this.vCenter -User $this.Username -Password $this.Password -WarningAction SilentlyContinue -ErrorAction Stop + } + Write-Debug("Connected to vCenter: {0}" -f $this.vCenter) + } + catch + { + Write-Error($Error[0].Exception.Message) + exit + } + } + + + [void] Dispose() + { + Write-Debug("Called Dispose Method of Instance: {0}" -f ($this)) + Disconnect-VIServer -WarningAction SilentlyContinue -Server $this.vCenter -Force -Confirm:$false | Out-Null + } + + static [vcConnector] GetInstance() + { + if ([vcConnector]::instance -eq $null) + { + [vcConnector]::instance = [vcConnector]::new() + } + + return [vcConnector]::instance + } +} + +class Content{ + [System.Collections.Generic.List[System.String]]$availableTags + [System.Collections.Generic.List[System.String]]$elements + + Content() + { + } + + Content([String]$filePath) + { + if ((Test-Path -Path $filePath) -eq $false) + { + throw ("Cannot find file: {0}" -f ($filePath)) + } + try + { + # Cast the Get-Content return type to Generic List of Strings in order to avoid fixed-size array + $this.elements = [System.Collections.Generic.List[System.String]](Get-Content -Path $filePath -ea SilentlyContinue -wa SilentlyContinue) + $this.availableTags = $this.elements[0].split(',') + # Delete the first element aka availableTags + $this.elements.RemoveAt(0) + } + catch + { + throw ("Error reading the file: {0}" -f ($filePath)) + } + } +} + +try +{ + $vc = [vcConnector]::new($Username, $Password, $vCenter) + $csvContent = [Content]::new($csvFile) + + Write-Host("Available Tags: {0}" -f ($csvContent.availableTags)) + + foreach ($element in $csvContent.elements) + { + [System.Collections.Generic.List[System.String]]$splittedList = $element.split(',') + # Get the Datastore Name + [System.String]$datastoreName = $splittedList[0] + # Removing Datastore Name + $splittedList.RemoveAt(0) + # Create a List of Tags which will be assigned to the Datastore + [System.Collections.Generic.List[PSObject]]$tagsToAssign = $splittedList | ForEach-Object { Get-Tag -Name $_ } + Write-Host("Tags to assign to Datastore: {0} are: {1}" -f ($datastoreName, $tagsToAssign)) + # Get Datastore object by the given Datastore Name, first field of the the line + $datastore = Get-Datastore -Name $datastoreName -ea Stop + # Iterate the assigned Datastore Tags + foreach ($tag in ($datastore | Get-TagAssignment)) + { + # Check if the current tag is one of the available ones. + if ($tag.Tag.Name -in $csvContent.availableTags) + { + # Remove the current assigned Tag + Write-Host("Removing Tag: {0}" -f ($tag)) + Remove-TagAssignment -TagAssignment $tag -Confirm:$false + } + } + # Finally add the new set of tags to the Datastore + foreach ($tag in $tagsToAssign) + { + Write-Host("Trying to assign Tag: {0} to Datastore: {1}" -f ($tag.Name, $datastoreName)) + # Assign the Tag + New-TagAssignment -Entity $datastore -Tag $tag + } + } +} +catch [VMware.VimAutomation.Sdk.Types.V1.ErrorHandling.VimException.VimException] +{ + Write-Error("VIException: {0}" -f ($Error[0].Exception.Message)) + exit +} +catch +{ + Write-Error $Error[0].Exception.Message + exit +} +finally +{ + # Let be assured that the vc connection will be disposed. + $vc.Dispose() +} From 3d0c65b8022ffa637dea3838efd93069f108d214 Mon Sep 17 00:00:00 2001 From: praveenmathamsetty Date: Mon, 15 May 2017 17:25:20 +0530 Subject: [PATCH 85/90] merge changes related to Get-HVPodSessions pull request https://github.com/vmware/PowerCLI-Example-Scripts/pull/71 --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 56ad921..d9bd7b9 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -8699,4 +8699,63 @@ function Remove-HVGlobalEntitlement { } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement +function Get-HVPodSession { +<# +.Synopsis + Gets the total amount of sessions for all Pods in a Federation +.DESCRIPTION + Gets the total amout of current sessions (connected and disconnected) for all Pods in a Federation (CPA) + based on the global query service. + The default object response is used which contains both success and fault information as well as the + session count per pod and the ID of each pod. +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer +.EXAMPLE + Get-HVPodSession +.OUTPUTS + Returns list of objects of type GlobalSessionPodSessionCounter +.NOTES + Author : Rasmus Sjoerslev + Author email : rasmus.sjorslev@vmware.com + Version : 1.0 + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2 + PowerCLI Version : PowerCLI 6.5 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $query_service_helper = New-Object VMware.Hv.GlobalSessionQueryServiceService + $count_spec = New-Object VMware.Hv.GlobalSessionQueryServiceCountSpec + $queryResults = @() + + foreach ($pod in $services.Pod.Pod_List()) { + $count_spec.Pod = $pod.Id + $info = $query_service_helper.GlobalSessionQueryService_GetCountWithSpec($services,$count_spec) + + foreach ($res in $info) { + if ($pod.Id.Id -eq $res.Id.Id) { + $queryResults += $res + } + } + } + return $queryResults +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Get-HVPodSession From 8337b986330eb1aa6f798ac9c1b07769bbec9af9 Mon Sep 17 00:00:00 2001 From: PARAMESHO Date: Tue, 16 May 2017 14:48:36 +0530 Subject: [PATCH 86/90] Advanced functions for customizing Application icons in Horizon This changes adds two advanced functions: Set-HVApplicationIcon -> Used to create/update an icon association for a given application. Remove-HVApplicationIcon -> Used to remove a customized icon association for a given application. --- .../VMware.Hv.Helper/VMware.HV.Helper.psm1 | 221 +++++++++++++++++- 1 file changed, 220 insertions(+), 1 deletion(-) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index d9bd7b9..85cad3a 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -8758,4 +8758,223 @@ function Get-HVPodSession { return $queryResults } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Get-HVPodSession +function Set-HVApplicationIcon { +<# +.SYNOPSIS + Used to create/update an icon association for a given application. + +.DESCRIPTION + This function is used to create an application icon and associate it with the given application. If the specified icon already exists in the LDAP, it will just updates the icon association to the application. Any of the existing customized icon association to the given application will be overwritten. + +.PARAMETER ApplicationName + Name of the application to which the association to be made. + +.PARAMETER IconPath + Path of the icon. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Creating the icon I1 and associating with application A1. Same command is used for update icon also. + Set-HVApplicationIcon -ApplicationName A1 -IconPath C:\I1.ico -HvServer $hvServer + +.OUTPUTS + None + +.NOTES + Author : Paramesh Oddepally. + Author email : poddepally@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] $ApplicationName, + + [Parameter(Mandatory = $true)] + $IconPath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -HvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break + } + Add-Type -AssemblyName System.Drawing + } + + process { + try { + $appInfo = Get-HVQueryResult -EntityType ApplicationInfo -Filter (Get-HVQueryFilter data.name -Eq $ApplicationName) -HvServer $HvServer + } catch { + # EntityNotFound, InsufficientPermission, InvalidArgument, InvalidType, UnexpectedFault + Write-Error "Error in querying the ApplicationInfo for Application:[$ApplicationName] $_" + break + } + + if ($null -eq $appInfo) { + Write-Error "No application found with specified name:[$ApplicationName]." + break + } + + if (!(Test-Path $IconPath)) { + Write-Error "File:[$IconPath] does not exists" + break + } + + $spec = New-Object VMware.Hv.ApplicationIconSpec + $base = New-Object VMware.Hv.ApplicationIconBase + + try { + $fileHash = Get-FileHash -Path $IconPath -Algorithm MD5 + $base.IconHash = $fileHash.Hash + $base.Data = (Get-Content $iconPath -Encoding byte) + $bitMap = [System.Drawing.Bitmap]::FromFile($iconPath) + $base.Width = $bitMap.Width + $base.Height = $bitMap.Height + $base.IconSource = "broker" + $base.Applications = @($appInfo.Id) + $spec.ExecutionData = $base + } catch { + Write-Error "Error in reading the icon parameters: $_" + break + } + + if ($base.Height -gt 256 -or $base.Width -gt 256) { + Write-Error "Invalid image resolution. Maximum resolution for an icon should be 256*256." + break + } + + $ApplicationIconHelper = New-Object VMware.Hv.ApplicationIconService + try { + $ApplicationIconId = $ApplicationIconHelper.ApplicationIcon_CreateAndAssociate($services, $spec) + } catch { + if ($_.Exception.InnerException.MethodFault.GetType().name.Equals('EntityAlreadyExists')) { + # This icon is already part of LDAP and associated with some other application(s). + # In this case, call updateAssociations + $applicationIconId = $_.Exception.InnerException.MethodFault.Id + Write-Host "Some application(s) already have an association for the specified icon." + $ApplicationIconHelper.ApplicationIcon_UpdateAssociations($services, $applicationIconId, @($appInfo.Id)) + Write-Host "Successfully updated customized icon association for Application:[$ApplicationName]." + break + } + Write-Host "Error in associating customized icon for Application:[$ApplicationName] $_" + break + } + Write-Host "Successfully associated customized icon for Application:[$ApplicationName]." + } + + end { + [System.gc]::collect() + } +} + +Function Remove-HVApplicationIcon { +<# +.SYNOPSIS + Used to remove a customized icon association for a given application. + +.DESCRIPTION + This function is used to remove an application association to the given application. It will never remove the RDS system icons. If application doesnot have any customized icon, an error will be thrown. + +.PARAMETER ApplicationName + Name of the application to which customized icon needs to be removed. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Removing the icon for an application A1. + Remove-HVApplicationIcon -ApplicationName A1 -HvServer $hvServer + +.OUTPUTS + None + +.NOTES + Author : Paramesh Oddepally. + Author email : poddepally@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [string] $ApplicationName, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -HvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break + } + } + + process { + try { + $appInfo = Get-HVQueryResult -EntityType ApplicationInfo -Filter (Get-HVQueryFilter data.name -Eq $ApplicationName) -HvServer $HvServer + } catch { + # EntityNotFound, InsufficientPermission, InvalidArgument, InvalidType, UnexpectedFault + Write-Error "Error in querying the ApplicationInfo for Application:[$ApplicationName] $_" + break + } + + if ($null -eq $appInfo) { + Write-Error "No application found with specified name:[$ApplicationName]" + break + } + + [VMware.Hv.ApplicationIconId[]] $icons = $appInfo.Icons + [VMware.Hv.ApplicationIconId] $brokerIcon = $null + $ApplicationIconHelper = New-Object VMware.Hv.ApplicationIconService + Foreach ($icon in $icons) { + $applicationIconInfo = $ApplicationIconHelper.ApplicationIcon_Get($services, $icon) + if ($applicationIconInfo.Base.IconSource -eq "broker") { + $brokerIcon = $icon + } + } + + if ($null -eq $brokerIcon) { + Write-Error "There is no customized icon for the Application:[$ApplicationName]." + break + } + + try { + $ApplicationIconHelper.ApplicationIcon_RemoveAssociations($services, $brokerIcon, @($appInfo.Id)) + } catch { + Write-Error "Error in removing the customized icon association for Application:[$ApplicationName] $_ " + break + } + Write-Host "Successfully removed customized icon association for Application:[$ApplicationName]." + } + + end { + [System.gc]::collect() + } +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Get-HVPodSession, Set-HVApplicationIcon, Remove-HVApplicationIcon From 4936b79a5cea87401471138b4c853566bb7a7e40 Mon Sep 17 00:00:00 2001 From: PARAMESHO Date: Tue, 16 May 2017 14:56:21 +0530 Subject: [PATCH 87/90] Updating the description of the advanced functions for ApplicationIcon Updating the description of the advanced functions for ApplicationIcon --- Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 85cad3a..5b68b9f 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -8789,6 +8789,7 @@ function Set-HVApplicationIcon { ===Tested Against Environment==== Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -8911,6 +8912,7 @@ Function Remove-HVApplicationIcon { ===Tested Against Environment==== Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 PowerShell Version : 5.0 #> From acb0383f1683a05de967a415de76247b558c1e3d Mon Sep 17 00:00:00 2001 From: NamedJason Date: Tue, 16 May 2017 08:19:00 -0700 Subject: [PATCH 88/90] Create DatastoreFunctions.psm1 --- Modules/DatastoreFunctions.psm1 | 184 ++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 Modules/DatastoreFunctions.psm1 diff --git a/Modules/DatastoreFunctions.psm1 b/Modules/DatastoreFunctions.psm1 new file mode 100644 index 0000000..e417112 --- /dev/null +++ b/Modules/DatastoreFunctions.psm1 @@ -0,0 +1,184 @@ +#Created by Alan Renouf, published at https://communities.vmware.com/docs/DOC-18008 +Function Get-DatastoreMountInfo { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Process { + $AllInfo = @() + if (-not $Datastore) { + $Datastore = Get-Datastore + } + Foreach ($ds in $Datastore) { + if ($ds.ExtensionData.info.Vmfs) { + $hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].diskname + if ($ds.ExtensionData.Host) { + $attachedHosts = $ds.ExtensionData.Host + Foreach ($VMHost in $attachedHosts) { + $hostview = Get-View $VMHost.Key + $hostviewDSState = $VMHost.MountInfo.Mounted + $StorageSys = Get-View $HostView.ConfigManager.StorageSystem + $devices = $StorageSys.StorageDeviceInfo.ScsiLun + Foreach ($device in $devices) { + $Info = "" | Select Datastore, VMHost, Lun, Mounted, State + if ($device.canonicalName -eq $hostviewDSDiskName) { + $hostviewDSAttachState = "" + if ($device.operationalState[0] -eq "ok") { + $hostviewDSAttachState = "Attached" + } elseif ($device.operationalState[0] -eq "off") { + $hostviewDSAttachState = "Detached" + } else { + $hostviewDSAttachState = $device.operationalstate[0] + } + $Info.Datastore = $ds.Name + $Info.Lun = $hostviewDSDiskName + $Info.VMHost = $hostview.Name + $Info.Mounted = $HostViewDSState + $Info.State = $hostviewDSAttachState + $AllInfo += $Info + } + } + + } + } + } + } + $AllInfo + } +} + +Function Detach-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Process { + if (-not $Datastore) { + Write-Host "No Datastore defined as input" + Exit + } + Foreach ($ds in $Datastore) { + $hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname + if ($ds.ExtensionData.Host) { + $attachedHosts = $ds.ExtensionData.Host + Foreach ($VMHost in $attachedHosts) { + $hostview = Get-View $VMHost.Key + $StorageSys = Get-View $HostView.ConfigManager.StorageSystem + $devices = $StorageSys.StorageDeviceInfo.ScsiLun + Foreach ($device in $devices) { + if ($device.canonicalName -eq $hostviewDSDiskName) { + $LunUUID = $Device.Uuid + Write-Host "Detaching LUN $($Device.CanonicalName) from host $($hostview.Name)..." + $StorageSys.DetachScsiLun($LunUUID); + } + } + } + } + } + } +} + +Function Unmount-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Process { + if (-not $Datastore) { + Write-Host "No Datastore defined as input" + Exit + } + Foreach ($ds in $Datastore) { + $hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname + if ($ds.ExtensionData.Host) { + $attachedHosts = $ds.ExtensionData.Host + Foreach ($VMHost in $attachedHosts) { + $hostview = Get-View $VMHost.Key + $StorageSys = Get-View $HostView.ConfigManager.StorageSystem + Write-Host "Unmounting VMFS Datastore $($DS.Name) from host $($hostview.Name)..." + $StorageSys.UnmountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid); + } + } + } + } +} + +Function Mount-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Process { + if (-not $Datastore) { + Write-Host "No Datastore defined as input" + Exit + } + Foreach ($ds in $Datastore) { + $hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname + if ($ds.ExtensionData.Host) { + $attachedHosts = $ds.ExtensionData.Host + Foreach ($VMHost in $attachedHosts) { + $hostview = Get-View $VMHost.Key + $StorageSys = Get-View $HostView.ConfigManager.StorageSystem + Write-Host "Mounting VMFS Datastore $($DS.Name) on host $($hostview.Name)..." + $StorageSys.MountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid); + } + } + } + } +} + +Function Attach-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Process { + if (-not $Datastore) { + Write-Host "No Datastore defined as input" + Exit + } + Foreach ($ds in $Datastore) { + $hostviewDSDiskName = $ds.ExtensionData.Info.vmfs.extent[0].Diskname + if ($ds.ExtensionData.Host) { + $attachedHosts = $ds.ExtensionData.Host + Foreach ($VMHost in $attachedHosts) { + $hostview = Get-View $VMHost.Key + $StorageSys = Get-View $HostView.ConfigManager.StorageSystem + $devices = $StorageSys.StorageDeviceInfo.ScsiLun + Foreach ($device in $devices) { + if ($device.canonicalName -eq $hostviewDSDiskName) { + $LunUUID = $Device.Uuid + Write-Host "Attaching LUN $($Device.CanonicalName) to host $($hostview.Name)..." + $StorageSys.AttachScsiLun($LunUUID); + } + } + } + } + } + } +} +# +#Get-Datastore | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +# +#Get-Datastore IX2ISCSI01 | Unmount-Datastore +# +#Get-Datastore IX2ISCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +# +#Get-Datastore IX2iSCSI01 | Mount-Datastore +# +#Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +# +#Get-Datastore IX2iSCSI01 | Detach-Datastore +# +#Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +# +#Get-Datastore IX2iSCSI01 | Attach-datastore +# +#Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +# From 6dab3ef94d4ce7487e178d84ce09b76240b6c7be Mon Sep 17 00:00:00 2001 From: Eric Gray Date: Thu, 25 May 2017 12:55:16 -0700 Subject: [PATCH 89/90] Initial commit of esxi-image scripts --- Scripts/esxi-image-comparator.ps1 | 97 +++++++++++++++++++++++++++ Scripts/esxi-image-creator.ps1 | 108 ++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 Scripts/esxi-image-comparator.ps1 create mode 100644 Scripts/esxi-image-creator.ps1 diff --git a/Scripts/esxi-image-comparator.ps1 b/Scripts/esxi-image-comparator.ps1 new file mode 100644 index 0000000..a9be557 --- /dev/null +++ b/Scripts/esxi-image-comparator.ps1 @@ -0,0 +1,97 @@ +<# +Script name: esxi-image-comparator.ps1 +Last update: 24 May 2017 +Author: Eric Gray, @eric_gray +Description: Compare contents (VIBs) of multiple VMware ESXi image profiles. +Dependencies: PowerCLI Image Builder (VMware.ImageBuilder,VMware.VimAutomation.Core) +#> + +param( + [switch]$ShowAllVIBs=$false, + [switch]$HideDates=$false, + [switch]$Interactive=$false, + [switch]$Grid=$false, + [string]$ProfileInclude, + [string]$ProfileExclude +) + +$profileList = Get-EsxImageProfile | sort -Property Name + +if ($ProfileInclude) { + $profileList = $profileList | ? Name -Match $ProfileInclude +} + +if ($ProfileExclude) { + $profileList = $profileList | ? Name -NotMatch $ProfileExclude +} + +if ($profileList.Count -eq 0) { + Write-Host "No ESXi image profiles available in current session." + Write-Host "Use Add-EsxSoftwareDepot for each depot zip bundle you would like to compare." + exit 1 +} + +if ($Interactive) { + $keep = @() + Write-Host "Found the following profiles:" -ForegroundColor Yellow + $profileList | % { write-host $_.Name } + + if ($profileList.Count -gt 7) { + Write-Host "Found $($profileList.Count) profiles!" -ForegroundColor Yellow + Write-Host "Note: List filtering is possible through -ProfileInclude / -ProfileExclude" -ForegroundColor DarkGreen + } + + write-host "`nType 'y' next to each profile to compare..." -ForegroundColor Yellow + + foreach ($profile in $profileList) { + $want = Read-Host -Prompt $profile.Name + if ($want.StartsWith("y") ) { + $keep += $profile + } + + } + $profileList = $keep + +} + +# go thru each profile and build a hash of the vib name and hash of profile name + version +$diffResults = @{} +foreach ($profile in $profileList ) { + foreach ($vib in $profile.VibList) { + $vibValue = $vib.Version + if (! $HideDates) { + $vibValue += " "+ $vib.CreationDate.ToShortDateString() + } + $diffResults.($vib.name) += @{$profile.name = $vibValue} + + } + +} + +# create an object that will neatly output as CSV or table +$outputTable=@() +foreach ($row in $diffResults.keys | sort) { + $vibRow = new-object PSObject + $vibRow | add-member -membertype NoteProperty -name "VIB" -Value $row + $valueCounter = @{} + + foreach ($profileName in $profileList.name) { + #populate this hash to decide if all profiles have same version of VIB + $valueCounter.($diffResults.$row.$profileName) = 1 + $vibRow | add-member -membertype NoteProperty -name $profileName -Value $diffResults.$row.$profileName + } + + if ($valueCounter.Count -gt 1 -or $ShowAllVIBs) { + $outputTable += $vibRow + } +} + +# useful for debugging +#$diffResults | ConvertTo-Json +#$outputTable|Export-Csv -Path .\image-diff-results.csv -NoTypeInformation + +if ($Grid) { + $outputTable | Out-GridView -Title "VMware ESXi Image Profile Comparator" +} else { + $outputTable +} \ No newline at end of file diff --git a/Scripts/esxi-image-creator.ps1 b/Scripts/esxi-image-creator.ps1 new file mode 100644 index 0000000..322c6e6 --- /dev/null +++ b/Scripts/esxi-image-creator.ps1 @@ -0,0 +1,108 @@ +<# +Script name: esxi-image-creator.ps1 +Last update: 24 May 2017 +Author: Eric Gray, @eric_gray +Description: Create a VMware ESXi image profile based on + one or more depots and offline driver bundles. +Dependencies: PowerCLI Image Builder (VMware.ImageBuilder,VMware.VimAutomation.Core) +#> + +param( + [switch]$NewestDate = $false, + [switch]$WriteZip = $false, + [switch]$WriteISO = $false, + [switch]$LeaveCurrentDepotsMounted = $false, + [string]$NewProfileName = "Custom Image $(Get-Date -Format "yyyyMMddhhmm")", + [ValidateNotNullOrEmpty()] + [ValidateSet('VMwareCertified','VMwareAccepted','PartnerSupported','CommunitySupported')] + [string]$Acceptance = "VMwareCertified", + [string[]]$Files = "*.zip" +) + +#### Specify optional image fine-tuning here #### +# comma-separated list (array) of VIBs to exclude +$removeVibs = @("tools-light") + +# force specific VIB version to be included, when more than one version is present +# e.g. "net-enic"="2.1.2.71-1OEM.550.0.0.1331820" +$overrideVibs = @{ + # "net-enic"="2.1.2.71-1OEM.550.0.0.1331820", +} + +#### end of optional fine-tuning #### + +# may be desirable to manually mount an online depot in advance, such as for HPE +# e.g. Add-EsxSoftwareDepot http://vibsdepot.hpe.com/index-ecli-650.xml +if (! $LeaveCurrentDepotsMounted) { + Get-EsxSoftwareDepot | Remove-EsxSoftwareDepot +} + +foreach ($depot in Get-ChildItem $Files) { + if ($depot.Name.EndsWith(".zip") ) { + Add-EsxSoftwareDepot $depot.FullName + } else { + Write-Host "Not a zip depot:" $depot.Name + } +} + +if ((Get-EsxImageProfile).count -eq 0) { + write-host "No image profiles found in the selected files" + exit 1 +} + +# either use the native -Newest switch, or try to find latest VIBs by date (NewestDate) +if ($NewestDate) { + $pkgsAll = Get-EsxSoftwarePackage | sort -Property Name,CreationDate -Descending + $pkgsNewestDate=@() + + foreach ($pkg in $pkgsAll) { + if ($pkgsNewestDate.GetEnumerator().name -notcontains $pkg.Name ) { + $pkgsNewestDate += $pkg + } + } + $pkgs = $pkgsNewestDate + +} else { + $pkgs = Get-ESXSoftwarePackage -Newest +} + +# rebuild the package array according to manual fine-tuning +if ($removeVibs) { + Write-Host "`nThe following VIBs will not be included in ${NewProfileName}:" -ForegroundColor Yellow + $removeVibs + $pkgs = $pkgs | ? name -NotIn $removeVibs +} + +foreach ($override in $overrideVibs.keys) { + # check that the override exists, then remove existing and add override + $tmpOver = Get-EsxSoftwarePackage -Name $override -Version $overrideVibs.$override + if ($tmpOver) { + $pkgs = $pkgs | ? name -NotIn $tmpOver.name + $pkgs += $tmpOver + } else { + Write-host "Did not find:" $override $overrideVibs.$override -ForegroundColor Yellow + } +} + +try { + New-EsxImageProfile -NewProfile $NewProfileName -SoftwarePackage $pkgs ` + -Vendor Custom -AcceptanceLevel $Acceptance -Description "Made with esxi-image-creator.ps1" ` + -ErrorAction Stop -ErrorVariable CreationError | Out-Null +} +catch { + Write-Host "Custom image profile $NewProfileName not created." -ForegroundColor Yellow + $CreationError + exit 1 +} + +Write-Host "`nFinished creating $NewProfileName" -ForegroundColor Yellow + +if ($WriteZip) { + Write-Host "Creating zip bundle..." -ForegroundColor Green + Export-EsxImageProfile -ImageProfile $NewProfileName -ExportToBundle -FilePath .\${NewProfileName}.zip -Force +} + +if ($WriteISO) { + Write-Host "Creating ISO image..." -ForegroundColor Green + Export-EsxImageProfile -ImageProfile $NewProfileName -ExportToIso -FilePath .\${NewProfileName}.iso -Force +} From 390ce91bb4f2b6fb03bbed56e0ebb4d58bacf7ea Mon Sep 17 00:00:00 2001 From: Kyle Ruddy Date: Fri, 26 May 2017 15:00:00 -0400 Subject: [PATCH 90/90] Update folder structure Update the folder structure to include moving the DatastoreFcuntions module into its own folder. --- Modules/{ => DatastoreFunctions}/DatastoreFunctions.psm1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Modules/{ => DatastoreFunctions}/DatastoreFunctions.psm1 (100%) diff --git a/Modules/DatastoreFunctions.psm1 b/Modules/DatastoreFunctions/DatastoreFunctions.psm1 similarity index 100% rename from Modules/DatastoreFunctions.psm1 rename to Modules/DatastoreFunctions/DatastoreFunctions.psm1