PowerShell/CLI: "Foreach" loop with multiple arrays
Asked Answered
C

5

8

I have a PowerCLI script that powers off a VM, changes its memory and cpu, and then powers it back on. I've adapted the script to utilize variables. This all works perfectly.

I'm now trying to modify the script to utilize arrays, in order to cycle through numerous VMs. The script portions that powers off and powers on the VMs works perfectly.

The trouble I'm having is using variables from two arrays in a foreach loop.

For each VM in $vm_name, I need to set the corresponding amount of memory found in $memory_gb.

This is what I have (it currently sets the same amount of memory ("1") for all of the VMs)....

$vm_name = @("OMAC-SBXWIN7AJM", "OMAC-SBXWIN2012R2AJM", "OMAC-SBXWIN2008R2AJM")
$memory_gb = 2,4,4

# SET THE VM MEMORY
Write-Host 'NOW SETTING THE VM MEMORY'
foreach ($objItem in $vm_name)
{Set-VM -VM $vm_name -MemoryGB 1 -confirm:$false 
Break
}

https://i.sstatic.net/E9hfY.png

...I've tried nesting a second foreach loop inside of the first, to no avail.

How do write the script so each VM in $vm_name, gets the corresponding amount of memory found in $memory_gb?

Crosspatch answered 7/8, 2014 at 20:41 Comment(0)
U
6

You have 2 options. First (and not what I would suggest) is a For() loop. It would go something like this:

For($I=0;$I -lt $vm_name.count;$I++){
    Set-VM -VM $vm_name[$I] -MemoryGB $memory_gb[$I] -confirm:$false
}

The better way would be to put it in a CSV with headers like VMName, Memory and then list each VM and the memory you want in it. Then run something like:

Import-CSV C:\Path\To\File.CSV | ForEach{Set-VM -VM $_.VMName -MemoryGB $_.memory -confirm:$false}
Unbolt answered 7/8, 2014 at 20:57 Comment(4)
Putting it into a data file is quite clever, hats off.Kerrill
This worked perfectly. Thank you! I will certainly be using a .CSV, as Ill eventually be modifying numerous machines.Crosspatch
This worked, but I got this error.... Set-VM : Cannot bind parameter 'MemoryGB' to the target. Exception setting "MemoryGB": "Object reference not set to an instance of an object." At U:\How To\PowerCLI\Test.ps1:8 char:36 + Set-VM -VM $vm_name[$i] -MemoryGB $memory_gb[$i] -confirm:$false + ~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (:) [Set-VM], ParameterBindingExcept ion + FullyQualifiedErrorId : ParameterBindingFailed,VMware.VimAutomation.ViCo re.Cmdlets.Commands.SetVM Since it worked, does the error really matter?Crosspatch
That's probably because I used -le instead of -lt so it was trying to iterate a 4th VM/memory set. I really expected you to utilize the second option, so I didn't pay as close of attention as I should have on the first one. I'll update the answer shortly to rectify that.Unbolt
C
7

You can use Zip function:

function Zip($a1, $a2) {
    while ($a1) {
        $x, $a1 = $a1
        $y, $a2 = $a2
        [tuple]::Create($x, $y)
    }
}

Usage:

zip 'a','b','c' 1,2,3 |% {$_.item1 + $_.item2}

Result:

a1
b2
c3
Celiaceliac answered 12/1, 2015 at 17:19 Comment(1)
@js2010 to clear it up for future readers: the $x, $a1 = $a1 syntax is unpacking the next member of the array instead of keeping a manual iterator $i or similarSupply
U
6

You have 2 options. First (and not what I would suggest) is a For() loop. It would go something like this:

For($I=0;$I -lt $vm_name.count;$I++){
    Set-VM -VM $vm_name[$I] -MemoryGB $memory_gb[$I] -confirm:$false
}

The better way would be to put it in a CSV with headers like VMName, Memory and then list each VM and the memory you want in it. Then run something like:

Import-CSV C:\Path\To\File.CSV | ForEach{Set-VM -VM $_.VMName -MemoryGB $_.memory -confirm:$false}
Unbolt answered 7/8, 2014 at 20:57 Comment(4)
Putting it into a data file is quite clever, hats off.Kerrill
This worked perfectly. Thank you! I will certainly be using a .CSV, as Ill eventually be modifying numerous machines.Crosspatch
This worked, but I got this error.... Set-VM : Cannot bind parameter 'MemoryGB' to the target. Exception setting "MemoryGB": "Object reference not set to an instance of an object." At U:\How To\PowerCLI\Test.ps1:8 char:36 + Set-VM -VM $vm_name[$i] -MemoryGB $memory_gb[$i] -confirm:$false + ~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (:) [Set-VM], ParameterBindingExcept ion + FullyQualifiedErrorId : ParameterBindingFailed,VMware.VimAutomation.ViCo re.Cmdlets.Commands.SetVM Since it worked, does the error really matter?Crosspatch
That's probably because I used -le instead of -lt so it was trying to iterate a 4th VM/memory set. I really expected you to utilize the second option, so I didn't pay as close of attention as I should have on the first one. I'll update the answer shortly to rectify that.Unbolt
S
4
$vm_name = @("OMAC-SBXWIN7AJM", "OMAC-SBXWIN2012R2AJM", "OMAC-
SBXWIN2008R2AJM")
$memory_gb = 2,4,4

# SET THE VM MEMORY
Write-Host 'NOW SETTING THE VM MEMORY'
foreach ($objItem in $vm_name)
{
    Set-VM -VM $vm_name -MemoryGB $memory_gb[$vm_name.indexOf($objItem)] -confirm:$false 
    Break
}

This will work as long as the two arrays are the same size.

Sinking answered 22/11, 2017 at 21:50 Comment(2)
you sir are a gentleman and a scholar, i could not figure out how to pass two array type variables to a foreach loop without it doing each array item on the other array items each time. so something that should have ran 4 loops was doing 16. But this part saved me: [$vm_name.indexof($objItem)]Storekeeper
Index of is a pretty clever solution.Sandon
S
3

Another, more compact, solution is to use a hashtable:

$vms = @{"OMAC-SBXWIN7AJM" = 2; "OMAC-SBXWIN2012R2AJM" = 4; "OMAC-SBXWIN2008R2AJM" = 4}

# SET THE VM MEMORY
Write-Host 'NOW SETTING THE VM MEMORY'
foreach ($vm in $vms.getEnumerator()){
    Set-VM -VM $vm.Name -MemoryGB $vm.Value -confirm:$false 
}
Sciatic answered 8/8, 2014 at 9:4 Comment(1)
This also worked for me. Thanks! I hadnt considered uses a hash table.Crosspatch
K
2
$vm_name = @("OMAC-SBXWIN7AJM", "OMAC-SBXWIN2012R2AJM", "OMAC-SBXWIN2008R2AJM")
$memory_gb = (2,4,4)

# SET THE VM MEMORY
Write-Host 'NOW SETTING THE VM MEMORY'
for( $i = 0; $i -lt $vm_name.length; $i++) {
  $vm = $vm_name[$i]
  $gb = $memory_gb[$i]
  write-host setting $vm to $gb GB ...
  Set-VM -VM $vm -MemoryGB $gb -confirm:$false 
}

You just need to ensure both arrays are of the same length.

Kerrill answered 7/8, 2014 at 20:57 Comment(1)
Thanks JesG. This provides the easiest to remember solution for me.Another

© 2022 - 2024 — McMap. All rights reserved.