RamblingCookieMonster / Invoke-Parallel

Speed up PowerShell with simplified multithreading
MIT License
384 stars 88 forks source link

Invoke-Parallel and PowerCLI #22

Open awiddersheim opened 9 years ago

awiddersheim commented 9 years ago

Have you had any luck running this with PowerCLI? I'm hitting a brick wall doing so and was hoping for some suggestions on what to try. In my script block I am adding the PowerCLI snapin so that it's functions can be used but I keep getting the following:

Get-RunspaceData : An item with the same key has already been added.
At C:\capacity-planning\windows\Invoke-Parallel\Invoke-Parallel\Invoke-Parallel.ps1:568 char:21
+                     Get-RunspaceData
+                     ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Get-RunspaceData

If I try running with the ImportModules parameter I get this:

Get-RunspaceData : You have  modified the global:DefaultVIServer and global:DefaultVIServers system variables. This is
not allowed. Please reset them to $null and reconnect to the vSphere server.
At C:\capacity-planning\windows\Invoke-Parallel\Invoke-Parallel\Invoke-Parallel.ps1:568 char:21
+                     Get-RunspaceData
+                     ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], InvalidState
    + FullyQualifiedErrorId : VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidState,Get-RunspaceData

It is all very confusing to me. This post seems to make it sound easy if I were to not use this module but I'd prefer to stick with this plumbing if at all possible:

http://velemental.com/2012/03/11/multithreading-powercli/

RamblingCookieMonster commented 9 years ago

Haven't tried this, but do have some internal use cases for it, will take a look tomorrow!

jrob24 commented 9 years ago

Just checking to see if you ever got around to testing this out. I am having the same errors as awiddersheim mentions.

RamblingCookieMonster commented 9 years ago

Hi @jrob24! No luck so far. Tagged this 'help wanted' as it's a bit low priority on my side, but will keep poking around when the need comes up. Most of my operations with PowerCLI prioritize functionality over speed - partially due to the lumbering nature of PowerCLI : )

If you have any ideas or get anything working, definitely let us know!

eeldivad commented 7 years ago

I finally found a workaround to this problem. runspaces are not thread-safe so you have to run them out of process. Here's my solution in C#, you should be able to reference this to make modifications to your powershell script to create a runspace out of processes. See line: RunspaceFactory.CreateOutOfProcessRunspace()

    public async Task TestPowercli(string name, string vcenterHost) {
        if (string.IsNullOrWhiteSpace(name)) { return; }
        if (string.IsNullOrWhiteSpace(vcenterHost)) { return; }

        instanceName = name.Trim();
        int timeoutMins = 5;
        string script = @"c:\temp\testpowercli\testpowercli.ps1 " + instanceName + " " + vcenterHost + " -verbose";

        PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(5, 0), null, null, false);
        using (Runspace runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance)) {
            PowerShell ps = PowerShell.Create();
            runspace.Open();
            ps.Runspace = runspace;
            ps.AddScript(script);
            outputCollection.DataAdded += outputCollection_DataAddedPowercli;

            // the streams (Error, Debug, Progress, etc) are available on the PowerShell instance.
            // we can review them during or after execution.
            // we can also be notified when a new item is written to the stream (like this):
            ps.Streams.Error.DataAdded += Error_DataAddedPowercli;
            ps.Streams.Verbose.DataAdded += Verbose_DataAddedPowercli;

            IAsyncResult result = ps.BeginInvoke<PSObject, PSObject>(null, outputCollection);

            DateTime start = DateTime.Now;
            while (result.IsCompleted == false) {
                if ((DateTime.Now - start).TotalMinutes > timeoutMins) {
                    Clients.All.getPowercliMessage(instanceName, "ERROR: Time out exceeded after " + timeoutMins + " minutes");
                    break;
                }

                await Task.Delay(1000);
            }

        }

        Clients.All.getPowercliMessage(instanceName, "Done");
    }
smokinbanz commented 6 years ago

I'm also running into this problem. Any further development without using C#?