Powershell invocations fail after #MaxConcurrentCommandsPerShell sequential commands
antaln opened this issue · 3 comments
Powershell invocations fail after #MaxConcurrentCommandsPerShell
sequential commands.
From what I can tell, this is not really a pypsrp issue, but a Windows WSMV implementation behavior.
Example
Consider the following snippet:
with wsman, RunspacePool(wsman) as pool:
for x in range(2000):
ps = PowerShell(pool)
ps.add_script("$PSVersionTable.Count")
output = ps.invoke()
print("x={}, out={}".format(x, output))
This fails after on my Win 2012R2 box after 1000 calls with following exception:
pypsrp.exceptions.WSManFaultError: Received a WSManFault message. (Code: w:InternalError, Reason: The WS-Management service cannot process the request. The maximum number of concurrent commands per shell has been exceeded. Retry the request later or raise the Maximum Commands per Shell quota.)
Analysis
This appears to be governed by Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell
quota.
From what I can tell, this looks like it's intentional:
MS:WSMV, Product Behavior note 118 states:
Section 3.1.4.12: Windows implementations of the Shell processor do not decrement the MaxConcurrentOperationsPerUser counter when a Signal request with a Terminate code is issued to a Text-based Command Shell.
but is in violation of the protocol spec:
If the control code of the Signal request (section 2.2.4.38) is http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/Terminate, the Shell processor MUST decrement the server-side counter for MaxConcurrentOperationsPerUser.<118>
I am somewhat perplexed that a counter preventing concurrent commands would be actually used to limit sequential commands in a remote shell. Am I missing something here?
The limit applies only to MS:WSMV Text-based shells. AFAIK, PowerShell is considered a custom shell.
PSRP also requires sending terminate signal after conclusion of each command.
Hmm I thought I added the signal to the PowerShell
class to do this but looking at the code that does not seem to be the case. The simplest solution seems to be to add a close()
method that sends the Terminate
signal that will decrement the command counter. It would be best to do it automatically but at this particular point in time I cannot as it's designed to be as efficient for tools like Ansible. Ansible gets away with it because they close the shell which also ends the counter.
As a workaround for now you can do
from pypsrp.wsman import SignalCode, WSMan
from pypsrp.powershell import PowerShell, RunspacePool
with wsman, RunspacePool(wsman) as pool:
for x in range(2000):
ps = PowerShell(pool)
ps.add_script("$PSVersionTable.Count")
output = ps.invoke()
pool.shell.signal(SignalCode.TERMINATE, str(ps.id).upper())
print("x={}, out={}".format(x, output))
Also as an FYI, the new psrp
namespace that's being written right now does do this by default but this is still in development.
Cool, thanks!