VMware Cloud Community
dbutch1976
Hot Shot
Hot Shot
Jump to solution

Deploy multiple VMs at the same time

Hello,

I am attempting to deploy multiple VMs at the same time using powercli.  I believe I need to had the -RunAsynch switch, howerver this does not appear to be working, instead the VMs are deploying one at a time.  Here is my code:

Connect-VIServer -Server vc1.MYDOMAIN.local -User MYDOMAIN\MYACCOUNT -Password MYPASSWORD

New-vm -vmhost prodh1.MYDOMAIN.local -Name TEST-SVR01 -Template W2K8R2SP1 -Datastore IOMEGA -OSCustomizationspec W2K8R2SP1 -Location _Tobedeleted| Start-VM -RunAsync

New-vm -vmhost prodh1.MYDOMAIN.local -Name TEST-SVR02 -Template W2K8R2SP1 -Datastore IOMEGA -OSCustomizationspec W2K8R2SP1 -Location _Tobedeleted| Start-VM -RunAsync

New-vm -vmhost prodh1.MYDOMAIN.local -Name TEST-SVR03 -Template W2K8R2SP1 -Datastore IOMEGA -OSCustomizationspec W2K8R2SP1 -Location _Tobedeleted| Start-VM -RunAsync

New-vm -vmhost prodh1.MYDOMAIN.local -Name TEST-SVR04 -Template W2K8R2SP1 -Datastore IOMEGA -OSCustomizationspec W2K8R2SP1 -Location _Tobedeleted| Start-VM -RunAsync

Start-Sleep -Seconds 300

get-vm "TEST-SVR01" | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" |?{$_.name -eq "Local Area Connection 3"} | set-vmguestnetworkinterface -Guestuser Administrator -GuestPassword "MYPASSWORD" -IPPolicy static -IP 192.168.1.25 -Netmask 255.255.255.0 -Gateway 192.168.1.1 -DNS 192.168.1.2,192.168.1.3 -RunAsync

get-vm "TEST-SVR02" | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" |?{$_.name -eq "Local Area Connection 3"} | set-vmguestnetworkinterface -Guestuser Administrator -GuestPassword "MYPASSWORD" -IPPolicy static -IP 192.168.1.25 -Netmask 255.255.255.0 -Gateway 192.168.1.1 -DNS 192.168.1.2,192.168.1.3 -RunAsync

get-vm "TEST-SVR03" | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" |?{$_.name -eq "Local Area Connection 3"} | set-vmguestnetworkinterface -Guestuser Administrator -GuestPassword "MYPASSWORD" -IPPolicy static -IP 192.168.1.25 -Netmask 255.255.255.0 -Gateway 192.168.1.1 -DNS 192.168.1.2,192.168.1.3 -RunAsync

get-vm "TEST-SVR04" | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" |?{$_.name -eq "Local Area Connection 3"} | set-vmguestnetworkinterface -Guestuser Administrator -GuestPassword "MYPASSWORD" -IPPolicy static -IP 192.168.1.25 -Netmask 255.255.255.0 -Gateway 192.168.1.1 -DNS 192.168.1.2,192.168.1.3 -RunAsync

Get-NetworkAdapter "TEST-SVR01" | Set-NetworkAdapter -NetworkName VM1 -Confirm:$false

Get-NetworkAdapter "TEST-SVR02" | Set-NetworkAdapter -NetworkName VM1 -Confirm:$false

Get-NetworkAdapter "TEST-SVR03" | Set-NetworkAdapter -NetworkName VM1 -Confirm:$false

Get-NetworkAdapter "TEST-SVR04" | Set-NetworkAdapter -NetworkName VM1 -Confirm:$false

Can anyone assist?

Thanks,

Duncan.

0 Kudos
1 Solution

Accepted Solutions
EKardinal
Enthusiast
Enthusiast
Jump to solution

Yes, you are right.

By specifying the -VM (Get-VM $modelVM) param you create the new VM from an existing one.

Just change the params of the New-VM cmdlet according to your needs and leave the -VM away.

This one creates your VMs based on a template and a customization specification.

$esxName = "prodh1.MYDOMAIN.local"

$template = "W2K8R2SP1"

$datastore = "IOMEGA"

$newVmList = "TEST-SRV01", "TEST-SRV02", "TEST-SRV03", "TEST-SRV04"

$custSpec = "W2K8R2SP1"

$location = "_Tobedeleted"

$taskTab = @{}

# Create all the VMs specified in $newVmList

foreach($Name in $newVmList) {

     $taskTab[(New-VM -Name $Name -VMHost (Get-VMHost -Name $esxName) -Template $template -Datastore $datastore -OSCustomizationSpec $custSpec -Location $location -RunAsync).Id] = $Name

}

Of course, you can write it like before. Then you have to change only the variable $newVmList in the original script.

foreach($Name in $newVmList) {

     $taskTab[(New-VM -Name $Name -VMHost "prodh1.MYDOMAIN.local" -Template" W2K8R2SP1" -Datastore" IOMEGA" -OSCustomizationSpec "W2K8R2SP1" -Location "_Tobedeleted" -RunAsync).Id] = $Name

}

You also need to insert the remaining part with the while loop and your network customizations!

http://www.lucd.info/2010/02/21/about-async-tasks-the-get-task-cmdlet-and-a-hash-table/

Regards

Emanuel

View solution in original post

0 Kudos
10 Replies
EKardinal
Enthusiast
Enthusiast
Jump to solution

Hi Duncan,

You applied the -RunAsync param to the Start-VM cmdlet. but the script is waiting for New-VM to finish. Because of this, only one deploy at a time.

Of course you can apply the -RunAsync param to the New-VM cmdlet instead, but the downside of running one or more tasks as a Job, is that you have to keep an eye on the progress.

Simple said, you can't start the VMs or apply any customization to them before the deploy is finished.

LucD has published a great script for this purpose.

About Async tasks, the Get-Task cmdlet and a hash table | LucD notes

Regards,

Emanuel

0 Kudos
dbutch1976
Hot Shot
Hot Shot
Jump to solution

Hi Eco91,

The script works great, but I'm brand new to scripting and I'm really struggling to understand a couple of things.  I want to deploy VMs from template and apply a customization to them, but this script clones from an existing VM instead.  I would imagine it's fairly simple to change this, but I don't even understand where it's getting the command to clone vs deploy, I'm assuming it's this line here:

# Create all the VMs specified in $newVmList
07foreach($Name in $newVmList){
08  $taskTab[(New-VM -VM (Get-VM $modelVm) -Name $Name -VMHost (Get-VMHost -Name $esxName) -RunAsync).Id] = $Name
09}

Is there a simple way to change this task from clone to deploy from template + apply OS customization?

Thanks!

0 Kudos
EKardinal
Enthusiast
Enthusiast
Jump to solution

Yes, you are right.

By specifying the -VM (Get-VM $modelVM) param you create the new VM from an existing one.

Just change the params of the New-VM cmdlet according to your needs and leave the -VM away.

This one creates your VMs based on a template and a customization specification.

$esxName = "prodh1.MYDOMAIN.local"

$template = "W2K8R2SP1"

$datastore = "IOMEGA"

$newVmList = "TEST-SRV01", "TEST-SRV02", "TEST-SRV03", "TEST-SRV04"

$custSpec = "W2K8R2SP1"

$location = "_Tobedeleted"

$taskTab = @{}

# Create all the VMs specified in $newVmList

foreach($Name in $newVmList) {

     $taskTab[(New-VM -Name $Name -VMHost (Get-VMHost -Name $esxName) -Template $template -Datastore $datastore -OSCustomizationSpec $custSpec -Location $location -RunAsync).Id] = $Name

}

Of course, you can write it like before. Then you have to change only the variable $newVmList in the original script.

foreach($Name in $newVmList) {

     $taskTab[(New-VM -Name $Name -VMHost "prodh1.MYDOMAIN.local" -Template" W2K8R2SP1" -Datastore" IOMEGA" -OSCustomizationSpec "W2K8R2SP1" -Location "_Tobedeleted" -RunAsync).Id] = $Name

}

You also need to insert the remaining part with the while loop and your network customizations!

http://www.lucd.info/2010/02/21/about-async-tasks-the-get-task-cmdlet-and-a-hash-table/

Regards

Emanuel

0 Kudos
dbutch1976
Hot Shot
Hot Shot
Jump to solution

Thanks so much!  Works great.

0 Kudos
dbutch1976
Hot Shot
Hot Shot
Jump to solution

Sorry, one last question, could you point me in the right direction in regards to adding the network customizations using the while loop?  It needs to wait 3 minutes after the VM has been deployed in order to allow enough time for the OS customization to run.

0 Kudos
EKardinal
Enthusiast
Enthusiast
Jump to solution

Paste your code starting at

Start-Sleep -Seconds 300

after the whole while loop. Should look like this

# Create Vms

foreach($Name in $newVmList) {

     ...

}

# Start each VM that is completed

$runningTasks = $taskTab.Count

while($runningTasks -gt 0) {

     ...

}

# START HERE

# Wait for OS Customization

Start-Sleep -Seconds 300

# Customize network

get-vm "TEST-SRV01" | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" | ? ...............

Regards

Emanuel

0 Kudos
dbutch1976
Hot Shot
Hot Shot
Jump to solution

Thanks so much.

I'm very close, the VMs are deploying, but the network customizations are all failing.  Here is the code:

$esxName = "ESXHOST1.DOMAIN.local"
$template = "W2K8R2SP1"
$datastore = "IOMEGA"
$newVmList = "SRV01", "SRV02", "SRV03", "SRV04"
$custSpec = "W2K8R2SP1"
$location = "_Tobedeleted"
$taskTab = @{}
Connect-VIServer -Server vc1.DOMAIN.local -User DOMAIN\ADMINISTRATOR -Password MYPASSWORD
# Create all the VMs specified in $newVmList
foreach($Name in $newVmList) {
     $taskTab[(New-VM -Name $Name -VMHost (Get-VMHost -Name $esxName) -Template $template -Datastore $datastore -OSCustomizationSpec $custSpec -Location $location -RunAsync).Id] = $Name
}

# Start each VM that is completed
$runningTasks = $taskTab.Count
while($runningTasks -gt 0){
Get-Task | % {
  if($taskTab.ContainsKey($_.Id) -and $_.State -eq "Success"){
   Get-VM $taskTab[$_.Id] | Start-VM
   $taskTab.Remove($_.Id)
   $runningTasks--
  }
  elseif($taskTab.ContainsKey($_.Id) -and $_.State -eq "Error"){
   $taskTab.Remove($_.Id)
   $runningTasks--
  }
}
Start-Sleep -Seconds 15
}


# START HERE 
# Wait for OS Customization 
Start-Sleep -Seconds 300 
# Customize network
Get-NetworkAdapter -Name $Name | Set-NetworkAdapter -NetworkName VM2 -Confirm:$false] = $Name
get-vm -Name $Name | Get-VMGuestNetworkInterface -Guestuser Administrator -GuestPassword "MYPASSWORD" |?{$_.name -eq "Local Area Connection 3"} | set-vmguestnetworkinterface -Guestuser Administrator -GuestPassword "MYPASSWORD" -IPPolicy static -IP 192.168.1.155 -Netmask 255.255.255.0 -Gateway 192.168.1.1 -DNS 192.168.1.2,192.168.1.3]

0 Kudos
EKardinal
Enthusiast
Enthusiast
Jump to solution

$Name is defined only within the foreach. You can't use it outside.

To customize the network, you have two possibilities.

-- The simple one Smiley Happy

Enter for every VM the appropriate command into the script (Set-NetworkAdapter and Set-VMGuestNetworkInterface) => Just copy/paste and adjust the ip addresses.

This will get very unhandy with increasing VM count.

-- The complex one

Adjust the $newVmList to an array, with a hash table for each vm containing the network data, and loop through (same as you did before).

With this method, you also could provide the VM data from a csv file or an other source.

But you should get used with hash tables, otherwise it could be confusing.

Windows PowerShell Tip: Working with Hash Tables

$newVmList = @(

    @{"Name" = "SRV01"; "IP" = "192.168.1.100"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; },

    @{"Name" = "SRV02"; "IP" = "192.168.1.101"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; },

    @{"Name" = "SRV03"; "IP" = "192.168.1.102"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; },

    @{"Name" = "SRV04"; "IP" = "192.168.1.103"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; }

)

Then you have to make a small change to the New-VM foreach. Should then look like this

foreach($VM in $newVmList) {

    $taskTab[(New-VM -Name $VM.Name -VMHost (Get-VMHost -Name $esxName) -Template $template -Datastore $datastore -OSCustomizationSpec $custSpec -Location $location -RunAsync).Id] = $VM.Name

}

After the Start-Sleep -Seconds 300, you could loop through the $newVmList again to customize the network for each VM.

foreach($VM in $newVmList) {

    Get-NetworkAdapter -VM $VM.Name | Set-NetworkAdapter -NetworkName $VM.NetworkName -Confirm:$false

    Get-VM -Name $VM.Name | Get-VMGuestNetworkInterface -Guestuser "Administrator" -GuestPassword "MYPASSWORD" | ? { $_.name -eq "Local Area Connection 3" } | Set-VMGuestNetworkInterface -Guestuser "Administrator" -GuestPassword "MYPASSWORD" -IPPolicy static -IP $VM.IP -Netmask $VM.Netmask -Gateway $VM.Gateway -DNS $VM.DNS

}

Regards

Emanuel

dbutch1976
Hot Shot
Hot Shot
Jump to solution

That's got it.  Just in case anyone else is trying to do the same thing here's the final code:

$esxName = "prodh1.MYDOMAIN.local"
$template = "W2K8R2SP1"
$datastore = "IOMEGA"
$newVmList = @(
    @{"Name" = "SRV01"; "IP" = "192.168.1.100"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; }, 
    @{"Name" = "SRV02"; "IP" = "192.168.1.101"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; }, 
    @{"Name" = "SRV03"; "IP" = "192.168.1.102"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; }, 
    @{"Name" = "SRV04"; "IP" = "192.168.1.103"; "Netmask" = "255.255.255.0"; "Gateway" = "192.168.1.1"; "DNS" = @("192.168.1.2", "192.168.1.3"); "NetworkName" = "VM2"; }
)
$custSpec = "W2K8R2SP1"
$location = "_Tobedeleted"
$taskTab = @{}
Connect-VIServer -Server VIRTUALCENTERSERVER.MYDOMAIN.local -User MYDOMAIN\MYUSER -Password MYPASSWORD
# Create all the VMs specified in $newVmList
foreach($VM in $newVmList) {
     $taskTab[(New-VM -Name $VM.Name -VMHost (Get-VMHost -Name $esxName) -Template $template -Datastore $datastore -OSCustomizationSpec $custSpec -Location $location -RunAsync).Id]=$VM.Name
}

# Start each VM that is completed
$runningTasks = $taskTab.Count
while($runningTasks -gt 0){
Get-Task | % {
  if($taskTab.ContainsKey($_.Id) -and $_.State -eq "Success"){
   Get-VM $taskTab[$_.Id] | Start-VM
   $taskTab.Remove($_.Id)
   $runningTasks--
  }
  elseif($taskTab.ContainsKey($_.Id) -and $_.State -eq "Error"){
   $taskTab.Remove($_.Id)
   $runningTasks--
  }
}
Start-Sleep -Seconds 15
}


# START HERE 
# Wait for OS Customization 
Start-Sleep -Seconds 1020
# Customize network
foreach($VM in $newVmList) { 
    Get-NetworkAdapter -VM $VM.Name | Set-NetworkAdapter -NetworkName $VM.NetworkName -Confirm:$false 
    Get-VM -Name $VM.Name | Get-VMGuestNetworkInterface -Guestuser "Administrator" -GuestPassword "MYPASSWORD" | ? { $_.name -eq "Local Area Connection 3" } | Set-VMGuestNetworkInterface -Guestuser "Administrator" -GuestPassword "MYPASSWORD" -IPPolicy static -IP $VM.IP -Netmask $VM.Netmask -Gateway $VM.Gateway -DNS $VM.DNS 
}

One change I had to make was to crank the sleep time way up in order to allow the OS Customization to run.

Thanks so much for all your help Emanuel!!

0 Kudos
gaurang00
Contributor
Contributor
Jump to solution

Hi,

I know this is too late to start this again, but I just started working on an assignment and I've to deploy 300 VMs and I am worst at scripting.

Here's my case:
I have got an ESXi server, say "TEST1", in which I've created a windows10 master image say "MASTER-W10" and I need to create 300 images of this, with customization of hostname, IP configs etc.
from the code below,

I've the esx name, datastore (which I am assuming "storage" that I see under the resources when I go the summary tab). what would be the template name?

is it gonna be the same as name of Master virtual machine i.e. "MASTER-W10" ??

Also, what does the $custSpec and $location mean here?

FYI, I am accessing ESXi directly through vSphere Client (not vCenter).

0 Kudos