Powershell Wrong output - powershell

guys i need help here, i want to return the $Location002 and $Location003 content look what is in output
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
$i = $_
# define name for VM, will be used for other resources
if ($i -eq 1) {
$locname = "$Location"
Write-Output $locname
}
else {
$locname = $("Location00" + "$i")
Write-Output $locname
}
}
output :
PS C:\Users\Marouane\Desktop\testpowershell> c:\Users\Marouane\Desktop\testpowershell\test.ps1
How many VMs?: 3
westus2
Location002
Location003
PS C:\Users\Marouane\Desktop\testpowershell>
i need to output westeurope and eastasia

Using a separate variable for each value in a group or list of things is a bit of an anti-pattern, you'll want to put them all in an array together instead:
# Define array of possible locations
# `$Locations[0]` will resolve to `westus2`
# `$Locations[1]` will resolve to `westeurope`, etc.
$Locations = #(
"westus2"
"westeurope"
"eastasia"
)
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
# Define the VM name
$name = "VirtualMachine$_"
# Pick next location from the $Locations array
# the % ensures we "wrap around" when we reach the end
$location = $Locations[($_ - 1) % $Locations.Length]
# Output a new object with Name + Chosen Location
[pscustomobject]#{
VMName = $name
Location = $location
}
}
Output for 3 VMs:
How many VMs?: 3
VMName Location
------ --------
VirtualMachine1 westus2
VirtualMachine2 westeurope
VirtualMachine3 eastasia

This is not how I would do it, but the more immediate problem is you're assigning a concatenated string to $location and writing to the output stream. I think what you want to do is reference the value of the earlier variable.
There are some clever syntaxes for that. I struggle to remember them. However below would be a start.
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
$i = $_
# define name for VM, will be used for other resources
if ($i -eq 1) {
$name = "$VMName"
$locname = "$Location"
Write-Output $locname
}
else {
$name = "$VMName" + "00" + "$i"
$locname = (Get-Variable ("Location00" + "$i")).Value
Write-Output $locname
}
}
Update With Alternative:
I'm still not sure what the goal is, but based on the original sample it would seem there's a 1 to 1 relationship between the location# and the VM number. That said if you go past the number of VMs you would have to adjust this to pick according to the intended pattern...
$Locations = 'westus2', 'westeurope', 'eastasia'
[int]$VMCount = Read-Host 'How many VMs?'
For( $i = 0; $i -lt $VMCount ; ++$i )
{
$Locations[$i]
}
Further Update:
Respective to Mathias's good answer :
$Locations = 'westus2', 'westeurope', 'eastasia'
[int]$VMCount = Read-Host 'How many VMs?'
For( $i = 0; $i -lt $VMCount ; ++$i )
{
$Locations[ $i % $Locations.Count ]
}
Using the modulus operator in this pattern is very efficient for distributing one list over another. I wrote a small post about My Modulus Obsession with this and some other uses.

You'll need to retrieve the variable's content with Get-Variable. You can also avoid the extra step of making an $i variable and instead use the automatic variable $_
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
# define name for VM, will be used for other resources
if ($_ -eq 1) {
$name = "$VMName"
$locname = "$Location"
Write-Output $locname
}
else {
$name = "$VMName" + "00" + "$_"
$locname = "Location00" + "$_"
Write-Output (get-variable $locname).value
}
}

Related

Hash Table, Multiples values in Key, Foreach Loop Powershell

I have filled the keys with the necessary values,
Every key will have multiples values
$vms = #{}
$vms.template += $templateName
$vms.name += $vmName
$vms.EsxiHostName += $esxiHostName
$vms.datastore += $datastoreName
$vms.network += $networkName
$vms.FolderLocation += $folderName
$vms.vCPU += $vCPU
$vms.CoresPerCPU += $vmCores
$vms.Memory += $vmRam
$vms.IP += $vmIP
$vms.SubnetMask += $vmMask
$vms.gateway += $vmGateway
$vms.DNS1 += $vmDns1
$vms.DNS2 += $vmDns2
$vms.Description += $vmDescription
$vms.TrendMicroScanDay += $tmscanday
$vms.inventory_billing_owner += $inventoryBillingOwner
And now what I want to do is something like this because I want to use these variables in another commands.
foreach ($vm in $vms) {
#Assign Variables
$VCTemplate = $vm.template
$VMName = $vm.Name
$VMHost = $vm.EsxiHostName
$Datastore = $vm.datastore
$NetworkName = $vm.network
$FolderLocation = $vm.FolderLocation
$vCPU = $vm.vCPU
$CoresPerCPU = $vm.CoresPerCPU
$Memory = $vm.Memory
$VMIP = $vm.IP
$SubnetMask = $vm.SubnetMask
$GW = $vm.Gateway
$DNS1 = $vm.DNS1
$DNS2 = $vm.DNS2
$Description = $VM.Description
$TrendMicroScanDay = $VM.TrendMicroScanDay
$inventory_billing_owner = $VM.inventory_billing_owner
}
It seems foreach loop doesn't work this way and I try to find information about it but was not possible
Someone know how can I work with a Foreach Loop and a Hash Table with multiples values per key?
Thanks
EDIT:
Thanks Mclayton for answer, I tried your solutions
First I want to send you what is inside of $vms
PS C:\Users\me\Desktop> $vms
Name Value
---- ----- SubnetMask {255.255.255.0, 255.255.255.255} description {TEST, Test 2}
Memory {4, 8}
name {Name1, Test 2}
vCPU {4, 8}
ip {10.10.10.1, 20.20.20.1} datastore {vsanDatastore, vsanDatastore} dns2 {10.10.10.5, 20.20.20.5}
gateway {10.10.10.3, 20.20.20.3}
template {ESSQLTEMPLATE01, WIN 10 Template}
FolderLocation {Office Domain, SysAdmin LAB}
TrendMicroScanDay {Day5, Day5}
CoresPerCPU {4, 8}
dns1 {10.10.10.4, 20.20.20.4}
EsxiHostName {es1esxi01p, es1esxi02p}
network {servers, data2}
Then with the first option running this for test
for($i = 0; $i -lt $vms.template.Length; $i++ )
{
$VCTemplate = $vms.template[$i];
$VMName2 = $vms.Name[$i];
}
PS C:\Users\me\Desktop> $VCTemplate
WIN 10 Template
I'm getting the second value, maybe I didn’t understand what you were saying
And with the second option, I was thinking what to use in the foreach ($something in $something_else)
but I ran this:
$vm3 = #()
$vm3 += new-object PSCustomObject -Property ([ordered] #{
Template = $vms.template
Name = $vms.name
EsxiHostName = $vms.EsxiHostName
datastore = $vms.datastore
network = $vms.network
FolderLocation = $vms.FolderLocation
vCPU = $vms.vCPU
CoresPerCPU = $vms.CoresPerCPU
Memory = $vms.Memory
IP = $vms.IP
SubnetMask = $vms.SubnetMask
gateway = $vms.gateway
DNS1 = $vms.DNS1
DNS2 = $vms.DNS2
Description = $vms.Description
TrendMicroScanDay = $vms.TrendMicroScanDay
})
foreach ($vm in $vm3)
{
write-host 'This is '$vm.template
}
And this was the result
PS C:\Users\me\Desktop> foreach ($vm in $vm3)
{
write-host 'This is '$vm.template
}
This is ESSQLTEMPLATE01 WIN 10 Template
In your code, $vms is a single hashtable object, and if you foreach() over a single object the loop will only run once. The fact that all of $vms's properties (e.g. $vms.template) are arrays doesn't make any difference to this.
If you really need to use a single hastable with properties that are parallel arrays, what you'll need to do is something like:
for($i = 0; $i -lt $vms.template.Length; $i++ )
{
$VCTemplate = $vms.template[$i];
$VMName = $vms.Name[$i];
... etc ...
... now do stuff with the $i'th vm ...
write-host $vmName;
}
but a better alternative would be to create $vms as an array of objects with #() (note round brackets not squiggly ones) - e.g.
$vms = #()
foreach( $something in $something_else )
{
$vms += new-object PSCustomObject -Property ([ordered] #{
Template = $something.template
Name = $something.name
... etc ...
})
}
and then you can iterate over $vms:
foreach ($vm in $vms)
{
write-host $vm.Name
}

How to add items to an array in a function as a script variable on Powershell?

I am trying to add items to an array variable that I am declaring outside of a function.
Here is the idea of my code in a very simplified way:
function Test($NAME, $SPEED){
$fName = "testName"
$fSpeed = 100
if($status = ($fName -eq $NAME) -and ($fSpeed -eq $SPEED))
{}
else{
if($fName -ne $NAME)
{$errorMessages += "The name is not" + $NAME}
if($fSpeed -ne $SPEED)
{$errorMessages += "The speed is not" + $SPEED}
}
return $status
}
$script:errorMessages=#()
$result=#()
$result += Test -NAME "alice" -SPEED "100"
$result += Test -NAME "bob" -SPEED "90"
#result is an array of booleans that I need later on
$errorMessages
When I display $errorMessages, this is the expected output that I'd like:
The name is not alice
The name is not bob
The speed is not 90
However, when I try to display the variable outside of the function, and even outside of the "else" block, I get nothing printed out. How can I correctly add the error messages to the array?
You want to call errorMessages via the script scope. Therefore you've to use $script:errorMessage (instead of $errorMessage) inside your function.
function Test($NAME, $SPEED) {
$fName = "testName"
$fSpeed = 100
$status = ($fName -eq $NAME) -and ($fSpeed -eq $SPEED)
if (!$status) {
if ($fName -ne $NAME) {
$script:errorMessages += "The name is not" + $NAME
}
if ($fSpeed -ne $SPEED) {
$script:errorMessages += "The speed is not" + $SPEED
}
}
$status
}
$errorMessages = #()
$result = #()
$result += Test -NAME "alice" -SPEED "100"
$result += Test -NAME "bob" -SPEED "90"
#result is an array of booleans that I need later on
$errorMessages
Now you get the expected output:
The name is notalice
The name is notbob
The speed is not90
Also be aware about the return statement in PowerShell -> stackoverflow answer
Hope that helps

Generate 2 different list in one foreach loop with powershell

I stucked in foreach part.I couldn't find any solution for generating 2 different lists in one foreach loop.I used 2 foreach but it didn't help.Below side I shared my desire output.
My code:
$InStuff = #'
a
b
c
'#.Split("`n").Trim()
$InStuff2 = #'
1
2
3
'#.Split("`n").Trim()
$SPart_1 = 'application="'
$SPart_2 = ' path='
$SPart_3 = ' name='
$SPart_4 = ' application'
foreach ($IS_Item in $InStuff) {
foreach ($IS2_Item in $InStuff2) {
$UName = $IS_Item
$UName2 = $IS2_Item
$Sentence = -join (
$SPart_1, $UName,
$SPart_2, $UName2,
$SPart_3, $UName2,
$SPart_4
)
''
$Sentence
}
}
Fail output :
application="a path=1 name=1 application
application="a path=2 name=2 application
application="a path=3 name=3 application
application="b path=1 name=1 application
application="b path=2 name=2 application
application="b path=3 name=3 application
application="c path=1 name=1 application
application="c path=2 name=2 application
application="c path=3 name=3 application
My desire output :
application="a path=1 name=1 application
application="b path=2 name=2 application
application="c path=3 name=3 application
Thank you
use a for loop:
$InStuff = #'
a
b
c
'#.Split("`n").Trim()
$InStuff2 = #'
1
2
3
'#.Split("`n").Trim()
$SPart_1 = 'application="'
$SPart_2 = ' path='
$SPart_3 = ' name='
$SPart_4 = ' application'
for ($i = 0; $i -lt $InStuff.count; $i++) {
$Sentence = -join (
$SPart_1, $InStuff[$i],
$SPart_2, $InStuff2[$i],
$SPart_3, $InStuff2[$i],
$SPart_4
), ''
$Sentence
}
This will likely go wrong if your input arrays are not the same length, so it is not that safe. Perhaps using a hash or custom object would be a better idea:
$arr = #()
$arr += new-object PSCustomObject -property #{application='a';path=1;name=1}
$arr += new-object PSCustomObject -property #{application='b';path=2;name=2}
$arr += new-object PSCustomObject -property #{application='c';path=3;name=3}
$arr | % { 'application="{0} path={1} name={2}' -f $_.application, $_.path, $_.name }
#arco444 is right, no matter what you will have problems if your lists are different lengths. You should reconsider how you are collecting and formatting the data. Here is an alternative method:
$InStuff = "a","b","c"
$InStuff2 = 1,2,3
$listCount = $InStuff.Count
$x = 0
do {
$strOut = "application= `"path = {0} name = {1} application`"" -f $InStuff[$x], $InStuff2[$x]
$strOut
$x++
}
while ( $x -lt $listCount )
Not sure what you want with a stray " in there, I've added one to enclose the output:
application= "path = a name = 1 application"
application= "path = b name = 2 application"
application= "path = c name = 3 application"
If you plan to use this output for further processing by PowerShell, like putting it in a csv with Export-Csv then you should forgo the application text and create an object instead:
$InStuff = "a","b","c"
$InStuff2 = 1,2,3
$listCount = $InStuff.Count
$x = 0
do {
[pscustomobject]#{
path = $InStuff[$x]
name = $InStuff2[$x]
}
$x++
}
while ( $x -lt $listCount )
While that's not exactly what you are asking for, it's been my experience that data in this format is far more useful:
path name
---- ----
a 1
b 2
c 3
you can add lines to
[pscustomobject]#{
path = $InStuff[$x]
name = $InStuff2[$x]
}
for the additional text (if it's a must) and do something like this:
[pscustomobject]#{
type = "application"
path = $InStuff[$x]
name = $InStuff2[$x]
}
and that will add a column for the word application

Use 2 arrays in one loop

I want to use 2 arrays in one loop, but I am failing each time to find out how?
$hosts = "1.1.1.1,2.2.2.2,3.3.3.3"
$vmotionIPs = "1.2.3.4,5.6.7.8,7.8.9.0"
foreach ($host in $hosts) ($vmotionIP in $vmotionIPs)
New-VMHostNetworkAdapter -VMHost $host-VirtualSwitch myvSwitch `
-PortGroup VMotion -IP $vmotionIP -SubnetMask 255.255.255.0 `
-VMotionEnabled $true
I know the above syntax is wrong but I just hope it conveys my goal here.
The most straightforward way is to use a hashtable:
$hosts = #{
"1.1.1.1" = "1.2.3.4" # Here 1.1.1.1 is the name and 1.2.3.4 is the value
"2.2.2.2" = "5.6.7.8"
"3.3.3.3" = "7.8.9.0"
}
# Now we can iterate the hashtable using GetEnumerator() method.
foreach ($hostaddr in $hosts.GetEnumerator()) { # $host is a reserved name
New-VMHostNetworkAdapter -VMHost $hostaddr.Name -VirtualSwitch myvSwitch `
-PortGroup VMotion -IP $$hostaddr.Value -SubnetMask 255.255.255.0 `
-VMotionEnabled $true
}
First, your arrays aren't arrays. They're just strings. To be arrays you'll need to specify them as:
$hosts = "1.1.1.1","2.2.2.2","3.3.3.3";
$vmotionIPs = "1.2.3.4","5.6.7.8","7.8.9.0";
Second, $host is a reserved variable. You should avoid using that.
Third, I'm assuming you want the first host to use the first vmotionIP, the second host to use the second vmotionIP, etc.
So, the standard way to do this is to do this:
$hosts = "1.1.1.1","2.2.2.2","3.3.3.3";
$vmotionIPs = "1.2.3.4","5.6.7.8","7.8.9.0";
for ($i = 0; $i -lt $hosts.Count; $i++) {
New-VMHostNetworkAdapter -VMHost $hosts[$i] `
-VirtualSwitch myvSwitch `
-PortGroup VMotion `
-IP $vmotionIPs[$i] `
-SubnetMask 255.255.255.0 `
-VMotionEnabled $true;
}
Or you can use the hashtable method #AlexanderObersht describes. This method changes the least about your code, however.
Thank you for your information. What you suggested worked for me some other script but i ended up achieving this using the following.
first i generated a series of ip addresses like this
$fixed = $host1.Split('.')[0..2]
$last = [int]($host.Split('.')[3])
$max = Read-Host "Maximum number of hosts that you want to configure?"
$max_hosts = $max - 1
$hosts =
$last..($last + $max_hosts) | %{
[string]::Join('.',$fixed) + "." + $_
}
and then i did
$vMotion1_ip1 = Read-Host "the 1st vmotion ip of the 1st host?"
$fixed = $vMotion1_ip1.Split('.')[0..2]
$last = [int]($vMotion1_ip1.Split('.')[3])
$max_hosts = $max - 1
$vMotions =
$last..($last + $max_hosts) | %{
[string]::Join('.',$fixed) + "." + $_
}
$first = [string]::Join('.',$fixed) + "." + $_
foreach ($vmhost in $vMotions) {write-host "$vmhost has the following network ("$first$(($last++))", "255.255.255.0")"}
not exactly like this but something along this way.
Thank you all for your answers. I ended up using the do while instead. This allows us to loop through as many as arrays as we want at the same time or include multiple arrays in one loop.
$hosts = #("1.1.1.1","2.2.2.2","3.3.3.3")
$vmotionIPs = #("1.2.3.4","5.6.7.8","7.8.9.0")
[int]$n = 0
do
{
$vmhost = $hosts[$n]
$vmotionIP = $vmotionIPs[$n]
New-VMHostNetworkAdapter -VMHost $vmhost-VirtualSwitch myvSwitch -PortGroup VMotion -IP $vmotionIP -SubnetMask 255.255.255.0 -VMotionEnabled $true
$n++
} while ($n -lt $hosts.count)

How to create ForEach loop to go through multiple variables Powershell

I've got a problem with ForEach loop. Im trying to loop through multiple variables of same kind just increment different.
Im trying to change the TextBox Text depending on if Label from same row has text.
This is how I could make it to just write and IF sentence for each Label but I was looking a way to loop each of these blocks through ForEach loop. I've got total of 8 Labels and Textboxes.
Here is the code: ( Im sure you'll figure out what I'm after :) )
IF ( $Label1.Text.Length -ne 0 )
{
$Label1.Visible = $true
$TextBox1.Visible = $true
$TextBox1.Text = ( "Enter new name for " + $Label1.Text )
}
example of ForEach
$Count = 1..8
$Count | ForEach-Object {
IF ( $Label($_).Text.Length -ne 0 )
{
$Label($_).Visible = $true
$TextBox($_).Visible = $true
$TextBox($_).Text = ( "Enter new name for " + $Label($_).Text )
}
}
etc...
I tried putting variables in array and loop through that way but ofcourse array changes the type to string and it doesnt work...
Give this a try, I can't test it using label & textbox object but it can work tuning it better:
1..8 | ForEach-Object {
IF ( (iex "`$Label$_.Text.Length") -ne 0 )
{
iex "`$Label$_.Visible = `$true"
iex "`$TextBox$_.Visible = `$true"
iex "`$TextBox$_.Text = 'Enter new name for ' + `$Label$_.Text"
}
}
You can use the Get-Variable cmdlet for that purpose:
1..8 | ForEach-Object {
if ( (Get-Variable "Label$_").Value.Text.Length -ne 0 )
{
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Text = ( "Enter new name for " + (Get-Variable "Label$_").Value.Text )
}
}