Automate bulk Windows and Linux VMs creation from template with Guest OS customization

Share this:

VMware vSphere Infrastructure build completed, tested and ready for workloads. What’s next? Right, it’s time to create bunch of Windows and Linux VMs for customers! Fastest way to do so – prepare VMware PowerCLI / PowerShell script.

In my case as part of the project I had to build 200+ VMs. Of course we can use Templates in vSphere and Guest OS customization during new VM deployment from Template, still this is boring task… Let’s automate it and create all of them with single PowerCLI / PowerShell script using CSV file as input for VM names and the rest VM specific parameters.

VM list file contains all necessary custom parameters specific for each VM like:

  • VM name
  • Name of the template which used to clone to VM
  • Specification name to customize common VM configurations for given OS type
  • Name of the Cluster in vSphere where to put new VM
  • Name of the Folder in vSphere where to put new VM
  • Datastore name for VM
  • Storage policy (in my case we have stretched vSAN configuration and several vSAN policies for different VM types)
  • Amount of vCPU
  • Amount of RAM
  • System disk size
  • IP address for VM
  • Subnet Mask
  • Default Gateway IP
  • DNS IP address
  • vSphere Port Group name (or NSX Segment name)

Here is example of such CSV file (vm-list.csv):

vmName,templateName,specName,clusterName,vmFolderName,datastoreName,storagepolicyName,numCpu,memSizeGB,systemDiskSizeGB,ipAddr,ipMask,ipGateway,ipDns,portgroupName
testwin01,win2019dc-withagents-template,Windows2019-without-domain,Cluster,TST,vsanDatastore,vSAN-Stretched,8,16,200,10.100.1.11,255.255.255.0,10.100.1.1,10.100.1.2,nsx-segment-10.100.1.0
testwin02,win2019dc-withagents-template,Windows2019-without-domain,Cluster,TST,vsanDatastore,vSAN-Stretched,8,16,200,10.100.1.12,255.255.255.0,10.100.1.1,10.100.1.2,nsx-segment-10.100.1.0
testlnx03,redhat92-withagents-template2,Linux-RedHat-9.2-Customization,Cluster,TST,vsanDatastore,vSAN-Stretched,4,8,200,10.100.1.13,255.255.255.0,10.100.1.1,10.100.1.2,nsx-segment-10.100.1.0
testlnx04,redhat92-withagents-template2,Linux-RedHat-9.2-Customization,Cluster,TST,vsanDatastore,vSAN-Stretched,4,8,200,10.100.1.14,255.255.255.0,10.100.1.1,10.100.1.2,nsx-segment-10.100.1.0

VM Customization specification example for Windows and Linux:

Storage policies example:

Once all pre-requisites are done you can run the script. If you run it without parameters, you get the help

Let’s run it once again with csv file as parameter

Ensure CSV file imported correctly and all parameters fit to your needs and press “Enter”

Authenticate to vCenter with account which has admin permissions to vSphere. Make a tea or coffee or even go to lunch (of you have tens or hundreds of VMs to deploy) and relax while script works instead of you.

Once completed you will see your VMs created according to specification, Powered On, connected to the network and IP configuration applied.

Similar result for Linux VMs:

Here is the script. Fill free to update it to your needs if you want.

######################################################################
##### Deploy VMs from VM_Template with guest OS customization    #####
##### (c) Yevgeniy Steblyanko                                    #####
######################################################################

$vCenterName = "vc01.lab.local"           # vCenter FQDN or IP

$param = $args[0]
if ($param) {
  # if parameter has file name defined, continue
  Write-Host "Importing file: " $param
  $vmParametersTable = Import-Csv -Path $param -Delimiter ","
  $vmParametersTable | Format-Table * | Out-String -Width 500
  Read-Host "Press ENTER to continue..."

  $vc = Connect-VIServer -Server $vCenterName
  if ($vc) {
    Foreach ($vmParameters in $vmParametersTable)
    {
      Write-Host -foregroundcolor "Green" "`nDeploying VM: " $vmParameters.vmName
      $template = Get-template -Name $vmParameters.templateName
      $spec = Get-OSCustomizationSpec -Name $vmParameters.specName
      $cluster = get-cluster $vmParameters.clusterName
      $datastore = Get-Datastore $vmParameters.datastoreName
      $storagePolicy = Get-SpbmStoragePolicy $vmParameters.storagepolicyName
      $tempCustSpecName = "TempCustomSpec-to-be-deleted-after-vm-deployment-" + $vmParameters.vmName + $(Get-Date -Format yyyy-MM-dd-HH-mm-ss)

      Get-OSCustomizationSpec -Name $vmParameters.specName | New-OSCustomizationSpec -Name $tempCustSpecName
      if ($vmParameters.specName -Match "linux")
        {
          $specTemp = Get-OSCustomizationSpec -Name $tempCustSpecName | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $vmParameters.ipAddr -SubnetMask $vmParameters.ipMask -DefaultGateway $vmParameters.ipGateway
          Write-Host "`nDNS is not applied on Linux, make it manually"
        }
      elseif ($vmParameters.specName -Match "windows")
        {
          $specTemp = Get-OSCustomizationSpec -Name $tempCustSpecName | Get-OSCustomizationNicMapping | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $vmParameters.ipAddr -SubnetMask $vmParameters.ipMask -DefaultGateway $vmParameters.ipGateway -Dns $vmParameters.ipDns
        }
      else {
        Write-Host -foregroundcolor "Red" "`nOS type not recognized from OS Customization string"
      }
      if ($specTemp)
      {
        $spec = Get-OSCustomizationSpec -Name $specTemp.spec
        $vm = New-Vm -Name $vmParameters.vmName -Template $template -OSCustomizationSpec $spec -ResourcePool $cluster -Location $vmParameters.vmFolderName -Datastore $datastore
        # if VM created successfully, update vCPU, RAM, Storage size
        if ($vm) {
          $vm | Set-Vm -MemoryGB $vmParameters.memSizeGB -NumCpu $vmParameters.numCpu -Confirm:$false
          $vm | Set-Vm -StoragePolicy $storagepolicy -Confirm:$false
          $portgroup = Get-VDPortgroup -Name $vmParameters.portgroupName
          $vm | Get-NetworkAdapter | Set-NetworkAdapter -Portgroup $portgroup -Confirm:$false
          $vm | Get-HardDisk | Set-HardDisk -CapacityGB $vmParameters.systemDiskSizeGB -Confirm:$false

          $spec | Remove-OSCustomizationSpec -Confirm:$false
          Write-Host -foregroundcolor "Green" "`nVM deployed: " $vmParameters.vmName
          Start-VM -VM $vm -RunAsync
        }
        else {
          Write-Host -foregroundcolor "Red" "`nVM create error"
        }
      }
    }
    Disconnect-VIServer -Server $vCenterName -Confirm:$False
  }
  else {
    Write-Host -foregroundcolor "Red" "`nvCenter connect error"
  }
}
else {
  # if parameter not defined, show error and stop
  Write-Host -foregroundcolor "Red" "`nMissing parameter"
  Write-Host "define <file>.csv as parameter, example:"
  Write-Host "bulk-vms-creation-from-template.ps1 vm-list.csv"
}

Script was tested and used for Windows 2019 and RedHat Linux 9.2 creation.

The following two tabs change content below.

Yevgeniy Steblyanko

Yevgeniy Steblyanko is an Infrastructure Architect/SME with experience in virtualization area for more than 15 years. His areas of interest are VMware vSphere, vSAN, NSX, automation on PowerCLI/PowerNSX. He has VMware certifications: VCIX-DCV, VCIX-NV.

About Yevgeniy Steblyanko

Yevgeniy Steblyanko is an Infrastructure Architect/SME with experience in virtualization area for more than 15 years. His areas of interest are VMware vSphere, vSAN, NSX, automation on PowerCLI/PowerNSX. He has VMware certifications: VCIX-DCV, VCIX-NV.
Bookmark the permalink.

2 Comments

  1. Hi Yevgeniy Steblyanko,
    I try the script and follow the step to create VM Customization Specifications, but I found the Linux RedHat 9 do not have hostname, IP address, subnet mask and gateway, I also use ESXi 8 and Redhat 9.3 to test

    Is there something wrong?

    Thank You

  2. Yevgeniy Steblyanko

    Hi Mic,
    Please check if you have following linux components installed: perl and dbus-tools.
    In case both installed then show output on the step like this:
    https://i0.wp.com/thevirtualist.org/wp-content/uploads/2024/07/image-4.png?w=1643&ssl=1

    Best regards,
    Yevgeniy

Leave a Reply

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