Task Data Mining – An improved Get-Task

Quite frequently there are questions in the VMTN PowerCLI Community for scripts that report on the tasks that ran in a vSphere environment.

The PowerCLI pssnapin provides a Get-Task cmdlet, but that only provides information about the recent tasks. An alternative is to use the Get-VIEvent cmdlet and extract all the TaskEvent entries.

But why not use the TaskHistoryCollector and it’s methods ? It provides many filtering options, and since this filtering is done in vSphere itself, this way of working is inherently much faster than using a filter in your script.

In analogy with the Get-VIEventPlus function, I published in my Get the vMotion/svMotion history post, here is the Get-TaskPlus function !

Update February 13th 2020

  • Added logic to break out of do-while loop and destroy the TaskCollector to avoid issues with max 32 collectors

The Script

Annotations

Line 62: This parameter should normally not be used. But in larger environments, settings the TaskHistoryCollector window to a value higher than 100, might improve the performance of the function

Line 66-110: An internal function that will extract the data from the returned TaskInfo objects.

Line 76-81: When the function sees that is running in a PowerShell v3 engine, it will use the [ordered] attribute.

Line 107: This is where the actual object is returned

Line 112-141: The TaskFilterSpec object is populated with the parameters that were passed to the function.

Line 123: The “self” recursion option does not seem to work for a number of managed objects. If you want to use a HostSystem or a ClusterComputeResource as the Entity, select the Recurse parameter as well. And filter out the children from the returned objects.

Line 145-146: Since the function can also be run against an ESXi server, we use the ServiceInstance to find the TaskManager object.

Line 148-153: In the TaskManager object, under the RecentTask property, there are pointers to Task objects. These are tasks that are queued, running or that have ended during the last minutes.

Line 157-163: If the Reverse switch is True, the TaskInfo objects are read newest to oldest. The script stores the method to read in a variable, this to avoid having to execute the Reverse test several times.

Line 165: With the Invoke method the function calls the selected method.

Line 166: When there are no tasks returned, the script breaks out of the do-while loop.

Line 171: The TaskHistoryCollector is destroyed. This is a best practice when working with HistoryCollectors, since a session can only have a limited number (32) of such Collectors.

Sample Usage

The function is easy to use.

In it’s simplest form it will return the default MaxSamples number (100) of tasks.

Due to the number of properties that are returned, it is much easier to save the results to a file, for example an XLSX file. You can use the Export-Xlsx function from my Export-Xlsx, the sequel, and ordered data post.

The result looks something like this.

taskPlus1

As you might notice, the properties are obviously not in the same order as we assigned them in the function. That is because the previous spreadsheet was created on a PowerShell v2 session. When we call the function in a PowerShell v3 session, the properties will be ordered.

Let’s also ask some more details in this invocation of the function.

In the result we get the columns/properties in the order they were specified in the function, and there are many additional properties present.

taskPlus2

The function allows you to select the returned tasks on several criteria.

For example, all tasks started by a specific Alarm during the last 7 days.

Or all the tasks that were started by a specific user, newest to oldest task.

Or all the task that gave an error between 2 and 4 days ago.

Start exploring the possibilities, and I would love to hear when you have used the function in one of your automation scripts.

Enjoy !

48 Comments

    Buddy Bishop

    I’m using this script in an attempt to monitor a set of clonevm tasks that run for several minutes or longer. The intention is that when all clone tasks are completed, the VM’s will then be powered up.
    For some reason a few VM’s clone tasks are never seen/detected as existing. However, they are clearly (albeit slowly) underway in the vSphere console.
    I have nine clone tasks in progress. The script only shows seven.
    What I can I look for or adjust to account for this?

    Bhuvan

    Hello LucD,

    I am trying to retrieve those running tasks using Get-TaskPlus, but it’s not able to fetch those running tasks; however, Get-Task command is able to show those running tasks for a while and it disappeared suddenly while the tasks is still running.

    Is there way to monitor those running tasks until it completes successfully. For example, the tasks like vMotion, svMotion, Host Remediation, etc.

    Lucas Taves

    I’m trying to use this to get tasks from a template, I basically want to retrieve all mark VM as template tasks to get when the template was last set as template.

    However, using Get-TaskPlus -Entity $template (template is the direct result of a Get-TaskPlus -Entity $template call) returns nothing.

    I have debugged and it all seems correct, the entity is properly set as the filter, the task manager is set as well, and the $tasks = $taskReadOp.Invoke($WindowSize) call returns nothing.

    I can see the tasks from the web console, and I’ve tried a series of parameters but they don’t seem to make any difference in this case.

      Lucas Taves

      Found out what’s happening, when the taskColletor ($tCollector = Get-View ($tskMgr.CreateCollectorForTasks($filter))) is created the tasks I want are there, so I just made sure the tasks returned by $taskReadOp.Invoke($WindowSize) are added into the existing task list instead of replacing it entirely. Seems to be working now.

        LucD

        Hi Lucas,
        Not sure what you changed.
        The tasks are retrieved in batches, and each task is then send to the Get-TaskDetails function.

        Note that tasks are (and I don’t know for what reason) not immediately available via the collector.
        I just experienced the same, but when I waited a bit, the results included the test tasks I ran.


        Name Description DescriptionId Task Created Task Started Task Ended State Result
        ---- ----------- ------------- ------------ ------------ ---------- ----- ------
        MarkAsVirtualMachine VirtualMachine.markAsVirtualMachine 6/2/2020 11:54:08 AM 6/2/2020 11:54:08 AM 6/2/2020 11:54:08 AM success
        MarkAsTemplate VirtualMachine.markAsTemplate 6/2/2020 11:34:08 AM 6/2/2020 11:34:08 AM 6/2/2020 11:34:08 AM success

    John Whittingham

    I know this post is old but it is still the best example of using the TaskHistoryCollector and one of the top Google results so I thought I would share my tweaks and corrections. Some of which are just incorporating the feedback from above, others looking more deeply into a couple of the issues raised.
    1. If no tasks are returned then the collectory was not being destroyed. Line 166 changed to break.
    2. Set default Start or End time if -Start or -End was specified (Taken from a reply by Luc above).
    3. Added -Server $viObject to Line 149 for the case when you pass in a VIServer object and not using the Global servers variable.
    4. Updated the code around the use of -Reverse. This was the most difficult to understand.
    a. Added collecting the events in .latestPage when reading previous becasue $tCollector.ResetColelctor() doesn’t set the page pointer to the newest task but the first task after the latestPage. Hence this collection was missing a 100 tasks (in it’s default settings).
    b. Reversed the order the tasks are passed to Get-TaskDetails when the order is oldest to newest. I think this is the strange order that some have noticed above. Pages where being collected in oldest to newest but within a page where being passed to Get-TaskDetails in newest to oldest hence every 100 tasks there would be a weird time jump.
    5. Separated -RealTime and -Filter actions into discrete calls using ParameterSetNames. For me collecting the task for 3 days ago and whats happening now just doesn’t make sense and adds time a gap in the results. also any filtering doesn’t apply to recent tasks so the 2 just seem separate. Take out the ParameterSets if you don’t like this.

    Here’s what I’ve ended up with:

    function Get-TaskPlus {
    Get-TaskPlus -Start (Get-Date).AddDays(-1)
    .EXAMPLE
    PS> Get-TaskPlus -Alarm $alarm -Details
    #>
    param(
    [CmdletBinding(DefaultParameterSetName=’Filter’)]
    [VMware.VimAutomation.ViCore.Impl.V1.VIServerImpl[]]$Server = $global:DefaultVIServer,
    [int]$MaxSamples = 100,
    [switch]$Details,
    [switch]$Keys,

    [Parameter(ParameterSetName=’Filter’)]
    [VMware.VimAutomation.ViCore.Impl.V1.Alarm.AlarmDefinitionImpl]$Alarm,

    [Parameter(ParameterSetName=’Filter’)]
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Entity,

    [Parameter(ParameterSetName=’Filter’)]
    [switch]$Recurse = $false,

    [Parameter(ParameterSetName=’Filter’)]
    [VMware.Vim.TaskInfoState[]]$State,

    [Parameter(ParameterSetName=’Filter’)]
    [DateTime]$Start,

    [Parameter(ParameterSetName=’Filter’)]
    [DateTime]$Finish,

    [Parameter(ParameterSetName=’Filter’)]
    [string]$UserName,

    [Parameter(ParameterSetName=’Filter’)]
    [switch]$Reverse = $false,

    [Parameter(ParameterSetName=’Filter’)]
    [int]$WindowSize = 100,

    [Parameter(ParameterSetName=’RealTime’,Mandatory=$true)]
    [switch]$Realtime
    )
    begin {
    function Get-TaskDetails {
    param(
    [VMware.Vim.TaskInfo[]]$Tasks
    )
    begin {
    }
    process {
    $tasks | % {
    $object = [ordered]@{ }
    $object.Add(“Name”, $_.Name)
    $object.Add(“Description”, $_.Description.Message)
    if ($Details) { $object.Add(“DescriptionId”, $_.DescriptionId) }
    if ($Details) { $object.Add(“Task Created”, $_.QueueTime) }
    $object.Add(“Task Started”, $_.StartTime)
    if ($Details) { $object.Add(“Task Ended”, $_.CompleteTime) }
    $object.Add(“State”, $_.State)
    $object.Add(“Result”, $_.Result)
    $object.Add(“Entity”, $_.EntityName)
    $object.Add(“VIServer”, $VIObject.Name)
    $object.Add(“Error”, $_.Error.ocalizedMessage)
    if ($Details) {
    $object.Add(“Cancelled”, (& { if ($_.Cancelled) { “Y” } else { “N” } }))
    $object.Add(“Reason”, $_.Reason.GetType().Name.Replace(“TaskReason”, “”))
    $object.Add(“AlarmName”, $_.Reason.AlarmName)
    $object.Add(“AlarmEntity”, $_.Reason.EntityName)
    $object.Add(“ScheduleName”, $_.Reason.Name)
    $object.Add(“User”, $_.Reason.UserName)
    }
    if ($keys) {
    $object.Add(“Key”, $_.Key)
    $object.Add(“ParentKey”, $_.ParentTaskKey)
    $object.Add(“RootKey”, $_.RootTaskKey)
    }
    New-Object PSObject -Property $object
    }
    }
    }

    $filter = New-Object VMware.Vim.TaskFilterSpec
    if ($Alarm) {
    $filter.Alarm = $Alarm.ExtensionData.MoRef
    }
    if ($Entity) {
    $filter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity
    $filter.Entity.entity = $Entity.ExtensionData.MoRef
    if ($Recurse) {
    $filter.Entity.Recursion = [VMware.Vim.TaskFilterSpecRecursionOption]::all
    }
    else {
    $filter.Entity.Recursion = [VMware.Vim.TaskFilterSpecRecursionOption]::self
    }
    }
    if ($State) {
    $filter.State = $State
    }
    if ($Start -or $Finish) {
    $filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
    if (!$Start) {
    $Start = Get-Date 1/1/1970
    }
    $filter.Time.beginTime = $Start
    if (!$finish) {
    $Finish = Get-Date
    }
    $filter.Time.endTime = $Finish
    $filter.Time.timeType = [vmware.vim.taskfilterspectimeoption]::startedTime
    }
    if ($UserName) {
    $filter.UserName = New-Object VMware.Vim.TaskFilterSpecByUserName
    $filter.UserName.userList = $UserName
    $filter.UserName.systemUser = $false
    }
    $nrTasks = 0
    }
    process {
    foreach ($viObject in $Server) {
    $si = Get-View ServiceInstance -Server $viObject
    $tskMgr = Get-View $si.Content.TaskManager -Server $viObject
    if ($Realtime) {
    $tasks = (Get-View $tskMgr.recentTask -Server $viObject).Info
    $selectNr = [Math]::Min($tasks.Count, $MaxSamples – $nrTasks)
    Get-TaskDetails -Tasks $tasks[0..($selectNr – 1)]
    $nrTasks += $selectNr
    }
    else {
    $tCollector = Get-View ($tskMgr.CreateCollectorForTasks($filter)) -Server $viObject
    if ($Reverse.IsPresent) {
    $tCollector.ResetCollector()
    $taskReadOp = $tCollector.ReadPreviousTasks
    $tasks = $tCollector.latestPage
    $selectNr = [Math]::Min($tasks.Count, $MaxSamples – $nrTasks)
    Get-TaskDetails -Tasks $tasks[0..($selectNr – 1)]
    $nrTasks += $selectNr
    do {
    $tasks = $taskReadOp.Invoke($WindowSize)
    if (!$tasks) { break }
    $selectNr = [Math]::Min($tasks.Count, $MaxSamples – $nrTasks)
    Get-TaskDetails -Tasks $tasks[0..($selectNr – 1)]
    $nrTasks += $selectNr
    } while ($nrTasks -lt $MaxSamples)
    }
    else {
    $tCollector.RewindCollector()
    $taskReadOp = $tCollector.ReadNextTasks
    do {
    $tasks = $taskReadOp.Invoke($WindowSize)
    if (!$tasks) { break }
    $selectNr = [Math]::Min($tasks.Count, $MaxSamples – $nrTasks)
    Get-TaskDetails -Tasks $tasks[($selectNr – 1)..0]
    $nrTasks += $selectNr
    } while ($nrTasks -lt $MaxSamples)
    }
    $tCollector.DestroyCollector()
    }
    }
    }
    }

      LucD

      Thanks John.

      On your 1st remark, I guess you missed my update today.
      In fact, it was not only the ‘break’ statement, but also the location where the Task collector is destroyed.
      As I see it, it should happen, as in my most recent code, after the do-while.

    Corty

    Hi Luc, Thanks for the function. Do you know if you can create custom vctasks – e.g. to track something that is not coming from a _task method?

    Cheers, Corty

      LucD

      Hi Corty,
      Afaik you can not start your own tasks on the vCenter.

    Joy Mamer

    Luc,,

    How would I alter this to get just the deleted vms with a Destroy_Task? I’d also like to go back more than a month.

    Name : Destroy_Task
    Description :
    DescriptionId : VirtualMachine.destroy
    Task Created : 1/17/2019 6:17:56 PM
    Task Started : 1/17/2019 6:17:56 PM
    Task Ended : 1/17/2019 6:17:56 PM
    State : success
    Result :
    Entity : presitzzcgw0
    VIServer : 10.63.114.15
    Error :
    Cancelled : N
    Reason : User
    AlarmName :
    AlarmEntity :
    ScheduleName :
    User : XXXX\jmamer

      LucD

      Hi Joy,
      The easiest would be to use the Get-ViEvent cmdlet.
      Something like this

      $start = (Get-Date).AddHours(-1)

      Get-VIEvent -Start $start -MaxSamples ([int]::MaxValue) |
      Where{$_ -is [VMware.Vim.TaskEvent] -and $_.Info.Name -eq 'Destroy_Task'}

      You can adapt the value of the $start variable to go further back in time.
      You can’t of course go back further than the duration for which VMs are kept.

    Greg Moyses

    I cant seem to use the entity parameter

    “Get-TaskPlus : Cannot process argument transformation on parameter ‘Entity’. Cannot convert the “SERVER-NAME”
    value of type “System.String” to type “VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl”.
    At C:\Untitled2.ps1:178 char:22
    + Get-TaskPlus -Entity SERVER-NAME -Details -Keys -MaxSamples 15000 …
    + ~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Get-TaskPlus], ParameterBindingArgumentTransformationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Get-TaskPlus””

    Any ideas?

    Greg

      LucD

      Hi Greg,
      The Entity parameter expects a .Net object, not a string I’m afraid.
      Something like

      $esx = Get-VMHost -Name MyEsx
      Get-TaskPlus -Entity $esx ....

        satya

        i want to get tasks with respect to datastore

          LucD

          Did you try passing a Datastore object on the Entity parameter?

    Everton Poyato

    Is it possible to put a column with the task initiator?

    Wayne S.

    Is it “normal” to have the task return with GTM rather than the time that shows in the task log? Was trying to get a report of when our backups start and finished and my start and end times are “not correct” based on what is returned. Do I have to make the correction after the fact or is there a way to have it return EST rather than GMT?

    for example start
    Entity Task Started Task Ended
    —— ———— ———-
    datastore1 9/24/2017 9:00:44 PM 9/24/2017 9:03:32 PM

    The actual start and and end times are.
    Entity Task Started Task Ended
    —— ———— ———-
    datastore1 9/24/2017 5:00:44 PM 9/25/2017 5:03:32 AM

      LucD

      Hi Wayne,
      Yes, that’s normal.
      All timestamps in Task and Events, but also in the logs, are in UTC.
      The view in the Web Client converts to your local timezone. In a script you will have to do that by calling the ToLocalTime() method on the DateTime object.

        Wayne S.

        Thanks. just modified the lines…

        $object.Add(“Task Started”,$_.StartTime.tolocaltime())

        if($Details){$object.Add(“Task Ended”,$_.CompleteTime.tolocaltime())}

    matt

    Fantastic, as usual!! Found a need to use this type of data mining just today and of course Luc had already done all the heavy lifting. I could not remember the names of the two vm’s I moved when testing some latency difference between backend storage though I do remember the destination datastore. A simple one liner did the trick:
    get-vm -Datastore destdsname |Get-MotionHistory -Days 7

    Robert van den Nieuwendijk

    The Get-TaskPlus function closes my PowerCLI console. It happens in the do-while loop in the lines 164-170. Probably by the exit command in line 166 if(!$tasks){exit}. I am using PowerCLI 6.3 Release 1 on PowerShell 4.0 connected to a vCenter v5.5 server.

      LucD

      Thanks for noticing that Robert.
      I changed it.

    Shikhar

    Is it possible to get the status, as well as the state? Eg: The state being:Error and the StatusL Unable to start operation

    Anton Coleman

    Hi Luc,

    I like the script you’ve provided a lot. It seems though, that I’m having an issue where the TaskHistoryCollector session isn’t being destroyed (I assume). This is a problem when I rerun this function multiple times within a short period, as the collector will not show up and recent tasks performed. I know the Get-Task will show recent, but the script I’m writing, will probably generate about 100 tasks in less than an hour, and I need a solid way to track the tasks so I can write the success/error, and task description to a log. Thoughts? Thanks for all that you do.

      LucD

      Thanks Anton.
      The problem is that DestroyCollector doesn’t give any feedback.
      One, not so elegant, way of testing if the Collector is actually destroyed, could be to try and use it (for example by doing a ResetCollector). If the Collector is not present anymore, you should get an Exception.
      Using that Exception you could include a Try-Catch in the code and make sure the Collector is actually gone before proceeding.

      Another solution could be to create a global collector ($global:tCollector), and then reuse that one in each call to the function.
      This avoids the overhead of setting up and removing the collector at each call of the function.

    Rusty

    Hi,

    I’ve just run this script in its most basic form, Get-TaskPlus and I get absolutely nothing.

    Peter vB

    Nice script! One issue though, I was trying the -Realtime switch but it gave me some trouble.

    Firstly I changed line 151 to something similar to line 168, it seems that the $tasks variable was not present. After that there was a type mismatch between Task and TaskInfo so I changed line 149 to these three lines:

    $taskObjects = Get-View $tskMgr.recentTask
    $tasks = @()
    $taskObjects | foreach { $tasks += $_.Info }

    That seemed to resolve the type mismatch but only a few recent tasks are returned. There still is about a two hour gap between the most recent task in vSphere client and the list your script returns.
    Any ideas?

      LucD

      Hi Peter,
      I’ll have a look

        jonpants

        Same thing reported. I did the changes that Peter suggested and that improved the -Realtime switch. I do get an odd sorting based on time of Task Started, though. Still, much more helpful than get-task. thanks!

        Samuel Wambach

        I had the same issue. I was able to get all of the tasks returned by making the above changes and setting ” [switch]$Reverse = $false ” on line 57. There seems to be a problem with the block at line 157 so I just disabled it.

        Also, thank you for sharing this script Luc. This was by far the best example of using the TaskHistoryCollector I could find and gave me a much clearer picture of how it worked. Thank you!

    IonutN

    Hi Luc,

    Awesome script, just what I needed to review many vCenter tasks. However i noticed some strange issue.
    I want to get the events from all esxi hosts in my vcenter,
    So I do :
    $ents = get-vmhost *
    foreach ($ent in $ents) {
    get-taskplus -entity $ent
    }

    I get no output from the function. i looked at the code and I’m not sure, but could it be that the object is cast a different type rather than what vmhost is returning.
    If I do get member on $ent i get this.
    TypeName: VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl

    The entity variable is cast as:
    [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]$Entity,

      LucD

      Hi,
      Strange, I just ran your script, and I seem to get the TaskEvents from all my ESXi nodes back.
      Does it work when you only use 1 ESXi node, for example
      Get-VMHost -Name MyEsx | Get-TaskPlus

      Luc

    jack

    Hi,
    I find a bug.
    In the vsphere 5.5, the script is not work.
    The result has a fixed time period.

      LucD

      Thanks for letting me know.
      Do you have any more information ?
      Is there an error message ?

    jrob24

    Not sure what is going on but I have ran this function three different times and each time it kills my PS console.

    PS v3
    PowerCLI 5.1 Release 2 build 1012425

    Example commands tested:
    Get-TaskPlus -Entity Host1
    Get-TaskPlus -Entity Host1 -Start (Get-Date).AddDays(-3)
    Get-TaskPlus -Entity Host1 -Start (Get-Date).AddDays(-1)

    Any ideas?

      LucD

      Are there any messages ?
      Did you run that from the PowerCLI prompt ? Or something else ?

        jrob24

        It was closing the console so fast I had to create a transaction log. Here is the error. It appears that when I use the -Start parameter it is expecting a value for -Finish.

        Get-TaskPlus -Entity $entity -Start (Get-Date).AddDays(-1) -MaxSamples 10
        Exception setting “endTime”: “Cannot convert null to type “System.DateTime”.”
        At line:126 char:7
        + $filter.Time.endTime = $Finish
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [], SetValueInvocationExceptio
        n
        + FullyQualifiedErrorId : ExceptionWhenSetting

          LucD

          Hi jrob24, that is indeed an issue.
          Can you update the code where the Start and Finish are place in the filter (lines 129-134) by the following code

          if($Start -or $Finish){
          $filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
          if(!$Start){$Start = Get-Date 1/1/1970}
          $filter.Time.beginTime = $Start
          if(!$Finish){$Finish = Get-Date}
          $filter.Time.endTime = $Finish
          $filter.Time.timeType = [vmware.vim.taskfilterspectimeoption]::startedTime
          }

          That way you should be able to only pass a Start parameter.

          For the other problem I don’t have a reply.
          What is in the $entity variable ? A VM, a folder… ?
          Does it crash for any entity ?
          Can you try without the Entity parameter

        jrob24

        I tested it with both a start and finish date, it killed the console and no error message. Here is transcript:
        Windows PowerShell transcript start
        Start time: 20140205163427
        **********************
        Transcript started, output file is trans.txt

        PowerCLI C:\temp> Get-TaskPlus -Entity $entity -Start (Get-date).AddDays(-2) -Finish (Get-Date)
        **********************
        Windows PowerShell transcript end
        End time: 20140205163450
        **********************

    Trevor Benson

    I love the get-taskplus script. One thing I noticed however, when I typo’d a -UserName it would kill my entire script and the shell/window I was working in. I think Line 166 should read be altered to
    ===
    if(!$tasks){return}
    ===
    so that in the event there is no return values, your shell does not close, but rather the script exits and returns back to the prompt with zero results. Did I miss some reason why Get-TaskPlus should not Break or Return, but rather exit and close the window? Let me know if I missed something, as I only took a brief look after I had an invalid username and it closed my shell down.

      admin

      Thanks Trevor, that is indeed a use case I didn’t test.
      No, you didn’t miss anything, I’ll update the script.

      John Whittingham

      With this setting when there are no tasks and you call Get-TaskPlus several times (i.e. foreach loop on all hosts) then after 32 iterations you get an error trying to create the “Collector”. This is because the collector is not destroyed.
      I think Line 166 would be better altered to
      ===
      if(!$tasks){break}
      ===

        LucD

        Hi John,
        Thanks for reporting that issue.
        I updated the code.

        There was in fact another issue with the placement of the call to the DestroyCollector method.

    harshvardhan gupta

    i need help in creating a powershell script to change vm’s iops, boot delay, scsi to paravirtualiz and thick disk to eagerly zeroed, i collected things from internet and created something like this but it is throwing some error.

    Please help me out.
    #Generated Form Function
    function GenerateForm {
    ########################################################################
    # Code Generated By: SAPIEN Technologies PrimalForms (Community Edition) v1.0.10.0
    # Generated On: 6/18/2013 10:10 AM
    # Generated By: ts-harshavardh.gupta
    ########################################################################

    #region Import the Assemblies
    [reflection.assembly]::loadwithpartialname(“System.Windows.Forms”) | Out-Null
    [reflection.assembly]::loadwithpartialname(“System.Drawing”) | Out-Null
    #endregion

    #region Generated Form Objects
    $form1 = New-Object System.Windows.Forms.Form
    $button4 = New-Object System.Windows.Forms.Button
    $button3 = New-Object System.Windows.Forms.Button
    $button2 = New-Object System.Windows.Forms.Button
    $button1 = New-Object System.Windows.Forms.Button
    $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
    #endregion Generated Form Objects

    #———————————————-
    #Generated Event Script Blocks
    #———————————————-
    #Provide Custom Code for events specified in PrimalForms.
    $button3_OnClick=
    {
    #TODO: Place custom script here
    #change IOPS to 500
    #$vmName = “MyVM”
    $_HEADER = “hostname”,”os”,”type”
    $vmName=Import-Csv ./vm_list.csv -Header $_HEADER
    $DiskLimitIOPerSecond = 500
    # $DiskLimitIOPerSecond = -1 # Unlimited

    foreach($vmNames in $vmName){
    $vm = Get-VM -Name $vmName
    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $vm.ExtensionData.Config.Hardware.Device |
    where {$_ -is [VMware.Vim.VirtualDisk]} | %{
    $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $dev.Operation = “edit”
    $dev.Device = $_
    $dev.Device.StorageIOAllocation.Limit = $DiskLimitIOPerSecond
    $spec.DeviceChange += $dev
    }

    $vm.ExtensionData.ReconfigVM_Task($spec)
    }
    }

    $button1_OnClick=
    {
    #TODO: Place custom script here
    #Change Boot Delay
    $value = “10000”

    $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $vmConfigSpec.BootOptions = New-Object VMware.Vim.VirtualMachineBootOptions
    $vmConfigSpec.BootOptions.BootDelay = $value

    Get-Content -Path c:\scripts\vm_list.csv | %{
    (Get-VM -Name $_).Extensiondata.ReconfigVM_Task($vmConfigSpec)
    }

    }

    $button2_OnClick=
    {
    #TODO: Place custom script here
    #Change scsi to paravirtualized
    #$vmName = “MyVM”
    $_HEADER = “hostname”,”os”,”type”
    $vmName=Import-Csv ./vm_list.csv -Header $_HEADER
    $ctrlName = “SCSI controller 0”

    foreach($vmNames in $vmName){
    $vm = Get-VM $vmName | Get-View

    # Get the controller and the devices connected to it
    $vm.Config.Hardware.Device | where {$_.DeviceInfo.Label -eq $ctrlName} | % {
    $oldCtrl = $_
    $ctrlKey = $_.Key
    $devs = @()
    $_.Device | % {
    $devKey = $_
    $vm.Config.Hardware.Device | where {$_.Key -eq $devKey} | % {
    $devs += $_
    }
    }
    }

    # Create the specification for the device changes
    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec

    # Remove old controller
    $old = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $old.device = $oldCtrl
    $old.operation = “remove”
    $spec.DeviceChange += $old

    # Update the devices connected to the controller
    $devs | % {
    $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $dev.device = $_
    $dev.device.ControllerKey = -100
    $dev.operation = “edit”
    $spec.DeviceChange += $dev
    }

    # Add new controller
    $new = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $new.Device = New-Object VMware.Vim.ParaVirtualSCSIController
    $new.Device.Key = -100
    if($defaultVIServer.Build -ge ‘258902’){
    $new.Device.ControllerKey = $oldCtrl.ControllerKey
    $new.Device.UnitNumber = $oldCtrl.UnitNumber
    }
    $new.operation = “add”
    $spec.DeviceChange += $new

    $vm.ReconfigVM($spec)
    }
    }

    $button4_OnClick=
    {
    #TODO: Place custom script here

    }

    $OnLoadForm_StateCorrection=
    {#Correct the initial state of the form to prevent the .Net maximized form issue
    $form1.WindowState = $InitialFormWindowState
    }

    #———————————————-
    #region Generated Form Code
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Height = 374
    $System_Drawing_Size.Width = 291
    $form1.ClientSize = $System_Drawing_Size
    $form1.DataBindings.DefaultDataSourceUpdateMode = 0
    $form1.Name = “form1”
    $form1.Text = “Change VM settings”

    $button4.DataBindings.DefaultDataSourceUpdateMode = 0

    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 57
    $System_Drawing_Point.Y = 309
    $button4.Location = $System_Drawing_Point
    $button4.Name = “button4”
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Height = 43
    $System_Drawing_Size.Width = 186
    $button4.Size = $System_Drawing_Size
    $button4.TabIndex = 3
    $button4.Text = “change disk to Eagerly Zeroed”
    $button4.UseVisualStyleBackColor = $True
    $button4.add_Click($button4_OnClick)

    $form1.Controls.Add($button4)

    $button3.DataBindings.DefaultDataSourceUpdateMode = 0

    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 57
    $System_Drawing_Point.Y = 230
    $button3.Location = $System_Drawing_Point
    $button3.Name = “button3”
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Height = 47
    $System_Drawing_Size.Width = 186
    $button3.Size = $System_Drawing_Size
    $button3.TabIndex = 2
    $button3.Text = “change IOPS to 500”
    $button3.UseVisualStyleBackColor = $True
    $button3.add_Click($button3_OnClick)

    $form1.Controls.Add($button3)

    $button2.DataBindings.DefaultDataSourceUpdateMode = 0

    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 57
    $System_Drawing_Point.Y = 143
    $button2.Location = $System_Drawing_Point
    $button2.Name = “button2”
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Height = 55
    $System_Drawing_Size.Width = 186
    $button2.Size = $System_Drawing_Size
    $button2.TabIndex = 1
    $button2.Text = “Change scsi to paravirtualized”
    $button2.UseVisualStyleBackColor = $True
    $button2.add_Click($button2_OnClick)

    $form1.Controls.Add($button2)

    $button1.DataBindings.DefaultDataSourceUpdateMode = 0

    $System_Drawing_Point = New-Object System.Drawing.Point
    $System_Drawing_Point.X = 57
    $System_Drawing_Point.Y = 64
    $button1.Location = $System_Drawing_Point
    $button1.Name = “button1”
    $System_Drawing_Size = New-Object System.Drawing.Size
    $System_Drawing_Size.Height = 49
    $System_Drawing_Size.Width = 186
    $button1.Size = $System_Drawing_Size
    $button1.TabIndex = 0
    $button1.Text = “Change Boot Delay”
    $button1.UseVisualStyleBackColor = $True
    $button1.add_Click($button1_OnClick)

    $form1.Controls.Add($button1)

    #endregion Generated Form Code

    #Save the initial state of the form
    $InitialFormWindowState = $form1.WindowState
    #Init the OnLoad event to correct the initial state of the form
    $form1.add_Load($OnLoadForm_StateCorrection)
    #Show the Form
    $form1.ShowDialog()| Out-Null

    } #End Function

    #Call the Function
    GenerateForm

    Kevin D

    Great script, but I found one bug. As written, it doesn’t work for users in my environment. I replaced lines 138-140 with the following:

    if($UserName){
    $userNameFilterSpec = New-Object VMware.Vim.TaskFilterSpecByUserName
    $userNameFilterSpec.UserList = $UserName
    $filter.UserName = $userNameFilterSpec
    }

      admin

      Hi Kevin, thanks for spotting that bug.
      I updated the script accordingly.

    Lenny Chauhan

    I am new to PowerCLI. I need a script to list all the eagerzeroedthick vmdk files. Would appreciate your assistance.

      admin

      Hi Lenny,
      Try something like this

      Get-VM | %{
      $vmName = $_.Name
      Get-HardDisk -VM $_ | where {!$_.ExtensionData.Backing.EagerlyScrub} |
      Select @{N="VM";E={$vmName}},Name
      }

Leave a Reply

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

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.