Using PowerCLI to build multiple VMs

My first script that I ever wrote was a script to build VMs. I was the newest member on the team, and i was giving the task of building 50 VMs for a new project that was getting started. It was a very daunting task, due to the completion date to have these 50 VMs to be completed. So one of the other members of the team told me about PowerCLI, but didn’t have any knowledge about it. So I started to read about it, and got it installed. I started to run very simple commands, such as Get-Template just to see what it does. The more I got used to running these commands, I started to put together the script. So I started with getting all of the information that I needed to build the VM, the VM specs. So I put all of this information into a CSV file.

Using PowerCLI to build multiple VMs
Using PowerCLI to build multiple VMs

So now that I have all of the VM specs into a CSV file, I needed to import them into the script for it to use.

$vms = Import-CSV "C:\Scripts\NewVMs.csv"

So now we have the bases of a the new script. Now I need to assign some variables. So as seen above in the CSV file we have header information, Name, Template, Cluster, Datastore,
Customization, vCPU, Memory, Network, Harddrive, Harddrive2, Diskformat, and Location. This step might be able to be avoided now, but had problems with it before, so that is why I chose
to set it up this way.

#Assign Variables
$VMName = $vm.Name
$Template = Get-Template -Name $vm.Template
$Cluster = $vm.Cluster
$Datastore = Get-Datastore -Name $vm.Datastore
$Custom = Get-OSCustomizationSpec -Name $vm.Customization
$vCPU = $vm.vCPU
$Memory = $vm.Memory
$Network = $vm.Network
$Location = $vm.Location

Now that we have the variables going into the script we need to do the actually work. So the command that enables the VM to be created is New-VM. With that we can pump the most of the
variables into the command New-VM, and a new VM will be generated.

New-VM -Name $VMName -Template $Template -ResourcePool (Get-Cluster $Cluster | Get-ResourcePool) -Location $Location -StorageFormat Thin -Datastore $Datastore -OSCustomizationSpec $Custom

That only uses a portion of the variables that we collected, so lets add some more of them to get a more complete VM. So we still have the number of vCPUs, amount of memory and the Network adapter setting.

$NewVM = Get-VM -Name $VMName
$NewVM | Set-VM -MemoryGB $Memory -NumCpu $vCPU -Confirm:$false
$NewVM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $Network -Confirm:$false

At this point we have the parts for a working script to build VMs. Now just to put it all together and we’ll have a script that can build any number of VMs based on the information placed in to the CSV file.

$vms = Import-CSV "C:\Scripts\NewVMs.csv"

foreach ($vm in $vms){
#Assign Variables
$Template = Get-Template -Name $vm.Template
$Cluster = $vm.Cluster
$Datastore = Get-Datastore -Name $vm.Datastore
$Custom = Get-OSCustomizationSpec -Name $vm.Customization
$vCPU = $vm.vCPU
$Memory = $vm.Memory
$Network = $vm.Network
$Location = $vm.Location
$VMName = $vm.Name

#Where the VM gets built
New-VM -Name $VMName -Template $Template -ResourcePool (Get-Cluster $Cluster | Get-ResourcePool) -Location $Location -StorageFormat Thin -Datastore $Datastore -OSCustomizationSpec $Custom
Start-Sleep -Seconds 10

#Where the vCPU, memory, and network gets set
$NewVM = Get-VM -Name $VMName
$NewVM | Set-VM -MemoryGB $Memory -NumCpu $vCPU -Confirm:$false
$NewVM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $Network -Confirm:$false
}

In the next post we will go over how to assign the IPs, subnet mask, gateway, and update the VMware tools.

– Stuart

Find this and all of my scripts at https://github.com/NotesofaScripter/PowerCLI

15 thoughts on “Using PowerCLI to build multiple VMs”

    1. Chetan,
      The customization provides the product key, owner, organization, domain, DNS server, and any other options for the server itself. If you plan on running a Windows Sysprep on the VM after build you can remove this portion.

      – Stuart

  1. Hey Stuart,

    your script will never work because the import variable you define when loading the .csv file is called “$vms” – but in the “assign variables”-block you refer to $vm.Name, $vm.Cluster, $vm.vCPU and such.

    $vms != $vm

    I think your script would benefit from that minor correction 😉

    1. Tobias,
      In the final script, I’m using a Foreach loop, so it can build multiple VMs as listed in the CSV that is being feed into the script. So while it seems incorrect while explaining the script sections, the final script is correct.

      $vms = Import-CSV "C:\Scripts\NewVMs.csv"
       
      foreach ($vm in $vms){
      #Assign Variables
      $Template = Get-Template -Name $vm.Template
      $Cluster = $vm.Cluster
      $Datastore = Get-Datastore -Name $vm.Datastore
      $Custom = Get-OSCustomizationSpec -Name $vm.Customization
      $vCPU = $vm.vCPU
      $Memory = $vm.Memory
      $Network = $vm.Network
      $Location = $vm.Location
      $VMName = $vm.Name
      
      1. Hey stuart

        okay, that would make it work 🙂 thanks for clarifying! A little sneak peek view into the script would have helped.

        Have a good one!

  2. Hey Stuart,

    until now I have worked with a script to automatically deploy VMs following a strict guideline. In that case all VMs would be based on the same name OSCustomization, Template, use the same location, and basically be identical for most part.

    That is why I revisited your website and thought that this script is really nice actually. Having a .csv file where you can specify all details for each and every single VM probably makes more sense in most cases. Anyway since you did not directly link to your GitHub repository or the files I visited and downloaded your .csv reference file but found it to have way more parameters than are being handled by your script.

    So my question is: Do you have a script that makes use of all of those parameters or can I basically just leave out everything after ‘Network’ column (plus vCPU and Memory) and use it with your script as described here on your blog page? I mean it’s clear to me that these are parameters that one _could_ use but currently the .csv file just offers more values than the script handles so I’m just a little confused 😉

    Thanks and sorry again for my first post, that one was stupid.

  3. Stuart, you and I have never met, and probably never will. This information is years old, but I am basically a one-man IT team for a training department, overseeing 400+ VMs. THIS BLEW MY MIND AND WILL SAVE MY LIFE.

    So THANK you!

    1. Jorge,
      What kind of problems are you having with this script? If you can post some of the error messages you are getting, that will help into getting this working for you.

      -Stuart

  4. Thank you for this script! I was hoping for some help deploying Content Library Templates…

    I’m getting an error…

    “Cannot bind parameter ‘ContentLibraryItem’. Cannot convert the “” value of type “System.Management.Automation.PSCustomObject” to type “VMware.VimAutomation.ViCore.Types.V1.ContentLibrary.ContentLibraryItem”.”

    I replaced “Get-Template -Name” with “Get-ContentLibraryItem -Name” however this isn’t working.

    I tried anothjer method of piping the get-contentlibraryitem to new-vm but piping depreciated piping variables now.

    I’m currently stuck so any help would be greatly appreciated.

    *Also, I added the connect to vmware host command at the beginning of this script since its left out for some reason as well as a command to change the current working folder.*

  5. You’ll get a binding error if not deploying from a standard “Template”.

    If you are deploying a VM from an OVF (i.e. Content Library) I had to make the following modification.

    #Where the VM gets built
    Get-ContentLibraryItem -name $Template | New-VM -name $vmname -ResourcePool (Get-Cluster $Cluster | Get-ResourcePool)

    Also changed
    #Assign Variables
    $Template = $vm.Template
    $Cluster = $vm.Cluster
    $Datastore = $vm.Datastore
    $Custom = $vm.Customization

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.