VMware Cloud Community
barnette08
Expert
Expert
Jump to solution

Create VM from Template

I am in need for some help with a simple little script that I want to start using in our lab deployments.  We typically just deploy a few VMs at a time, so nothing crazy is needed.  I am just confused about all the switches needed because I have seen a few examples online, but nothing with all of them.  Here is what I need:

- Deploy from Template

- VM Name

- Set Cluster

- Set Datastore Cluster

- Set Disk Format - Thin, Thick

- Set Folder (if needed)

- Set Customization Script

- Set Portgroup

- Set Mac Address Manually

- Set VM Annotation

- Power On

We create a lot of one off VMs for requests from our users in the lab, but clicking through the GUI is time consuming, so editing a flat file or script would be much easier since a lot of this stuff doesn't really change much.  Just the VM name and mac address are the most dynamic.

Reply
0 Kudos
1 Solution

Accepted Solutions
mattboren
Expert
Expert
Jump to solution

Hello, again-

Odd -- I would expect it to start connected by default (have "Connect at power on" enabled).  Well, no problem, we can just add another little piece to the portion that sets the NIC settings.  So, the updated line to set the NIC portgroup and MAC addr, and also set the NIC to start connected:

## set NIC portgroup and MAC addr, and set NIC to start connected
$vmNewOne | Get-NetworkAdapter |
   
Set-NetworkAdapter -PortGroup $oSomePG -Confirm:$false |
   
Set-NetworkAdapter -MacAddress $strNewMacAddr -StartConnected:$true -Confirm:$false

Just put that in place of the code that was already setting the portgroup and MAC address, and it should be all good.  Is it?

View solution in original post

Reply
0 Kudos
15 Replies
mattboren
Expert
Expert
Jump to solution

Hello, barnette08-

While you can use New-VM for most of the items, you do need to set the NIC properties and VM power state after creating the new VM.  So, something like:

## make hashtable of values for new VM creation
$hshNewVMParams = @{
    Template
= $strSourceTemplate
    Name
= $strNewVMName
   
## default "Resources" resource pool, so as to put VM in desired cluster
    ResourcePool = Get-Cluster $strSomeCluster | Get-ResourcePool -Name "Resources"
    Datastore
= Get-DatastoreCluster $strDestDStoreCluster
    DiskStorageFormat
= $strStorageFormat
    Location
= Get-Folder $strDestinationFolder
    OSCustomizationSpec
= Get-OSCustomizationSpec -Name $strOurCustSpecName
    Notes
= $strVMNotes
}
## end hsh

## create new VM
$vmNewOne = New-VM @hshNewVMParams
## set NIC portgroup and MAC addr
$vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $strSomePG -MacAddress $strNewMacAddr -Confirm:$False
## start the VM
Start-VM -VM $vmNewOne

This uses a hash table and parameter splatting to make the New-VM call short and sweet.  The bit of code assumes that you have defined the given variables that are used in the hash table before the creation of the new hash table, but that should be straight forward.  (set a template name variable, a variable for the new VM name, a variable for the destination cluster name, the value for the disk storage format, etc.).  Then, once the new VM is created, you can set the PortGroup and MAC address, and then power-on the VM.  That do it for you?

Update:  There was a further question about using this code, and I added example below of populating the variable values

To populate the variable to use this code, one could start out the script with a variable-definition section, like:

## populate things manually (this would be before the rest of the code that actually does the deployment)
#
# name of template to use
$strSourceTemplate = "win2012R2Template"
## name for new VM
$strNewVMName = "newVM0"
## name of destination cluster
$strSomeCluster = "bigCluster2"
## name of destination datastore cluster
$strDestDStoreCluster = "allFlash2"
## storage format to use for new VM
$strStorageFormat = "Thick"
## name of destination VM inventory folder
$strDestinationFolder = "WinVMs"
## name of OSCustSpec to use
$strOurCustSpecName = "WinOSCS"
## notes to add to VM
$strVMNotes = "new VM for Billy in accounting"
## name of virtual portgroup for this VM
$strSomePG = "devPG0"
## new MAC addr to use
$strNewMacAddr = "00:50:56:0f:08:04"

Then, when the rest of the script runs, the values for the variables are substituted for the appropriate commands, and eventually a new VM should result.  There are other ways to populate these variables, like, say, putting the values into a CSV, importing the CSV, and assigning the given values to the corresponding variables.  At that point, when it is time to deploy a new VM, it is just a matter of updating the CSV file (or creating a new one), and then the same code will whip out the new VM.

Message was edited by Matt Boren -- added example of manually populating variables for clarification, and changed "PortGroup" parameter to "NetworkName" parameter in the Set-NetworkAdapter call.

barnette08
Expert
Expert
Jump to solution

Thanks for taking the time to explain all of that info!

Here is the error output that I am getting:


New-VM : 11/5/2014 11:32:37 AMNew-VM

Required property datastore is missing from data object of type VirtualMachineRelocateSpecDiskLocator

while parsing serialized DataObject of type vim.vm.RelocateSpec.DiskLocator

at line 1, column 1083

while parsing property "disk" of static type ArrayOfVirtualMachineRelocateSpecDiskLocator

while parsing serialized DataObject of type vim.vm.RelocateSpec

at line 1, column 1026

while parsing property "location" of static type VirtualMachineRelocateSpec

while parsing serialized DataObject of type vim.vm.CloneSpec

at line 1, column 1015

while parsing property "cloneSpec" of static type VirtualMachineCloneSpec

while parsing serialized DataObject of type vim.storageDrs.StoragePlacementSpec

at line 1, column 324

while parsing call information for method RecommendDatastores

at line 1, column 218

while parsing SOAP body

at line 1, column 207

while parsing SOAP envelope

at line 1, column 38

while parsing HTTP request for method recommendDatastores

on object of type vim.StorageResourceManager

at line 1, column 0

At C:\Users\barned1\Desktop\create_win2k12R2.ps1:37 char:19

+ $vmNewOne = New-VM <<<<  @hshNewVMParams

+ CategoryInfo      : NotSpecified: (:) [New-VM], InvalidRequest
+ FullyQualifiedErrorId : Client20_VmServiceImpl_ApplySdrsRecommendation_ViError,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewVM

Set-NetworkAdapter : Parameter set cannot be resolved using the specified named parameters.

At C:\Users\barned1\Desktop\create_win2k12R2.ps1:39 char:52

+ $vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter <<<<  -Portgroup $strSomePG -MacAddress $strNewMacAddr -Confirm:$False

+ CategoryInfo      : InvalidArgument: (:) [Set-NetworkAdapter], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.SetNetworkAdapter

Start-VM : Cannot validate argument on parameter 'VM'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.

At C:\Users\barned1\Desktop\create_win2k12R2.ps1:41 char:13

+ Start-VM -VM <<<<  $vmNewOne

+ CategoryInfo      : InvalidData: (:) [Start-VM], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.StartVM

Here is the code I put together, from your instruction:

## populate things manually (this would be before the rest of the code that actually does the deployment)

## name of template to use

$strSourceTemplate = "Labcloud_w2k12R2"

## name for new VM

$strNewVMName = "vm-d00243"

## name of destination cluster

$strSomeCluster = "USD Cloud - Lab Cluster III (FID 30)"

## name of destination datastore cluster

$strDestDStoreCluster = "10k SAS LabCloud DSC - Cluster III"

## storage format to use for new VM

$strStorageFormat = "Thin"

## name of destination VM inventory folder

$strDestinationFolder = "Test"

## name of OSCustSpec to use

$strOurCustSpecName = "LabCloud_w2012R2"

## notes to add to VM

$strVMNotes = "emsdshared"

## name of virtual portgroup for this VM

$strSomePG = "LabCloud vNet 10.109.140 (440 /22)"

## new MAC addr to use

$strNewMacAddr = "00:50:56:2d:8c:fd"

## make hashtable of values for new VM creation

$hshNewVMParams = @{

    Template = $strSourceTemplate

    Name = $strNewVMName

## default "Resources" resource pool, so as to put VM in desired cluster

    ResourcePool = Get-Cluster $strSomeCluster | Get-ResourcePool -Name "Resources"

    Datastore = Get-DatastoreCluster $strDestDStoreCluster

    DiskStorageFormat = $strStorageFormat

    Location = Get-Folder $strDestinationFolder

    OSCustomizationSpec = Get-OSCustomizationSpec -Name $strOurCustSpecName

    Notes = $strVMNotes

} ## end hsh

## create new VM

$vmNewOne = New-VM @hshNewVMParams

## set NIC portgroup and MAC addr

$vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -Portgroup $strSomePG -MacAddress $strNewMacAddr -Confirm:$False

## start the VM

Start-VM -VM $vmNewOne

Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hmm.  Potentially related to the way that I suggested of using a datastore cluster for the Datastore value in the hashtable.  What happens if, in the hashtable, you replace the datastore line:

Datastore = Get-DatastoreCluster $strDestDStoreCluster

with

Datastore = "myDatastoreNameHere"

?  (where you put an actual datastore name in the quotes there)

Reply
0 Kudos
barnette08
Expert
Expert
Jump to solution

Sorry it has taken so long to respond, it took me some time to get available enough to do some testing on this.  It looks like, when specifying a DS within the DS Cluster it allows the script to continue - althought I would really like to use the DS Cluster if possible.

I have a few things that didn't go through as expected though.  The portgroup and mac address didn't get set and the Annotation was set as notes, not as owner.  I need to set the owner field if possible.

Network Error:

Set-NetworkAdapter : Parameter set cannot be resolved using the specified named parameters.

At C:\Users\barned1\Documents\VMware\PowerCLI\create_win2k12R2.ps1:39 char:52

+ $vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter <<<<  -Portgroup $strSomePG -MacAddress $strNewMacAddr -Confirm:$False

    + CategoryInfo          : InvalidArgument: (:) [Set-NetworkAdapter], ParameterBindingException

    + FullyQualifiedErrorId : AmbiguousParameterSet,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.SetNetworkAdapter

Annotation Notes vs. Owner:

pastedImage_0.png

Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hello-

Got it.  Yes, being able to specify a datastore cluster would be far preferable.  We'll need to see if anyone on the dev crew has any input on the topic.

As for setting the PortGroup and MacAddress -- I used the "PortGroup" parameter where I should have used "NetworkName" (different parameter sets), so the "Parameter set cannot be resolved" error.  My mistake.  I have corrected that in the Set-NetworkAdapter piece in the original code above -- it now uses NetworkName instead of PortGroup.

And, as for the Annotation / Owner part:  Notes are an annotation of a VM.  The "Owner" item in the Annotations section of the GUI is a separate, custom attribute.  To set a value for a custom attribute, we need to employ Set-Annotation.  So, to set the a value for the Owner custom attribute, you would use:

$vmNewOne | Set-Annotation -CustomAttribute Owner -Value "my owner info here"

(at some point after the new VM is already created).  How do those do for you?

Reply
0 Kudos
barnette08
Expert
Expert
Jump to solution

We're headed in the right direction for sure.  It looks like the owner still isn't getting set, but that could be how I implemented your changes.  Also the network change seems to go through and set the PG and mac, but it leaves the NIC disconnected and still gives a bit of an error.  Once we get this working, I would like to see about potentially doing more than one...but I know thats outside the scope of my original question.

$hshNewVMParams = @{

    Template = $strSourceTemplate

    Name = $strNewVMName

## default "Resources" resource pool, so as to put VM in desired cluster

    ResourcePool = Get-Cluster $strSomeCluster | Get-ResourcePool -Name "Resources"

    Datastore = "8000-10kSP1-LUN02"

    DiskStorageFormat = $strStorageFormat

    Location = Get-Folder $strDestinationFolder

    OSCustomizationSpec = Get-OSCustomizationSpec -Name $strOurCustSpecName

#    Owner = $strVMNotes

} ## end hsh

## create new VM

$vmNewOne = New-VM @hshNewVMParams

## set NIC portgroup and MAC addr

$vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $strSomePG -MacAddress $strNewMacAddr -Confirm:$False | Set-Annotation -CustomAttribute Owner -Value $strVMOwner

## start the VM

Start-VM -VM $vmNewOne

WARNING: Specifying a distributed port group name as network name is no longer supported. Use the -Portgroup parameter.

Set-Annotation : 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 C:\Users\barned1\Documents\VMware\PowerCLI\create_win2k12R2.ps1:39 char:136

+ $vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $strSomePG -MacAddress $strNewMacAddr -Confirm:$False | Set-Annotation <<<<  -CustomAttribute Owner -Value $strVMOwner

    + CategoryInfo          : InvalidArgument: (Network adapter 1:PSObject) [Set-Annotation], ParameterBindingException

    + FullyQualifiedErrorId : InputObjectNotBound,VMware.VimAutomation.ViCore.Cmdlets.Commands.SetAnnotation

Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hello, again-

Ah, yes -- I was not testing with a distributed vSwitch, which it appears that you are using.  So, we'll need to break up the Set-NetworkAdapter portion into two separate calls.  And, I was unclear on where to use the Set-Annotation call -- that was a separate line, not something to add to a pipeline.  The updated portions to set the network items and set the custom attribute value would be like this:

...hash table and variables defined already...
## create new VM
$vmNewOne = New-VM @hshNewVMParams
## set NIC portgroup and MAC addr
$vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -PortGroup $strSomePG -Confirm:$false | Set-NetworkAdapter -MacAddress $strNewMacAddr -Confirm:$false
## set the "owner" custom attribute value in the annotations
$vmNewOne | Set-Annotation -CustomAttribute Owner -Value "my owner info here"
## start the VM
Start-VM -VM $vmNewOne

And, that assumes that you defined the config variables and made the hashtable already, before running these things.  Getting there?

barnette08
Expert
Expert
Jump to solution

Yep we are definitely getting there.  We've resolved the annotation issue, but the network issue still remains.  Looks like it's still having issue finding the portgroup, setting the mac, and enabling the connected checkbox.

Sorry for posting so much output, but I wasn't sure if any of the deprecated errors would be useful. Thanks again for all your help!

Set-NetworkAdapter : 11/17/2014 10:36:40 AMSet-NetworkAdapter    Could not find VirtualPortGroupBase with name 'LabCloud vNet 10.109.140 (440 /22)'.

At C:\Users\user\Documents\VMware\PowerCLI\create_win2k12R2_new.ps1:37 char:52

+ $vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter <<<<  -PortGroup $strSomePG -Confirm:$false | Set-NetworkAdapter -MacAddress $strNewMacAddr -Confirm:$false

+ CategoryInfo      : ObjectNotFound: (LabCloud vNet 10.109.140 (440 /22):String) [Set-NetworkAdapter], VimException
+ FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_ObjectNotFound,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.SetNetworkAdapter

Set-NetworkAdapter : 11/17/2014 10:36:40 AMSet-NetworkAdapter    Value cannot be found for the mandatory parameter Portgroup

At C:\Users\user\Documents\VMware\PowerCLI\create_win2k12R2_new.ps1:37 char:52

+ $vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter <<<<  -PortGroup $strSomePG -Confirm:$false | Set-NetworkAdapter -MacAddress $strNewMacAddr -Confirm:$false

+ CategoryInfo      : NotSpecified: (:) [Set-NetworkAdapter], VimException
+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevice.SetNetworkAdapter

AnnotatedEntityId : VirtualMachine-vm-93914

AnnotatedEntity   : script-test

Name          : Owner
Value         : emsdshared
Uid          

WARNING: The 'Description' property of VirtualMachine type is deprecated. Use the 'Notes' property instead.

WARNING: The 'HardDisks' property of VirtualMachine type is deprecated. Use 'Get-HardDisk' cmdlet instead.

WARNING: The 'NetworkAdapters' property of VirtualMachine type is deprecated. Use 'Get-NetworkAdapter' cmdlet instead.

WARNING: The 'UsbDevices' property of VirtualMachine type is deprecated. Use 'Get-UsbDevice' cmdlet instead.

WARNING: The 'CDDrives' property of VirtualMachine type is deprecated. Use 'Get-CDDrive' cmdlet instead.

WARNING: The 'FloppyDrives' property of VirtualMachine type is deprecated. Use 'Get-FloppyDrive' cmdlet instead.

WARNING: The 'Host' property of VirtualMachine type is deprecated. Use the 'VMHost' property instead.

WARNING: The 'HostId' property of VirtualMachine type is deprecated. Use the 'VMHostId' property instead.

PowerState          : PoweredOn
Version             : v9
Description         : Template Updates in Windows and McAfee - 10/2/14
Notes               : Template Updates in Windows and McAfee - 10/2/14
Guest               : script-test:
NumCpu              : 2
MemoryMB            : 4096
MemoryGB            : 4
HardDisks           : {Hard disk 1}
NetworkAdapters     : {Network adapter 1}
UsbDevices          : {}
CDDrives            : {CD/DVD drive 1}
FloppyDrives        : {}
Host                : ncesxucs337
HostId              : HostSystem-host-64490
VMHostId            : HostSystem-host-64490
VMHost              : ncesxucs337
VApp                :
FolderId            : Folder-group-v92729
Folder              : Test
ResourcePoolId      : ResourcePool-resgroup-63228
ResourcePool        : Resources
PersistentId        : 501f0cda-b205-e8e9-2ea9-032b670d7ed7
UsedSpaceGB         : 21.608409518375992774963378906
ProvisionedSpaceGB  : 84.08985483087599277496337891
DatastoreIdList     : {Datastore-datastore-68663}
HARestartPriority   : ClusterRestartPriority
HAIsolationResponse : AsSpecifiedByCluster
DrsAutomationLevel  : AsSpecifiedByCluster
VMSwapfilePolicy    : Inherit

VMResourceConfiguration : CpuShares:Normal/2000 MemShares:Normal/40960

GuestId             : windows8Server64Guest
Name                : script-test
CustomFields        : {[Owner, emsdshared]}
ExtensionData       : VMware.Vim.VirtualMachine
Id                  : VirtualMachine-vm-93914
Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hello, barnette08-

Hm -- from that error it would appear that there is no virtual portgroup (standard or distributed) of name "LabCloud vNet 10.109.140 (440 /22)".  Is that definitely the name of the portgroup?  (as in, what does the following return?):

Get-VirtualPortGroup -Name "LabCloud vNet 10.109.140 (440 /22)"

If that does return a portgroup, could it be that the particular portgroup is not available on the VMHost on which this new VM resides?  Let us know.  And, glad to try to help!

Reply
0 Kudos
barnette08
Expert
Expert
Jump to solution

I get the following when running the get command for the PG.  Also all esx hosts in the cluster are connected to this dvs.  We don't have to specify the dvport as in dvportgroup-81869 do we?

Name                  Key                        VLanId PortBinding NumPorts
----                  ---                        ------ ----------- --------
LabCloud vNet 10.109.1... dvportgroup-81869                 Static  1024
Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hmm -- seems like that name is spot-on, then.  And, presumably, the new is created in that cluster where you confirmed that all of the VMHosts are connected to that vDS?

As for specifying the Key of "dvportgroup-81869", I do not think that is necessary.  So, since the Get-VirtualPortGroup test call succeeded, how does it do if you use the resultant virtual portgroup for the Set-NetworkAdapter call instead of just the portgroup name?  Like:

##...
#
# name of virtual portgroup for this VM
$strSomePG = "LabCloud vNet 10.109.140 (440 /22)"
## ADDED:  virtual portgroup object to use for this VM
$oSomePG = Get-VirtualPortGroup -Name $strSomePG
##...do other stuff, and New-VM call...##
#
# EDITED to use PortGroup object instead of just name of portgroup:
#
# set NIC portgroup and MAC addr
$vmNewOne | Get-NetworkAdapter | Set-NetworkAdapter -PortGroup $oSomePG -Confirm:$false | Set-NetworkAdapter -MacAddress $strNewMacAddr -Confirm:$false
##...do the rest...##

That gets the virtual portgroup object prior to trying to set the VM's network adapter to use it, and then uses said object.  Same behavior?

Reply
0 Kudos
barnette08
Expert
Expert
Jump to solution

That worked without errors, but for some reason the portgroup doesn't show connected. 

pastedImage_0.png

Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hello, again-

Odd -- I would expect it to start connected by default (have "Connect at power on" enabled).  Well, no problem, we can just add another little piece to the portion that sets the NIC settings.  So, the updated line to set the NIC portgroup and MAC addr, and also set the NIC to start connected:

## set NIC portgroup and MAC addr, and set NIC to start connected
$vmNewOne | Get-NetworkAdapter |
   
Set-NetworkAdapter -PortGroup $oSomePG -Confirm:$false |
   
Set-NetworkAdapter -MacAddress $strNewMacAddr -StartConnected:$true -Confirm:$false

Just put that in place of the code that was already setting the portgroup and MAC address, and it should be all good.  Is it?

Reply
0 Kudos
barnette08
Expert
Expert
Jump to solution

That did the trick,  I really appreciate the help!

I can use this as-is now, but will be searching over time about the datastore cluster thing we were talking about.  I also would be interested in running this for creating more than one VM, but without a loop because I they aren't always the same name or incremented names.  I may open a new thread on that if you suggest.  Thanks again!

Reply
0 Kudos
mattboren
Expert
Expert
Jump to solution

Hello-

Great to hear -- and it only took us a hundred posts or so.

I was going to say, "sure" for the new thread, but see that it is already rolling.

And, yes, yw -- glad to help.  Happy Holidays

Reply
0 Kudos