I have been working for a while now on a VM creation script for a customer, to allow them to add a VM through either a CSV import or through a GUI. The GUI is written in another language, and relies on capturing the Stdout from the powerCLI script and outputting to a edit control in the GUI so the technicians can follow the progress. Everything thus far is working just fine.
I had an addendum request that I add some sort of progress message to tasks that take a little longer, such as the New-VM call so the technicians (lower level) can see that the script is not hanging. I know that when running the PowerCLI script directly you get the typical powershell progress bar. I am wondering if there is any way to pull out of that just the percentage of completion, so I can output to the external GUI.
I could use something like Write-Progress if I were calling New-VM in a loop against a number of machines, but in the case of a single VM creation it doesn't look like it will do the job. I also looked at Wait-Task, but that does not seem output anything that I can capture. I would be interested if anyone has any suggestions.
No, it's because you are not specifying the RunAsync switch.
Then the New-VM cmdlet will not return a Task object.
Add a RunAsync = $true to the splat hash table.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Can you add the -runasync parameter to the New-VM cmdlet and grab the output, which is a task? Then you can check percentcomplete:
$task = new-vm -name test3 -runasync [otheroptions here]
WriteOutput $task.PercentComplete
The percent complete should update as the task continues, so you may want to add a sleep/poll cycle or whatever makes sense for your application.
And by the way the new VM would be in the $task.Result object, assuming the task completes successfully.
I'm afraid a Task object doesn't refresh automatically, so you'll have to do this in a While-loop.
The progress can be displayed from the Task object's '% Complete' property.
Something like this (you can eventually place a 'sleep' before the Get-Task in the While-loop
$t = New-VM -Name NewVM -Template stress -Datastore MyDS -VMHost MyEsx -RunAsync
while('Running','Queued' -contains $t.State){
Write-Progress -Activity 'Creating VM' -PercentComplete $t.PercentComplete
$t = Get-Task -Id $t.Id
}
Write-Output "Task '$($t.Description)/$($t.Id)' for $(Get-View -Id $t.ObjectId -Property Name | select -ExpandProperty Name) ended with status $($t.State)"
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I think this may be a difference in PowerCLI versions, but your script doesn't work because the $t.ObjectId is a GUID and the get-task -Id parameter wants a MOID. I ran into the same problem when writing my first response. I tested on 6.5R1 and 6.5.3 and got the same result, but noticed that the task is auto-refreshing.
Example error from your script:
Get-Task : 12/11/2017 1:26:10 PM | Get-Task | The identifier 9b29eecf-fa92-4799-968f-8caf0be408db resulted in no objects. |
At newvmtest.ps1:9 char:10
+ | $t = Get-Task -Id $t.Id | |
+ | ~~~~~~~~~~~~~~~~~~ | |
+ CategoryInfo | : ObjectNotFound: (:) [Get-Task], VimException | |
+ FullyQualifiedErrorId : Client20_OutputTracker_ReportNotFoundLocators_LocatorNotProduced,VMware.VimAutomation.Vi |
Core.Cmdlets.Commands.GetTask
Get-View : Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not
null or empty, and then try the command again.
At newvmtest.ps1:13 char:68
+ ... k '$($t.Description)/$($t.Id)' for $(Get-View -Id $t.ObjectId -Proper ...
+ | ~~~~~~~~~~~ | |
+ CategoryInfo | : InvalidData: (:) [Get-View], ParameterBindingValidationException | |
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInte |
rop.GetVIView
This works, however:
$t = New-VM -Name test11 -VMHost host -RunAsync
Write-Output $t.PercentComplete
while($t.PercentComplete -ne 100) {
Write-Output $t.PercentComplete
Start-Sleep -s 5
}
Write-Output $t.PercentComplete
Sample Output:
0
0
100
Not sure what exactly you are doing, but there is no auto-update on properties.
See the about_runasync text "...This state is not automatically updated and has one of the following values - Error, Queued, Running, and Success. To refresh the task status, pass the Task object to the Get-Task cmdlet...."
Also the Get-Task cmdlet is expecting a [String] representation of the Task MoRef on the Id parameter, and the Id property of a returned Task object is exactly that.
And it is not a PowerCLI version thing either.
I checked the Id parameter and the Id property all the way back to PowerCLI 5.5, it is a [string] in all those versions.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The issue isn't the type, but that the value passed back in the id field is a GUID (in string format). So when you pass that into the -id parameter of the get-task call it doesn't find anything. Passing the object results in the same problem.
What version of vSphere are you running? I'm on 6.5 Update 1b - build 6816762
So maybe it's a bug there returning the wrong value for ID, thus breaking this chain. This would be on the server side vs. client side.
As for why the PercentComplete updates itself... I assure you that I haven't hacked anything to cause this to happen and if I open the vSphere client side by side with the script, the status corresponds to what I receive in the task.PercentComplete value. If I take off the sleep, I can see several incremental updates... 2.5, 50, 100, etc.
Everything that I'm doing is in the scripts I posted.
Thanks for the suggestions. I get an error about ID being null or empty, whether I run it directly from PowerCLI or through the GUI. Out of curiosity, would this be due to it being contained in a try/catch block? Or perhaps the use of splat? Also, I have noticed (and this may well just be a limitation of the GUI script) that only what I write out with Write-Host is properly captured. Write-Output is often sporadic in whether it is caught. I will have to anonymize the code a bit to show the entire build function, but the relevant piece is below:
#Section 1 determine single datastore or cluster
...
#Section 2 If not in A.D. add
...
#Section 3 Create Customization Spec
...
#Section 4 Configure NIC Mappings on Customization Spec
...
#Section 5 Create the VM
Write-Host "Creating the New VM....."
Start-Sleep -Milliseconds 100
$sVM = @{
Name = $oVM.Name
Template = Get-Template $oVM.Template -Location $oVM.Site
DataStore = $sDStoreOutput
OSCustomizationSpec = $oVM.Name
}
$esx = Get-VMHost -Name $oVM.Host -ErrorAction SilentlyContinue
$cluster = Get-Cluster -Name $oVM.Host -ErrorAction SilentlyContinue
if($esx){
$sVM.Add('VMHost',$esx)
}elseif($cluster){
$sVM.Add('ResourcePool',$cluster)
}else{
Write-Host "Cannot find Host or Cluster, please check spelling"
}
Try {
$t = New-VM @sVM
While('Running','Queued' -contains $t.State) {
Write-Progress -Activity 'Creating VM' -PercentComplete $t.PercentComplete
$t = Get-Task -Id $t.ID
}
Write-Host "New VM" $oVM.Name "created"
Start-Sleep -Seconds 5
} catch [Exception] {
Write-Host "Unable to create VM. Error is: " $Error
Exit
}
The VM is created, though I see no output as to progress, but then it fails with the error below. It seems as if the object is never created, or at least the script cannot access the ID member:
Unable to create VM. Error is: Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
No, it's because you are not specifying the RunAsync switch.
Then the New-VM cmdlet will not return a Task object.
Add a RunAsync = $true to the splat hash table.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Bah, sorry I missed that part. That does it. Thanks again
I'm on the same vSphere version.
Look at the following example.
After the task completes, the Task object still shows running and 0%
And the Id field is not a Guid, but a string representation of a MoRef to a Task object
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
That is odd. What I ended up doing was just grabbing the PercentComplete piece like so:
Try {
$t = New-VM @sVM -RunAsync
While('Running','Queued' -contains $t.State) {
Write-Host "...... $($t.PercentComplete)%"
Start-Sleep 20
$t = Get-Task -Id $t.ID
}
Write-Host "...... 100%"
Write-Host "New VM" $oVM.Name "created"
Start-Sleep -Seconds 5
......
Seems to work well in both PowerCLI and the GUI.
OK so the reason for the difference is that I was not cloning from template, but simply creating a new VM. PowerCLI returns a different type of task object depending on which route you take, either a client side task or (presumably) a server side task, although it is not named as such.
Here is a call to just create a VM:
PS> $t = New-VM -Name test14 -VMHost host -RunAsync
PS > $t.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ClientSideTaskImpl VMware.VimAutomation.Sdk.Util10.Task.ClientSideTaskImpl
Here is a call to clone (note it is intentionally a failure by calling to create a VM of a pre-existing name to make it happen more quickly):
PS> $x = New-VM -Name test14 -Template template -VMHost host -RunAsync
PS > $x.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False TaskImpl VMware.VimAutomation.ViCore.Impl.V1.VIObjectImpl
As you can see, the object types are different between the two calls to the same cmdlet. Why there is a difference in these two, I can only surmise the reason being that the PowerCLI devs understand that some tasks run faster than others, and created a ClientSideTask implementation to handle this (i.e. automatically updates itself because of the quick return).
You wouldn't expect it given that the vSphere Web Services API handles a VM creation using a call to CreateVM_Task() which returns a Managed Object reference to a task. It handles a clone operation in the same manner, with a call to CloneVM_Task() which also returns a Managed Object Reference to a task. In both cases, if you are coding against the API directly, you have to periodically check in with the server to get the task information using the Managed Object Reference to the task.
PowerCLI decided for some reason to make a difference between these two calls and return different object types, although I am sure their implementation is exactly as I described above.
So using the above example of $t being a CreateVM_Task (client side) and $x being a CloneVM_Task (server side, for lack of a better term), here are the properties:
PS > $t | select *
Client :
CmdletTaskInfo :
ExtensionData :
Id : 1d20d6be-e872-4a46-8a8d-83b0c9d71e79
IsCancelable : False
Result : test14
Name : CreateVM_Task
Description : Create virtual machine
State : Success
PercentComplete : 100
StartTime : 12/11/2017 2:28:51 PM
FinishTime : 12/11/2017 2:28:52 PM
ObjectId :
Uid : /Local=/ClientSideTask=1d20d6be-e872-4a46-8a8d-83b0c9d71e79/
NonTerminatingErrorList : {}
TerminatingError :
PS > $x | select *
ServerId : /VIServer=lab\jsnyder@hwlvcs01.lab.local:443/
State : Running
IsCancelable : True
PercentComplete : 0
StartTime : 12/11/2017 2:30:49 PM
FinishTime :
ObjectId : VirtualMachine-vm-179
Result :
Description : Clone virtual machine
ExtensionData : VMware.Vim.Task
Id : Task-task-84405
Name : CloneVM_Task
Uid : /VIServer=lab\jsnyder@hwlvcs01.lab.local:443/Task=Task-task-84405/
Client : VMware.VimAutomation.ViCore.Impl.V1.VimClient
CmdletTaskInfo :
As you can see, the ID field in a "client side" task is a GUID and the ID field in the "not client side" task is a Managed Object Reference.
And as you say, the second one ($x) does not automatically update itself, whereas the first one ($t) does.
But you are doing a Get-Task as well in the While loop.
That's why the field is updated.
Which Gui do you mean? The Web Client?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
No, sorry, I meant the GUI I wrote around this script. It has an output pane like so: