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.
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?
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.
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 AM | New-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
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)
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:
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?
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
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?
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 AM | Set-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 AM | Set-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 |
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!
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 |
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?
That worked without errors, but for some reason the portgroup doesn't show connected.
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?
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!
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
