Skip to content

How to create Virtual Machines from CSV with PowerCLI on VMware

Whenever it’s possible I like to automate whatever I can to release myself for other activities and save a huge time that would be wasted on repeatable or time-consuming tasks.

I have been using pieces of the following script for a long time, in a “manual automation” way, let’s say. Getting blocks and loops of scripts and creating and configuring virtual machines as needed.

In the last month I put together in a way that can be shared, and even if you don’t have much experience with powershell, you can still make use of it. 🙂


Install PowerCLI Module

The offline installation is available here.

Virtual Machine Template

Create a virtual machine template to be used as a base image for the deployment. No special requirements here, create accordingly to your environment.

VM Customization Specifications

Create two virtual machine customization specifications on vCenter (Linux and Windows). The only requirement is to set DNS under network section for the Linux, otherwise you might have issues after the deployment within the guest os.

CSV File

That’s the source file with all virtual machine details. There must be certain columns with specific values. These values are shown below:

  • Name
  • vCPU
  • vRAM
  • vNetwork
  • vDisk2 ..N
  • IP
  • Subnet
  • Gateway
  • Datastore
  • VMFolder
  • Specs
  • Template
  • Notes
  • OSType
  • Dns1
  • Dns2

You can download an example spreadsheet in the link below.

Each line will be specific to a virtual machine. That’s how the loop will read and create/configure accordingly.

vDisks columns can be increased as many required (vDisk2, vDisk3, vDisk4, etc.). For those VMs which doens’t use the additional disks, put “0” where not required. It starts at vDisk2 to be easily identified later, as vDisk1 will be the OSDisk from template. The vDisk format can be also changed, line 63 in the script.

The OSType value should be either “Windows” or “Linux“, that’s just a condition in order to properly run the VM creation per GuestOS.

Running the script it will always prompt for the CSV file, insert the full path location.

The next step will prompt vCenter credentials, once confirmed it won’t be prompted again if you run the script on the same powershell session.

The script then starts to read each value and deploy the VMs accordingly:

After initial deployment, the script will configure the VMs, in that section custom settings are configured, like vCPU, vRAM, vNetwork, etc.

At the end you should see a message of deployment complete and the VMs will be powered on, and probably customization specifications will be running. After that the virtual machines will be ready for use and fully customized.

Check the full script below, save as *.ps1 and use Powershell ISE or Powershell to run.

Enjoy 🙂

Published inPowerShellvSphere


  1. Gab Gab

    Hello Rafael,
    your script works like a charm but I have 3 issues:
    1. Where can I insert a parameter for network card to start and stay connected at power on, curently the vm’s network starts with network card disconnected.
    2. IPv6 – I need this disabled, tried some modifications but doesn’t work for me
    3. Despite the IPv4 has the correct settings in network connection, when I run ipconfig – output is

    I would appreciate any advice.

    • Gab Gab

      Well, I figure out all the above issues 🙂
      1. Added the following string at the end of your script to resolve the network connection status : Get-VM $vm.Name | Get-NetworkAdapter -Name “Network adapter 1” | Set-NetworkAdapter -StartConnected:$true -Confirm:$false | Out-Null
      2. IPv6 disabled with invoke command
      3. I had duplicate IPs 🙂

      • Hey Gab,

        Thanks for the feedback, I’m glad that you found the solution and appreciate it for sharing it.
        That’s a good point, will add that in the script 🙂

  2. Maurice Maurice

    Does this work with Host Clusters and Datastore clusters? So Cluster names would go where Host name and Datastore name goes in the csv? Or would script need to be changed?

    • Hey Maurice, Datastore cluster works fine as well, just make sure that SDRS is in automated mode.

  3. Maurice Maurice

    2 issues so far. Our port group names are the VLAN numbers. so vlan 300 is call portgroup 300. The script bombs out on that. Also it seems like it doesn’t like spaces for the template names. So if the template name is Windows 2019 DE it also fails. Any changes I can make besides renaming our port groups and template names in VCenter?

    • Hey Maurice, that’s a good point, I’ve never tested with only number in the port group number neither spaces in any name.

      Let me do some tests and will reply you back, for now, removing the spaces and renaming it would be the easier temp solution.


  4. Ryan Ryan

    Hello Rafael,

    That is a nice script.
    I am getting an error on the last part of the script. I think it is the part you just added. Could you also tell me what and where I need to add to disable IPv6? Also if I add a value for vDisk1 will it change the hard disk 1 size?

    WARNING: The ‘Version’ property of VirtualMachine type is deprecated. Use the ‘HardwareVersion’ property instead.
    Get-NetworkAdapter : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
    At scriptlocation.ps1:87 char:23
    + … M $vm.Name | Get-NetworkAdapter -Name “Network adapter 1” | Set-N …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (servername:PSObject) [Get-NetworkAdapter], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.GetNetworkAdapter

    Get-NetworkAdapter : 5/9/2023 2:20:02 PM Get-NetworkAdapter NetworkAdapter with name ‘“Network’ was not found using the specified filter(s).
    At scriptlocation.ps1:87 char:23
    + … M $vm.Name | Get-NetworkAdapter -Name “Network adapter 1” | Set-N …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (:) [Get-NetworkAdapter], VimException
    + FullyQualifiedErrorId : Core_OutputHelper_WriteNotFoundError,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.GetNetworkAdapter

    • Hi Ryan, you’re right! I have just removed the new line added to not affect the script and will work on that and update it again later.

      Regarding the vDisk1, the answer is no, as that one comes with the Template itself, and should be set in advance within the template.

      To disable IPv6 you can use Disable-NetAdapterBinding cmd, but let me know what you’re trying to achieve and I can help you.

Leave a Reply

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