Deploy multiple VMs from template with PowerCLI

Deploy multiple VMs from templateI frequently need to deploy multiple VMs from a template in large numbers, most often a bunch of Citrix XenApp or XenDesktop servers following an update to the gold image. Without an automated method deployment can become a headache and would take days to deploy multiple VMs, especially when taking into account the post-deploy tasks such as activating Windows, activating Office, installing Citrix, putting the computer into the right OU etc.

Rather than consuming large amounts of paracetamol I have written a (fairly long) PowerCLI script to remove as many of the manual and mundane tasks as I could. In this blog post I am going to run through the script and explain how it works. You may download the complete script and use it “out-the-box” or make your own modifications, otherwise read on to understand how I deploy multiple VMs from template using PowerCLI.

The script currently does the following:

  • Deploys multiple VMs from template
  • Chooses the best datastores to deploy multiple VMs based on capacity and number of VMs/datastore
  • Powers on the VM
  • Puts the VM in the correct OU within AD
  • Activates Windows
  • Activates Office
  • Changes IP address
  • Starts a custom service (I used this to start IMA service which adds the VM to Citrix AppCenter)

The complete script is currently approximately 700 lines in total and you can download the complete script, however for the educational purpose of this article I will just go through some major snippets. Hopefully this will help give you some ideas or to understand the basics of how this works.

In this blog post I will cover the following aspects of the script:

  • How to deploy multiple VMs from template simultaneously
  • Keeping track of VMs as they go through the build process
  • Changing the IP address of a VM once deployed

Deploy multiple VMs from template simultaneously

To deploy multiple VMs from template simultaneously I make use of PowerShell Jobs. For a complete understanding of how this works read through ScriptingGuy’s excellent blog post. I will be using just two of these commands; Start-Job and Get-Job

Now, when invoking the Start-Job command a new hidden PowerShell instance will be launched in the background and runs the job within here. There are a few things to note;

  1. A new PowerCLI instance means a new session to vCenter, you will need to forward the current session to each instance/job you start
  2. If you have a PowerShell profile script, this script will run for each instance/job you start

I am not going to cover Multi-threading PowerCLI as this has been explained very well on the vElemental blog, I suggest you check it out for a full understanding of how this works. For now just know that I will be using two .ps1 files – the “deploy VM script” which will be invoked by what I like to call the “control script”, every time we want to deploy another VM simultaneously.

The deploy VM script

This script is simple. Other than the bit of work required in passing the vCenter session credentials over from the “control script”, all we need to do here is create a mini PowerCLI script to deploy one VM. So first, lets gather some parameters about the VM we are to deploy:

There are just a few more parameters I will need, the host/cluster I am to deploy to, the customisation spec and the true VMware folder location:

There is something important to note about customisation specs. I have set my customisation spec to use DHCP and I will be applying a static IP once the VM has deployed, powered on and been customised. One method many people have discussed is to clone your main customisation spec into a new “temporary spec”, add the IP address details into this temporary spec and delete after you have deployed a VM with it. I just didn’t get along with this method. It would be nice if there was a way we could supply the details within PowerCLI for which you are prompted for when using the vSphere Client, as far as I know this is not possible.

Finally, we need the command to deploy a VM using the parameters we have collected above.

That’s it for the deploy VM script, I told you it was simple!

The control script

Getting slightly more complicated, the control script is the “brain” of the process and, you guessed it, controls the deployment of multiple VMs as well as completing any other post-deployment tasks. Lets just cover the basics.

First we must gather some information about the VMs we are to deploy. Some information I like to “hard code” in parameters at the top of the script such as the template name, DNS servers and gateway address. Other information I like to collect interactively when at run-time such as VM names, IP addresses, number of VMs. Here is an example:

You might have already noticed, when I deploy multiple VMs they will have the same name but a different number at the end, for example; MAILSERVER01, MAILSERVER02 etc. I also deploy VMs with a sequence of adjacent IP addresses for example;, etc. This makes deployment and this script very simple because all I need to know is the first VM name, the first IP address, and how many VMs to deploy. From this information I can create a nice PowerShell array with each VM name and its IP address. Lets split up the first IP address we have collected to do this:

Now I have the IP address prefix, such as 172.16.0 and the first IP address suffix, such as 101. If I put together the prefix and the suffix I get the complete IP address: I can add one to the suffix, put them together again and have the IP address of the next VM: Simples!

Again, I need to do this with the VM name, so take the last two characters of the name and you have the first VM number (suffix), take the other characters and you have the VM name (prefix):

Keeping track of VMs as they go through the build process

Now we have collected all the info we need to deploy multiple VMs, I like to create an array which will keep track of the VMs, tasks and their status. So lets create a loop to build the VM name and the IP address using the prefixes and suffixes we created earlier and add each one to the array. I also add a few other properties for each VM to keep track of the build status, such as Clone Initiated, Cloned, Powered On and Re-IP.

This is a good start, we now have a list of VMs, their IP addresses and the status of deployment. You could modify the above to perhaps import a list from CSV, rather than ask for the VM details interactively. A good reason to do this would be if you wanted to deploy multiple VMs without following my naming convention or adjacent IP addresses.

Prepare the command to deploy a VM using the deploy VM script we created earlier. I also clear the current job list in case any are still in memory from a previous time I ran the script without reloading my PowerShell session:

Time to start deploying multiple VMs! The following loop will continue looping until all tasks have been completed, that is all the properties for each VM within $vmStatus have a value of 1. Notice in the next few examples before each task I select the VMs which have not yet had that task completed and where previous tasks have been completed using a where query on $vmStatus. If the task then completes successfully, I set that property to 1.

There are a few variables in the above example I have not yet mentioned, such as $vmsComplete.Count, $vmsCloneInitiated.Count and $runningJobCount. Lets update these

Finally, the rest of the loop to complete the remaining tasks. I have stripped out much of the code within each task to keep this looking simple for the purpose of the article.

Changing the IP address of a VM once deployed

I mentioned earlier I didn’t get along with the guest customisation method of setting the VM’s IP address. In the example above you can see where I set the IP address – one of the last things I do. I removed the code snippet for clarity in the article but here it is. Note, I also flush DNS and wait 15 seconds before continuing:


That’s all folks! As I mentioned previously I have published the entire script for download but hopefully the above will give you a better understanding of how this works than reading the script alone. There are a few extra features in the full script which are not explained above, such as selecting an appropriate datastore for each VM auto-magically, activating Windows and Office and starting a Windows service. You can disable these features using the $enableFeature parameters at the top of script.

Did you find this useful? I’d love to hear your feedback, or if you are having any issues running the script or a variant of your own leave a commend and I will do my best to help!

One thought on “Deploy multiple VMs from template with PowerCLI

  1. Hi,
    Thanks for sharing the script! I am having trouble with the job calling the deployvm script, it does not seem to have the machinename or datastore at that point. Can you explain the following?
    – when you set the job up, where are you getting all of those arguments?
    – when the job is started, more parameters are sent – haven’t they already been set up when you set up the job?
    – the deployvm script seems to be waiting for different named parameters

    Sorry it’s just that I am not experienced enough in PS to understand all of this, though I have tried. Does the script run for you?


Leave a Reply

Your email address will not be published. Required fields are marked *