Spartacus75
Contributor
Contributor

How to pass parameters in a variable in a loop FOREACH ?

Hello everyone

I opened a new topic because I need your lights.

I have a script that allows me to automatically create multiple virtual machines.

I would now like to put parameters into a variable so that I can from select from my command prompt the number of machines at create and the name of machines.

I tried the variable args [] but it does not work in a foreach loop.

Here my script:

Connect-VIServer -Server 10.*.*.* -Protocol https -User admin-Password admin

2..$args[0] | Foreach {
New-vm -vmhost Server1.rio.paris.eu -Name "$args[1]$_" -Template TestScript -Datastore Database_1

}

2..$args[0] | Foreach {
Start-VM -VM "$args[1]$_"
}

In this script, the variable "$args[0]" to indicate the number of machine created and  the variable "args[1]" to indicate the name of the machines.

But it does not work.

I hope to have been clear on my problem.

Thanks so much for your help.

0 Kudos
22 Replies
Spartacus75
Contributor
Contributor

Smiley Happy

0 Kudos
LucD
Leadership
Leadership

Why don't you used named parameters, that is a lot easier and clearer

param($Count,$Name)

Connect-VIServer -Server 10.*.*.* -Protocol https -User admin-Password admin
2..$Count | Foreach {
  New-vm -vmhost Server1.rio.paris.eu -Name "$Name$_" -Template TestScript -Datastore Database_1
} 2..$Count | Foreach {   Start-VM -VM "$Name$_"
}

You can now call the script as follows

C:\  PS> ./script.ps1 -Count 5 -Name testvm


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

Hello,

Thanks you so much Luc, very nice !

I have another question.

I have a script that is able to create machines, give a name and the IP parameters. I tested the loops separately, it works well.

However, when I gather the loops in the same script and that I execute, I got error messages.
I think that all the loops of script are executed simultaneously.
As a result, the configuration of name and the IP does not work.

Are there a powershell command that can verify that the first loop is complete before starting the second loop?

here is my script:

param($Count,$Name,$Type,$Ip)

Connect-VIServer -Server 10.*.*.* -Protocol https -User admin-Password Admin
2..$Count | Foreach {
  New-vm -vmhost Server.rio.england.eu -Name "$Name$_" -Template $Type -Datastore BASE_OF_30
  }
 
2..$Count | Foreach {
  Start-VM -VM "$Name$_"}
 
2..$Count | Foreach {
$VM = Get-VM "$Name$_"
$ESXHost = $VM | Get-VMHost
$secpasswd = ConvertTo-SecureString "admin" -AsPlainText -Force
$GuestCred = New-Object System.Management.Automation.PSCredential ("admin", $secpasswd)
Function Set-WinVMIP ($VM, $HC, $GC){
$Ip++
$ps1= "c:\ip.bat $Ip $VM"
Invoke-VMScript -VM $VM -HostCredential $HC -GuestCredential $GC -ScriptType bat -ScriptText $ps1

}
}


Set-WinVMIP $VM $HostCred $GuestCred

Thank so much for your help.

0 Kudos
LucD
Leadership
Leadership

The 2 loops are executed in sequence, the 2nd will not start before the first loop is finished.

But what probably happens is that VMs that are created by the New-VM cmdlets in the 1st loop, are not yet fully available when you reach the 2 loop.

You could loop through those VMs, before starting the 2nd, and check if they are available


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

Thank you for your help Lucd,

How to loop through those VMs ? How to check if they are available before to running the second script ?

Thank

0 Kudos
Spartacus75
Contributor
Contributor

Anyone have an idea?Smiley Happy

Thank.

0 Kudos
Spartacus75
Contributor
Contributor

Hello everybody,

I have a big problem.

I designed my script so that it automatically increments the last decimal of my Ip (10,170,219.%1).

So my script will be able to create machines, start the machines, give a name and an IP address.

My problem is that my script can do everything except incrementing the IP decimal. Yet I put in my loop the argument "$ Ip + +" to increment.

Here is my scrip:

param($Count,$Name,$Type,$Ip)

Connect-VIServer -Server 10..*.*.* -Protocol https -User admin -Password admin

2..$Count | Foreach {
  New-vm -vmhost 007.london.england.eu -Name "$Name$_" -Template $Type -Datastore Data_base007  }
 
read-host "Press Enter"

2..$Count | Foreach {
  Start-VM -VM "$Name$_"}
 
read-host "Press Enter"

2..$Count | Foreach {
$VM = Get-VM "$Name$_"
write-host "$Name$_"
$ESXHost = $VM | Get-VMHost
$secpasswd = ConvertTo-SecureString "formation" -AsPlainText -Force
$GuestCred = New-Object System.Management.Automation.PSCredential ("formation", $secpasswd)
Function Set-WinVMIP ($VM, $HC, $GC){
$Ip++
$ps1= "c:\ip.bat $Ip $VM"
Invoke-VMScript -VM $VM -HostCredential $HC -GuestCredential $GC -ScriptType bat -ScriptText $ps1

}
Set-WinVMIP $VM $HostCred $GuestCred
}

would you have a solution to this problem?

Thank you.

0 Kudos
LucD
Leadership
Leadership

The problem is that your $Ip variable contains a string and not an integer.

The ++ operator only works on numbers.

You'll have to split out the last number, increment that and join the parts of the IP address back together.

For example

$ip = "192.168.1.1"
$fixed = $ip.Split('.')[0..2]
$last = [int]($ip.Split('.')[3])

$last..($last + 9) | %{
    [string]::Join('.',$fixed) + "." + $_
}

This will create a series of IP addresses starting with 192.168.1.1 and ending with 192.168.1.10


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

HelloSmiley Happy,


Thank you very much for your help, happily that the VMWARE community  is hereSmiley Wink !
In fact, when I run my script via the command prompt, I give in parameter a number has  the last decimal of ip address . : Script.sp1 -Name Machine1 Template-type-Ip 24.
The incrementing will be based on this number for the other machines.
Thus, the first machine will created with the IP address: 10.170.229.24
For each machine created the last decimal should increment of 1.
Example:
Machine 2 -> 10.170.229.25
Machine 3 -> 10.170.229.26
Machine 4 -> 10.170.229.27
etc....
To accomplish this objective, it is mandatory that I go in once I enter a number as parameter.
in fact, my script works, but my script configures all machines with the same IP address. which causes Ip address conflict.
The problem with your script is that I can not give a number in parameter.
As you understand the problem?
Thank you very much.Smiley Happy
0 Kudos
LucD
Leadership
Leadership

You're having a problem with the scope of the $Ip variable.

You increment the number ($Ip) inside the function, that change will be gone when you exit the function.

One solution is to make that a global variable ($global:Ip),

or change the script like this

param($Count,$Name,$Type,$Ip)

Connect-VIServer -Server 10..*.*.* -Protocol https -User admin -Password admin
2..$Count | Foreach {
  New-vm -vmhost 007.london.england.eu -Name "$Name$_" -Template $Type -Datastore Data_base007 
}
read-host "Press Enter" 2..$Count | Foreach {   Start-VM -VM "$Name$_"
} read-host "Press Enter" 2..$Count | Foreach {   $VM = Get-VM "$Name$_"  write-host "$Name$_"
  $ESXHost = $VM | Get-VMHost
  $secpasswd = ConvertTo-SecureString "formation" -AsPlainText -Force
  $GuestCred = New-Object System.Management.Automation.PSCredential ("formation", $secpasswd)   Function Set-WinVMIP ($VM, $HC, $GC, $IP){     $ps1 = "c:\ip.bat $IP $VM"
   
Invoke-VMScript -VM $VM -HostCredential $HC -GuestCredential $GC -ScriptType bat -ScriptText $ps1   }   Set-WinVMIP $VM $HostCred $GuestCred,$Ip
 
$Ip++
}


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

Thank you very much, you're the best!Smiley Happy

I have a last question.

How can i proceed for that my loop of configuration is executed after the startup of machines ?

Indeed, the loop that allows to configure the name and ip address start just after the loop of startup machines. However, at the moment of his execution, the machines have not yet started.

Again thank you for your help, this is really great!Smiley Happy

0 Kudos
Spartacus75
Contributor
Contributor

Hello,

I tried different methods, but my loop of configuration of name and ip address is always executed before the machines are started.
You have an idea to fix the problem?
0 Kudos
Spartacus75
Contributor
Contributor

Smiley Happy

0 Kudos
Spartacus75
Contributor
Contributor

Hello everyone,

is there a command to check if the machine is started or not ?

I can not solve this problem, my loop of configuration of name and ip address debuted before the machines are started, and therefore, it does not work.

I would like my script execute the first two loops, then verifies that the machines are started before execute the last loop, is it possible? I find no solution on this issue.

Thank you for your helpSmiley Happy.

Here is my script:

param($Count,$Name,$Type,$Ip)

Connect-VIServer -Server 10..*.*.* -Protocol https -User admin -Password admin
2..$Count | Foreach {
  New-vm -vmhost 007.london.england.eu -Name "$Name$_" -Template $Type -Datastore Data_base007 
} 2..$Count | Foreach {   Start-VM -VM "$Name$_"
} read-host "Press Enter"

2..$Count | Foreach {   $VM = Get-VM "$Name$_"  write-host "$Name$_"
  $ESXHost = $VM | Get-VMHost
  $secpasswd = ConvertTo-SecureString "formation" -AsPlainText -Force
  $GuestCred = New-Object System.Management.Automation.PSCredential ("formation", $secpasswd)   Function Set-WinVMIP ($VM, $HC, $GC, $IP){     $ps1 = "c:\ip.bat $IP $VM"
    Invoke-VMScript -VM $VM -HostCredential $HC -GuestCredential $GC -ScriptType bat -ScriptText $ps1   }   Set-WinVMIP $VM $HostCred $GuestCred,$Ip
  $Ip++
}
0 Kudos
LucD
Leadership
Leadership

There are in fact 2 things you have to look at:

1) the VM is started. That one is quite easy. Just check the powerstate of the VM

$vm = Get-VM MyVM 
while ($vm.PowerState -ne "PoweredOn"){   sleep 5
 
$vm = Get-VM $vm
}

2) The OS running inside the VM has to be ready as well.

You want to change the NIC settings, so you have to make sure that the OS is completely started.

This can be done in several ways: a ping, a WMI call....

The simplest method, nd least intelligent, is to just wait a number of seconds (you should determine how long by looking how long it takes the OS to start and become available)


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

Hello,

Thank you for your answer.

In fact, I think that if my script is able to check if the last machine created is started, it should be good.

Let me explain, my script creates in a first time the machines, then in a second time the starts the machines one by one.

If my script is capable of checking if the last machine is started, it would mean that all other machines have had time to start, and therefore the OS also had the time to start.

For this, I must put a condition before my loop of configuration.

If "the last machine" = "poweron"
then
"execute the following loop."

What do you think?

0 Kudos
LucD
Leadership
Leadership

Why not keep it simple and just check if all the VMs you created are powered on ?

And then add some time as a kind of grace period.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
Spartacus75
Contributor
Contributor

Yes, that's exactly what I want.

How to implement this functionality in my script?

Sorry, I'm a beginner in powershell.

Thank you very much.

0 Kudos
Spartacus75
Contributor
Contributor

Hello everyone,

I inserted your piece of code in my script "$vm = Get-VM MyVM
while ($vm.PowerState -ne "PoweredOn"){
  sleep 5
  $vm = Get-VM $vm
}
"

The problem is that it does not work, I made ​​a mistake?

Mon script:

param($Count,$Name,$Type,$Ip)

Connect-VIServer -Server 10.*.*.* -Protocol https -User admin -Password admin
2..$Count | Foreach {
  New-vm -vmhost u007.london.server.com -Name "$Name$_" -Template $Type -Datastore 007_datastore  }

2..$Count | Foreach {
  Start-VM -VM "$Name$_"}
 
$vm = Get-VM "$Name$_" while ($vm.PowerState -ne "PoweredOn"){
sleep 5  $vm = Get-VM $vm}


2..$Count | Foreach  {
$VM = Get-VM "$Name$_"
write-host "$Name$_"
$ESXHost = $VM | Get-VMHost
$secpasswd = ConvertTo-SecureString "formation" -AsPlainText -Force
$GuestCred = New-Object System.Management.Automation.PSCredential ("formation", $secpasswd)
Function Set-WinVMIP ($VM, $HC, $GC, $Ip){

$ps1= "c:\ip.bat $Ip $VM"
Invoke-VMScript -VM $VM -HostCredential $HC -GuestCredential $GC -ScriptType bat -ScriptText $ps1
}
Set-WinVMIP $VM $HostCred $GuestCred $Ip
$Ip++
}

Thank you.

0 Kudos