Hi,
I've been running into an issue attempting to retrieve information from multiple Virtual Center servers using the PowerCLI in a multi-threaded environment using C#. Essentially, in one test scenario, I have one PowerCLI script that is used to retrieve raw disk information from VMs, one thread per Virtual Center, and multiple Virtual Centers.
When using a single thread to retrieve such information, everything works fine. Using multi-threading generates this error:
Message: VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidState: 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 VMware.VimAutomation.ViCore.Util10Ps.BaseCmdlet.SessionStateManager.SyncDefaultVISeverPSVariables(SessionState sessionState, PSHost host) at VMware.VimAutomation.Sdk.Util10Ps.BaseCmdlet.ErrorCallbackCmdletBase.BeginProcessing() at System.Management.Automation.Cmdlet.DoBeginProcessing() at System.Management.Automation.CommandProcessorBase.DoBegin()
The PowerCLI script that retrieves raw disk info looks like this:
if ( (Get-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin "VMware.VimAutomation.Core"
}
$global:DefaultVIServer = $null
$global:DefaultVIServers = $null
$server = connect-viserver -Server "ServerName" -User "UserName" -Password "Password" #-NotDefault -Verbose:$true -Debug:$true
$global:DefaultVIServer
$global:DefaultVIServers
$VMs = Get-VM -Server $server
$count= 0
$Disks = @()
foreach($VM in $VMs)
{
if (++$count % 10 -eq 0) {Write-Output "Done $count VMs"}
if($VM -ne $null)
{
foreach ($HDD in get-HardDisk -VM $VM -Server $server)
{
if ($HDD.DiskType -eq "rawPhysical" -or $HDD.DiskType -eq "rawVirtual")
{
if ($Disks -notcontains $HDD) {$Disks += $HDD}
}
}
}
}
return $Disks
As a stand-alone script, the code above works just fine. However, if the " -NotDefault -Verbose:$true -Debug:$true" is enabled in the call to connect-viserver, the line that reads "$VMs = Get-VM -Server $server" throws the same exception:
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.
I have attempted to resolve the issue by doing exactly what the exception says, resetting the $global:DefaultVIServer and $global:DefaultVIServers variables to null and reconnecting to the vSphere server, however, the error persists. I'm thinking that if I can resolve this issue when running the code as a standalone script, and using the -NotDefault option in connect-viserver cmdlet, then I will be able to run the script by invoking it from C# and using multi-threading.
Any suggestions for how I can resolve this issue?
Thanks,
Juan
Try setting the servermode to multiple
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Confirm:$false | Out-Null
Arnim
I've modified the script with your recommendation, but the error persists. The script now looks like this:
if ( (Get-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin "VMware.VimAutomation.Core"
}
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple-Confirm:$false | Out-Null
$global:DefaultVIServer = $null
$global:DefaultVIServers = $null
$server = connect-viserver -Server "ServerName" -User "Username" -Password "Password" -NotDefault -Verbose:$true -Debug:$true
$global:DefaultVIServer
$global:DefaultVIServers
$VMs = Get-VM -Server $server
$count= 0
$Disks = @()
foreach($VM in $VMs)
{
if($VM -ne $null)
{
foreach ($HDD in get-HardDisk -VM $VM -Server $server)
{
if ($HDD.DiskType -eq "rawPhysical" -or $HDD.DiskType -eq "rawVirtual")
{
if ($Disks -notcontains $HDD) {$Disks += $HDD}
}
}
}
}
return $Disks
It still errors out with the same exception on the line, " $VMs = Get-VM -Server $server" even though the $server variable holds the connection info to the Virtual Center server.
Try removing the following 2 lines
$global:DefaultVIServer = $null $global:DefaultVIServers = $nul
____________
Blog: LucD notes
Twitter: lucd22
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The error persists. Why would the call "$VMs = Get-VM -Server $server" fail when the $server variable is set and the connection is established? Not sure what else I need to do here...
Is there anything in the $Server variable ?
Did the Connect-VIServer work ?
____________
Blog: LucD notes
Twitter: lucd22
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I'm using PowerGUI Script Editor to debug the script. I set a breakpoint on the connect-viserver line, pressed F10, and then typed the following into the command prompt:
: PS C:\Windows\SysWOW64> $server.IsConnected
True
: PS C:\Windows\SysWOW64> $server.IsConnected
True
After connecting and pressing F10, the following still comes up:
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 :line:12 char:13
+ $VMs = Get-VM <<<< -Server $server
I don't have any problems running your script.
I ran it using PowerCLI 4.1 against vCenter 4.0
I think it must be something in your PowerShell environment.
If you use the -Notdefault parameter, the serverconnection is not added to $global:DefaultVIServer (single mode) or $global:DefaultVIServers (multiple mode)
Why do you want to run multiple threads?
If you set the servermode to multiple, PowerCLI handles the session management to multiple VIServers.
If connected to multiple servers your command automatically runs against all the connected servers.
I've modified your code. The body of your code can be easily turned into a oneliner.
if ( (Get-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue) -eq $null ) { Add-PsSnapin "VMware.VimAutomation.Core" } Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Confirm:$false | Out-Null $server = connect-viserver -Server "ServerName1","ServerName2" -User "Username" -Password "Password" Get-VM | Get-HardDisk | ?{$_.DiskType -eq "rawPhysical" -or $_.DiskType -eq "rawVirtual"}
-
Arnim van Lieshout
Blogging: http://www.van-lieshout.com
Twitter: http://www.twitter.com/avlieshout
If you find this information useful, please award points for "correct" or "helpful".
Your script seems to work from the PowerCLI prompt.
This looks like a possible PowerGui problem.
Did you raise the question in their forum as well ?
____________
Blog: LucD notes
Twitter: lucd22
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I don't have any problems running it in PowerGUI editor.
-
Arnim van Lieshout
Blogging: http://www.van-lieshout.com
Twitter: http://www.twitter.com/avlieshout
If you find this information useful, please award points for "correct" or "helpful".
Ok, I can reproduce your problem now!.
It looks like a PowerGUI problem.
The problem only raises when you use the -NotDefault parameter in Connect-VIserver
If you remove the parameter your code runs fine.
-
Arnim van Lieshout
Blogging: http://www.van-lieshout.com
Twitter: http://www.twitter.com/avlieshout
If you find this information useful, please award points for "correct" or "helpful".
Hi,
Thank you guys for your comments - they've helped me realize what the cause of this problem is.
I'm using multiple threads in C#, and calling different powershell scripts from each thread. I decided to use multiple threads in C# because I noticed the calls that PowerCLI is making take a while to respond, and since I want to run multiple PowerCLI scripts at a time to retrieve data, I thought using multi-threading was a good approach.
However, this multi-threading approach generates the aforementioned exception, since the PowerShell Runspace in C# is not thread safe, and each script may connect to a different vSphere server at the same time. Since connections to different VCs may happen at the same time, this modifies the global:DefaultVIServer variables and the exception is raised.
For the time being, I've managed to avoid the exception by placing a lock around where the Runspace is open and closed. However, this seems to defeat the purpose of having multi-threading, since the lock is placed around one of the most expensive operations executed. I've read that using processes instead of threads will resolve the issue, but that seems kind of hackish.
Might you know of another way to make the Runspace thread-safe, or run multiple threads within a Runspace without modifying the global:DefaultVIServer variables?
Your comments are very much appreciated,
Juan
I'm not sure if it's possible to make the Runspace thread-safe - you could probably post a question in the PowerShell forum, there might be someone who knows how to achieve this.
Another option to achieve what you want is to make all the necessary connections from the main thread. Then you can pass the appropriate connection to each child thread(script). You'll just have to modify your scrips to work only against the specified connection, rather than all available connections (e.g. use the -Server parameter of the cmdlets).
-
PowerCLI development team
Thanks for all your feedback guys, its been very helpful. After re-thinking the problem, I've decided to use the vSphere SDK, as that is thread-safe. Its been a good learning experience using the PowerCLI though. Many thanks
Hi,
Can you please send me the code you have written using vSphere SDK. My id is sameer.baveja19@gmail.com
Thanks in advance.
Hi,
Does anyone know why when I execute PowerCLI in PowerGUI, I get this error mesage:
I cannot execute any Get- powerCLI command and the above is the result from the one simple command Get-VMHost.
I finally found a workaround to this problem. runspaces are not thread-safe so you have to run them out of process. You don't even have to use snapin like one guy mentions. 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");
}
This was the first thread I ran across concerning parallelism so I'm adding some other findings for whomever runs across this.
https://www.lucd.info/2015/03/17/powercli-and-powershell-workflows/
I think `workflows` work on Powershell Desktop (5.1) and Powershell Core (6+)
Support for the PSv7 feature Foreach -Parallel | VMware PowerCLI
Thanks LucD!!