Run PowerShell scripts that interact with host

If you have been using “Run .NET Script” for some time, you probably know that not all kinds of PowerShell scripts will work with this activity.  If a script tries to use “Write-Host” or similar to interact with the host, the execution will fail.  For instance, try to run “[Guid]::NewGuid() | Write-Host” in Runbook Tester, you will see the following error:

image

Attach windbg to PolicyModule.exe and capture the exception, we can see:

(174.159c): CLR exception - code e0434f4d (first chance)
Exception object: 04f274cc
Exception type: System.Management.Automation.Host.HostException
Message: Cannot invoke this function because the current host does not implement it.
InnerException: <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131501
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=08abea90 ebx=e0434f4d ecx=00000001 edx=00000000 esi=08abeb18 edi=06d040c0
eip=766bb9bc esp=08abea90 ebp=08abeae0 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
KERNELBASE!RaiseException+0x58:
766bb9bc c9 leave
0:024> !clrstack
OS Thread Id: 0x159c (24)
ESP EIP
08abeb68 766bb9bc [HelperMethodFrame: 08abeb68]
08abec0c 5d460695 System.Management.Automation.Internal.Host.InternalHostRawUserInterface.ThrowNotInteractive()
08abec20 5d4606f1 System.Management.Automation.Internal.Host.InternalHostRawUserInterface.get_ForegroundColor()
08abec54 5cb29451 Microsoft.PowerShell.Commands.ConsoleColorCmdlet.get_ForegroundColor()
08abec84 5cb47207 Microsoft.PowerShell.Commands.WriteHostCommand.PrintObject(System.Object)
08abecc0 5cb475f0 Microsoft.PowerShell.Commands.WriteHostCommand.ProcessRecord()
08abecf0 5cfe2388 System.Management.Automation.Cmdlet.DoProcessRecord()
08abecf8 5d0311a0 System.Management.Automation.CommandProcessor.ProcessRecord()
08abed38 5d01cfe5 System.Management.Automation.CommandProcessorBase.DoExecute()
08abed68 5d0284cd System.Management.Automation.Internal.PipelineProcessor.Inject(System.Object, Boolean)
08abed84 5d023baf System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object, System.Collections.Hashtable, Boolean)
08abedcc 5d02ac03 System.Management.Automation.PipelineNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)
08abee18 5d02a884 System.Management.Automation.StatementListNode.ExecuteStatement(System.Management.Automation.ParseTreeNode, System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)
08abee64 5d02a79e System.Management.Automation.StatementListNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Collections.ArrayList ByRef, System.Management.Automation.ExecutionContext)
08abeea8 5d02a6b1 System.Management.Automation.ParseTreeNode.Execute(System.Array, System.Management.Automation.Internal.Pipe, System.Management.Automation.ExecutionContext)
08abeec4 5d02a4bf System.Management.Automation.ScriptCommandProcessor.ExecuteWithCatch(System.Management.Automation.ParseTreeNode, System.Array)
08abef04 5d028a79 System.Management.Automation.ScriptCommandProcessor.RunClause(System.Management.Automation.ParseTreeNode, System.Object, System.Object)
08abef50 5d02861c System.Management.Automation.ScriptCommandProcessor.Complete()
08abef68 5d01d1fe System.Management.Automation.CommandProcessorBase.DoComplete()
08abef9c 5d023c2f System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(System.Object, System.Collections.Hashtable, Boolean)
08abefe4 5d023b12 System.Management.Automation.Internal.PipelineProcessor.SynchronousExecute(System.Array, System.Collections.Hashtable)
08abeff8 5d01fe09 System.Management.Automation.Runspaces.LocalPipeline.InvokeHelper()
08abf050 5d01f8c7 System.Management.Automation.Runspaces.LocalPipeline.InvokeThreadProc()
08abf0a0 5ec57036 System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
08abf0ac 5ec604bf System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
08abf0c4 5ec56fb4 System.Threading.ThreadHelper.ThreadStart()
08abf2ec 6fd11b4c [GCFrame: 08abf2ec]

In general, if we want to run a PowerShell script we will create a Runspace object, create a pipeline from the runspace and add scripts to it, and invoke it.  When we create the Runspace object, there are multiple factory methods can be used (see RunspaceFactory Members on TechNet).  The activity uses a simple one thus the default host is used which has various limitations.  If it was your own code, you would need to write a custom PSHost (see PSHost Class) and pass it to CreateRunspace method.  By the way, it is a nontrivial work.  However it is not, so you may look for two options:

  • Revise the script so it contains no *-Host cmdlet anywhere.
  • Use a different activity.

If the script cannot be changed for some reason (e.g. has to use a cmdlet which interacts with host), we can try the second option.  In fact, PowerShell integration pack is designed to solve this issue.  This IP has been changed a lot recently, the documentation does not reflect the latest state, I will write a few posts to explain the changes.

The most straightforward way to run 32-bit PowerShell script is to use “Quick Run Script” activity.  Suppose we want to run “Get-Process” and capture the output, the input parameters will be:

image

Note the output log file will not be overwritten after running the activity, new logs will be appended after the existing content.  After running the output log file looks like:

image

You can also set the input parameter to capture the logs that write to standard error.  The output device is set to 132 columns in width, so each line in the output/error will be padded with some spaces, remember to make the Notepad window big enough otherwise it will seem a bit strange.

From the optional properties, you may see that this activity can also run scripts remotely via WinRM (same as Invoke-Command cmdlet):

image

The PowerShell IP can also run 64-bit PowerShell scripts locally or remotely in a native manner without running “winrm quickconfig”, I will write another post to explain that.