PowerCLI to walk through clusters and hosts

Nov2009 picture Nov2009 · May 3, 2017 · Viewed 11.5k times · Source

Here is what I am trying to do: I have a vSphere setup with multiple clusters, underneath those clusters there are a few hosts.

I am trying to write a script that walks through the clusters and inside each cluster, puts the host into maintenance mode, moves it outside the cluster, start/stop a VM, add memory to it, then moves the host back into the cluster it was moved out of.

Here is what I have so far. The inner loop works, but the outer one is just making everything run twice and adding the cluster name as $cluster in the inner loop.

Any ideas? I just want the inner loop to run for all the hosts in each cluster.

I added -WhatIf for testing, you can ignore those.

Connect-VIServer vcenter01

$clusters = Get-Cluster
$esxhosts = Get-Cluster $clusters | Get-VMHost
$Datacenter = "Datacenter01"
$sleep = 1

& {
    foreach ($cluster in $clusters) {
        foreach ($esxhost in $esxhosts) {
            Set-VMHost $esxhost -State Maintenance -WhatIf
            Move-VMhost $esxhost -Destination $Datacenter -WhatIf
            Set-VMHost $esxhost -State Connected -WhatIf
            Sleep $sleep
            Stop-VMGuest -Vm Z-VRA-$esxhost -Confirm:$false -WhatIf
            Sleep $sleep
            Set-VM -Vm Z-VRA-$esxhost -MemoryGB 6 -Confirm:$false -WhatIf
            Start-VM -Vm Z-VRA-$esxhost -WhatIf
            Sleep $sleep
            Move-VMhost $esxhost -Destination $cluster -WhatIf
        }
    }
}

Disconnect-VIServer vcenter01

Here is what a working copy looks like (Thanks @Ansgar Wiechers):

I added in some code to start/stop HA admission control on each cluster as it works through it. That will stop issues with VM's vacating during maintenance mode if you are low on resorces.

Connect-VIServer vcenter01

$Datacenter = "Datacenter01"
$sleep = 1

Get-Cluster  | ForEach-Object {
$cluster = $_
Set-Cluster -HAAdmissionControlEnabled $false -Cluster $cluster -Confirm:$false -Whatif
$cluster  | Get-VMHost | ForEach-Object {
        Set-VMHost $_ -State Maintenance -WhatIf
        Move-VMhost $_ -Destination $Datacenter -WhatIf
        Set-VMHost $_ -State Connected -WhatIf
        Sleep $sleep
        Stop-VMGuest -Vm Z-VRA-$_ -Confirm:$false -WhatIf
        Sleep $sleep
        Set-VM -Vm Z-VRA-$_ -MemoryGB 6 -Confirm:$false -WhatIf
        Start-VM -Vm Z-VRA-$_ -WhatIf
        Sleep $sleep
        Move-VMhost $_ -Destination $cluster -WhatIf


    }
   Set-Cluster -HAAdmissionControlEnabled $true -Cluster $cluster Confirm:$false -Whatif
}


Disconnect-VIServer vcenter01

Answer

Ansgar Wiechers picture Ansgar Wiechers · May 4, 2017

This statement gives you all clusters:

$clusters = Get-Cluster

This statement gives you all hypervisors of all clusters:

$esxhosts = Get-Cluster $clusters | Get-VMHost

Because your inner loop already iterates over all hypervisers of all clusters, iterating over the clusters in an outer loop repeats that operation for each cluster. For two clusters you get the result twice, for three clusters you'd get the result three times, and so on.

Since the final operation in the inner loop is not cluster-agnostic, your code might actually break something if you removed the chicken switch. You need to enumerate the hypervisors per cluster. I don't have access to a vSphere system, but I suppose something like this should do what you want:

Get-Cluster | ForEach-Object {
    $cluster = $_
    $cluster | Get-VMHost | ForEach-Object {
        Set-VMHost $_ -State Maintenance -WhatIf
        ...
        Move-VMhost $_ -Destination $cluster -WhatIf
    }
}

Side note: the & { ... } around your loops is pointless. Just drop it.