VMware Cloud Community
MayurPatel
Expert
Expert
Jump to solution

Need help with modifying a VM creation PowerCLI script to support vCenter and ESXi Host

I have the below script that I have created with a lot of help from LucD - Big thank you! This has served me well for my lab rip-replace procedures until now. This script works when I run it against my single ESXi host when it is not connected to my vCenter server, but it throws up errors when I run this script when the Hosts is connected to vCenter and it fails to change the SCSI controller to Paravirtual. I think when the host is under control of vCenter some of the parameters change.

If I point it to the vCenter instead of ESXi Host, I still get the same problem. Additionally, I would also like to add the option of creating VMs in different resource pools of a vSphere cluster. My Datacenter name is LAB and my Cluster is called Local with resource pools like TestLab, Horizon View etc.

Any ideas please!

Error message:

Set-ScsiController : 10/21/2014 6:02:11 PMSet-ScsiController   The operation for the entity "W81X86" failed

with the following message: "Access to resource settings on the host is restricted to the server that is managing it:

'192.168.1.5'."

Get-ScsiController -VM $VM | where {$_.Type -eq "VirtualLsiLogicSAS"} | Set-Scs ...

~~~~~~~

+CategoryInfo     : NotSpecified: (:) [Set-ScsiController], HostAccessRestrictedToManagementServer
+ FullyQualifiedErrorId : Client20_TaskServiceImpl_CheckServerSideTaskUpdates_OperationFailed,VMware.VimAutomation

   .ViCore.Cmdlets.Commands.VirtualDevice.SetScsiController

The actual script:

# Create LAB Virtual Machines from CSV File

    Add-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue

    Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$False | Out-Null

    $ESXHost = "192.168.1.10"

  

  $DelayValue = "5000" # 5 Seconds Boot Delay

    $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec

    $vmConfigSpec.BootOptions = New-Object VMware.Vim.VirtualMachineBootOptions

    $vmConfigSpec.BootOptions.BootDelay = $DelayValue

    $vmConfigSpec.BootOptions.bootOrder += New-Object VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice

    $vmConfigSpec.flags = New-Object VMware.Vim.VirtualMachineFlagInfo

    $vmConfigSpec.flags.enableLogging = $false

    $net = new-object -ComObject WScript.Network

    $net.MapNetworkDrive("b:", "\\192.168.1.3\Build", $false, "", "")

    connect-VIServer -Server "$ESXHost" -User "root" -Password "P@ssw0rd"      

  

  

    Import-CSV B:\LabBuild\Lab.csv -UseCulture | %{

    $disks = @($_.HD1)

    if($_.HD2 -ne 0){

    $disks += $_.HD2

    $vm = New-VM -VMhost $ESXhost -Name $_.VMName -MemoryMB $_.MemoryMB -NumCPU $_.NumCPU -Version $_.Version -GuestId $_.GuestId -Floppy -Datastore $_.Datastore -DiskGB $disks -DiskStorageFormat "Thin" -resourcepool $_.RPool -Notes    $_.Notes -CD #-RunAsync

    Get-CDDrive -VM $vm | Set-CDDrive -ISOPath $_.ISOPath -StartConnected $true -Confirm:$false | Out-Null

    Get-NetworkAdapter -VM $vm | Set-NetworkAdapter -NetworkName $_.NetworkName -Type $_.NetworkType -Confirm:$False | Out-Null

    Get-FloppyDrive -VM $vm | Set-FloppyDrive -FloppyImagePath $_.FloppyPath -StartConnected $true -Confirm:$false | Out-Null

    Get-ScsiController -VM $VM | where {$_.Type -eq "VirtualLsiLogicSAS"} | Set-ScsiController -Type Paravirtual

    $vm.ExtensionData.ReconfigVM_Task($vmConfigSpec) | Out-Null

      

    }

    #This line checks the powerstate and POWER ON VM to start installation

    #$vmState = Get-VM $vm | Select-Object PowerState

   # If ($vmState -match "ff") {Get-VM $vm | Start-VM}

}

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

The Destination parameter on the Copy-DatastoreItem cmdlet takes an object that specifies a folder or a file on the datastore.

Have a look at KB2001041, it explains the cmdlet and it's parameter in more detail.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
11 Replies
LucD
Leadership
Leadership
Jump to solution

Do the Connect-VIServer to the vCenter instead of the ESXi host.

That is in fact what the error is saying, see also KB2021618


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
MayurPatel
Expert
Expert
Jump to solution

I had read the KB article you pointed out to me. I would prefer not to have to stopping the vpxa and hostd services on my ESXhost each time I want to refresh my lab virtual machines.

I tried changing the connection to my vCenter server instead of ESXi host as below:

$VCenter = "192.168.1.5"

connect-VIServer -Server "$VCenter" -User "LAB\vi-admin" -Password "P@ssw0rd"

$vm = New-VM -VMhost $vCenter -Name $_.VMName -MemoryMB $_.MemoryMB -NumCPU $_.NumCPU -Version $_.Version -GuestId $_.GuestId -Floppy -Datastore $_.Datastore -DiskGB $disks -DiskStorageFormat "Thin" -resourcepool $_.RPool -Notes

$_.Notes -CD #-RunAsync

But then it does not like the $vm = New-VM -VMhost

New-VM : 10/22/2014 4:39:09 PMNew-VM    Could not find VMHost with name '192.168.1.5'.

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:28 char:11

+ $vm = New-VM -VMhost "$VCenter" -Name $_.VMName -MemoryMB $_.MemoryMB -NumCP ...

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo: ObjectNotFound: (192.168.1.5:String) [New-VM], VimException
+ FullyQualifiedErrorId : Core_ObnSelector_SelectObjectByNameCore_ObjectNotFound,VMware.VimAutomation.ViCore.Cmdlets.Commands.NewVM  

I need to change to something else. I searched to find other similar script examples for creating mass VMs from a CSV file but most use the foreach looping which would mean I have to re-write my script and not being a scripter I find it quite daunting :smileyconfused:

Reply
0 Kudos
Zsoldier
Expert
Expert
Jump to solution

The problem is you are targeting the vCenter and not an ESXi host registered in vCenter.

Below is what I mean:

$VCenter = "192.168.1.5"

connect-VIServer -Server "$VCenter" -User "LAB\vi-admin" -Password "P@ssw0rd"

$TargetESXiHost = Get-VMHost NameofESXiServerinVCenter

$vm = New-VM -VMhost $TargetESXiHost -Name $_.VMName -MemoryMB $_.MemoryMB -NumCPU $_.NumCPU -Version $_.Version -GuestId $_.GuestId -Floppy -Datastore $_.Datastore -DiskGB $disks -DiskStorageFormat "Thin" -resourcepool $_.RPool -Notes

$_.Notes -CD #-RunAsync

Chris Nakagaki (中垣浩一)
Blog: https://tech.zsoldier.com
Twitter: @zsoldier
Reply
0 Kudos
MayurPatel
Expert
Expert
Jump to solution

Thank you very much for your guidance, I modified as you suggested and it worked perfectly.

$vm = New-VM -VMhost $TargetESXiHost -Name $_.VMName -MemoryMB $_.MemoryMB -NumCPU $_.NumCPU -Version $_.Version -GuestId $_.GuestId -Floppy -Datastore $_.Datastore -DiskGB $disks -DiskStorageFormat "Thin" -resourcepool $_.RPool -Notes

While I am on it I would like to use the above method to also be able to create mass VMs into:

1 Datacenter=LAB and Cluster =Local (has 3 esxi hosts)

2 vCenter Resource Pool(s). (In my present implementation, I deploy into different Resource Pools local to my ESX host but would like to now be able to add them into vCenter resource pools).

Is it possible to add these two options to my above basic script without needing to change it drastically?

Also is there a way to speed up the processing like being able to do it in parallel?

Message was edited by: Mayur J Patel Added a question on how to speed up the processing.

Reply
0 Kudos
Zsoldier
Expert
Expert
Jump to solution

Since DataCenter is the top-level object and clusters/vmhosts would be child members of it, you would need to query for the datacenter first then call for objects beneath.

Like:

$DataCenter = Get-DataCenter Lab

$Cluster = $DataCenter | Get-Cluster Local

$ResourcePool = $Cluster | Get-ResourcePool "Name of ResourcePool"

You would then replace then delete the -vmhost switch and simply use the -ResourcePool switch w/ the $ResourcePool variable like in the example above.

To run these jobs in parallel, you would simply need to add the -runasync switch.  That won't necessarily make it faster, but it will make submit all the jobs you want @ once and cloning will begin.  Speed all depends on the backend disk.

Chris Nakagaki (中垣浩一)
Blog: https://tech.zsoldier.com
Twitter: @zsoldier
MayurPatel
Expert
Expert
Jump to solution

Below is my modified my script following closely what you suggested. But having issues with applying the custom settings for the VM properties.

    Add-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue

    Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$False | Out-Null

     $VCenter = "192.168.1.5"

#    $ESXHost = "192.168.1.205"

#    $TargetESXiHost = "192.168.1.10"

 

    $DelayValue = "5000" # 5 Seconds Boot Delay

 

    $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec

        $vmConfigSpec.BootOptions = New-Object VMware.Vim.VirtualMachineBootOptions

        $vmConfigSpec.BootOptions.BootDelay = $DelayValue

        $vmConfigSpec.BootOptions.bootOrder += New-Object VMware.Vim.VirtualMachineBootOptionsBootableCdromDevice

        $vmConfigSpec.flags = New-Object VMware.Vim.VirtualMachineFlagInfo

        $vmConfigSpec.flags.enableLogging = $false

    connect-VIServer -Server "$VCenter" -User "LAB\vi-admin" -Password "P@ssw0rd" 

 

 

    Import-CSV G:\Build\LabBuild\Lan_LocalCluster.csv -UseCulture | %{

    $disks = @($_.HD1)

    if($_.HD2 -ne 0){

    $disks += $_.HD2

    }

# Create VM in a Cluster

     $DataCenter = Get-DataCenter Lab

     $Cluster = $DataCenter | Get-Cluster Local

     $ResourcePool = $Cluster | Get-ResourcePool "Test Lab1"

I removed the -VMHost and added the -resourcepool variable and the command looks like this and although it creates the VMs in the Resource pool, it fails to do the customization settings because once again like before it does know which host it is connected to therefore failing to set the properties.


The CSV files headers are set like this: VMName,MemoryMB,NumCpu,Version,GuestId,Datastore,HD1,HD2,ISOPath,NetworkName,NetworkType,Notes,RPool


I could do with your help here.

     $vm = New-VM -Name $_.VMName -MemoryMB $_.MemoryMB -NumCPU $_.NumCPU -Version $_.Version -GuestId $_.GuestId -Datastore $_.Datastore -DiskGB $disks -DiskStorageFormat "Thin" -Notes $_.Notes -resourcepool $_.RPool -CD -RunAsync

    Get-CDDrive -VM $vm | Set-CDDrive -ISOPath $_.ISOPath -StartConnected $true -Confirm:$false | Out-Null

    Get-NetworkAdapter -VM $vm | Set-NetworkAdapter -NetworkName $_.NetworkName -Type $_.NetworkType -Confirm:$False | Out-Null

    Get-FloppyDrive -VM $vm | Set-FloppyDrive -FloppyImagePath $_.FloppyPath -StartConnected $true -Confirm:$false | Out-Null

    Get-ScsiController -VM $VM | where {$_.Type -eq "VirtualLsiLogicSAS"} | Set-ScsiController -Type Paravirtual

    $vm.ExtensionData.ReconfigVM_Task($vmConfigSpec) | Out-Null

     

    }

Error messages

Get-CDDrive : Cannot bind parameter 'VM'. Cannot convert the

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" value of type

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" to type

"VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine".

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:37 char:21

+     Get-CDDrive -VM $vm | Set-CDDrive -ISOPath $_.ISOPath -StartConnected $true  ...

+                     ~~~

    + CategoryInfo          : InvalidArgument: (:) [Get-CDDrive], ParameterBindingException

    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevic

   e.GetCDDrive

Get-NetworkAdapter : Cannot bind parameter 'VM'. Cannot convert the

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" value of type

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" to type

"VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine".

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:38 char:28

+     Get-NetworkAdapter -VM $vm | Set-NetworkAdapter -NetworkName $_.NetworkName  ...

+                            ~~~

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

    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevic

   e.GetNetworkAdapter

Get-ScsiController : Cannot bind parameter 'VM'. Cannot convert the

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" value of type

"VMware.VimAutomation.ViCore.Util10.Task.ClientSideTaskImpl" to type

"VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine".

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:40 char:25

+     Get-ScsiController -VM $VM | where {$_.Type -eq "VirtualLsiLogicSAS"} | Set-Scs ...

+                            ~~~

    + CategoryInfo          : InvalidArgument: (:) [Get-ScsiController], ParameterBindingException

    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,VMware.VimAutomation.ViCore.Cmdlets.Commands.VirtualDevic

   e.GetScsiController

You cannot call a method on a null-valued expression.

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:42 char:5

+     $vm.ExtensionData.ReconfigVM_Task($vmConfigSpec) | Out-Null

+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : InvokeMethodOnNull

Message was edited by: Mayur J Patel Added error messages

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You are running the New-VM cmdlet with the RunAsync switch.

Then the cmdlet does not return a VirtualMachine object, but a Task object.

That is why the Get-CDDrive fails, the value on the VM parameter is not what the cmdlet expects.

If you run with the RunAsync switch, you should include a mechanism to wait till the VM is actually created.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

MayurPatel
Expert
Expert
Jump to solution

Thank you Luc, removing the -RunAsync switched fixed my issue. In the previous implementation of my script I did not use this switch and it does take quite a bit of time to create about 32 virtual machine configurations so I thought if I could find an easy way to process the script in parallel. I suppose in order to use this method I would have to add some checking logic of ensuring each of the steps have been completed? Touch bit beyond my capabilities.

My scripts now works very well and is able to now create VMs inside a vCenter managed cluster/Resourepool. Incidentally, I now receive a warning message about one of the commands I am using in my script being deprecated and has been replaced to "High" I don't quite understand how I should change this here ? Get-ScsiController -VM $VM | where {$_.Type -eq "VirtualLsiLogicSAS"} | Set-ScsiController -Type Paravirtual

WARNING: The confirm impact of Set-ScsiController cmdlet is deprecated and will be changed to "High" in a future

release.

WARNING: PowerCLI scripts should not use the 'Client' property. The property will be removed in a future release.

One last improvement I wish to add to my script is being able to copy a pre-defined "template.nvram" file which I have customized from a datastore and would like to replace it for every new VM that gets created and renamed to the name of the VM. I tried to do this by using others examples by doing this Copy-DatastoreItem vmstore:\Build\LabBuild\template.nvram $_.Datastore\$_.VMName\$vm.nvram  but it doesn't seem to work. What am I doing wrong here?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

You can easily monitor background tasks with the Get-Task cmdlet.

See for example .  Re: PowerCLI script to svMotion VM's based on a list of VM's

The warning about the Confirm switch is because you will have to add the -Confirm:$false switch on the cmdlet to avoid being prompted.

If you already have this, nothing to worry about.

See this ConfirmPreference blogpost for some background info.

Is the VM powered off when you copy the nvram file ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
MayurPatel
Expert
Expert
Jump to solution

Thanks for pointing me in the direction of an example of how to incorporate a task monitoring or waiting in a script. I will try and add it to my script.

As to your question Is the VM powered off when you copy the nvram file ?  Yes, during the VM creating process all the VM's are powered off. Below is the error message I get when I include the code for copying the "template.nvram" file. I think it almost works because I can see that it is able to rename the file with the VM name but something is not correct in my parameters.

Copy-DatastoreItem : A positional parameter cannot be found that accepts argument '\@{VMName=CS01; MemoryMB=1024;

NumCpu=1; Version=v9; GuestId=windows7Server64Guest; Datastore=vsanDatastore; HD1=20; HD2=0; ISOPath=[Host4]

ISO\MDTBuildLab_x64.iso; NetworkName=VM Network; NetworkType=vmxnet3; Notes=*** View Connection Server 1 ***;

RPool=VIEW}.VMName\CS01.nvram'.

At G:\Build\LabBuild\Lan_vSANMDTLab.ps1:41 char:2

+     Copy-DatastoreItem vmstore:\Host4\template.nvram $_.Datastore\$_.VMName\$vm.nvr ...

+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [Copy-DatastoreItem], ParameterBindingException

    + FullyQualifiedErrorId : PositionalParameterNotFound,VMware.VimAutomation.ViCore.Cmdlets.Commands.CopyDatastoreIt

   em


Line 41 is Copy-DatastoreItem vmstore:\Host4\template.nvram $_.Datastore\$_.VMName\$vm.nvram


I have placed template.nvram on the root of Host4 datastore.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The Destination parameter on the Copy-DatastoreItem cmdlet takes an object that specifies a folder or a file on the datastore.

Have a look at KB2001041, it explains the cmdlet and it's parameter in more detail.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos