[UPDATE 2014-10-21] the code and the assembly are now available under Apache 2.0 license on GitHub at https://github.com/dfch/biz.dfch.CS.System.Utilities
[UPDATE 2014-12-10] see below for further details on this issue and a workaround to this that does not need PSRemoting

Recently I observed a strange bug that occured when trying to invoke a PowerShell job via the Windows Task Scheduler. In my case I had defined a PowerShell script “Starter.ps1” that was run under a domain service account with local administrative permissions and in turn that “Starter.ps1” script would start a new job “Worker” via Start-Job run under a different service account (also a domain user with full local administrative permissions).
The “Worker” job would just not execute and the PSRemotingJob would return a ‘FAILED’ state. That was actually strange as the scripts would run just fine when invoked interactively (via the same user accounts). And having local administrators would normally deny the possibility of “permission” problems.

As soon as I ran the “Task” job with the same credentials as the “Starter.ps1” (or the “Starter.ps1” with the same credentials as tha “Task” job) everything worked fine.

When looking at the Powershell Windows event logs there was an intersting error message that showed up just after the runspace was created:

Log Name:    Microsoft-Windows-PowerShell/Operational
Source:    Microsoft-Windows-PowerShell
Date:      10/11/2014 12:18:31 PM
Event ID:    8197
Task Category: Connect
Level:     Verbose
Keywords:    None
User:      DOMAIN\user
Computer:    server1.example.com
Description:
Runspace state changed to Broken
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
  <Provider Name="Microsoft-Windows-PowerShell" Guid="{b73fd0a1-1d67-4b5a-9b55-1d0b4f39dc5d}" />
  <EventID>8197</EventID>
  <Version>1</Version>
  <Level>5</Level>
  <Task>1</Task>
  <Opcode>10</Opcode>
  <Keywords>0x0</Keywords>
  <TimeCreated SystemTime="2014-10-11T11:18:31.092340900Z" />
  <EventRecordID>11011</EventRecordID>
  <Correlation ActivityID="{b827d098-05b2-446d-afae-5ce30e6a8b7c}" />
  <Execution ProcessID="9696" ThreadID="15948" />
  <Channel>Microsoft-Windows-PowerShell/Operational</Channel>
  <Computer>server1.example.com</Computer>
  <Security UserID="S-1-5-21-123456789-123456789-123456789-1234" />
  </System>
  <EventData>
  <Data Name="param1">Broken</Data>
  </EventData>
</Event>

With that information another look at a revealed a bug report on Microsoft Connect that described this problem similarily:
PSV3 start-Jobs running from scheduled task with different credentials fail

So all in all this seems rather strange and it has obviously not been fixed as it still happens with PowerShell v4.0. A possible workaround might be to use “Start-Process” instead of “Start-Job” or to use “PSRemoting” for that. If anyone has futher suggestions on how to workaround or solve this any feedback is highly appreciated.

[UPDATE 2014-10-12]
When doing futher tests it showed that trying “Start-Process” does not solve the problem either. In fact, when using “Start-Process” with a credential set different from the original caller, it is completely ignored and the “Task” process is started with the same credentials as the orginial invoker (as if the alternate credentials had not been supplied at all).
Furthermore the same holds true when using a C# command line programm that uses Process.Start() to invoke a child process with different credentials. When trying that it makes no different if the Task Scheduler UI or the “AT.exe” command line is invoked to define and start the scheduled task.
Trying a “LogonUser” call via P/Invoke did not help either, as the process failed with the following error message in the Debug Output when started with the LOGON32_LOGON_INTERACTIVE logon type …

CLR: Managed code called FailFast, saying "
Access is denied.
"

… and the event log show this message:

Application: powershell.exe
Framework Version: v4.0.30319
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: Access is denied.

Stack:
   at System.Environment.FailFast(System.String)
   at System.Management.Automation.WindowsErrorReporting.FailFast(System.Exception)
   at Microsoft.PowerShell.UnmanagedPSEntry.Start(System.String, System.String[])
   at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
   at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)

Log Name:      Application
Source:        .NET Runtime
Date:          10/12/2014 2:21:53 PM
Event ID:      1025
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      SERVER1
Description:
Application: powershell.exe
Framework Version: v4.0.30319
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: Access is denied.

Stack:
   at System.Environment.FailFast(System.String)
   at System.Management.Automation.WindowsErrorReporting.FailFast(System.Exception)
   at Microsoft.PowerShell.UnmanagedPSEntry.Start(System.String, System.String[])
   at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
   at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)

Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name=".NET Runtime" />
    <EventID Qualifiers="0">1025</EventID>
    <Level>2</Level>
    <Task>0</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2014-10-12T12:21:53.000000000Z" />
    <EventRecordID>19313</EventRecordID>
    <Channel>Application</Channel>
    <Computer>SERVER1</Computer>
    <Security />
  </System>
  <EventData>
    <Data>Application: powershell.exe
Framework Version: v4.0.30319
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: Access is denied.

Stack:
   at System.Environment.FailFast(System.String)
   at System.Management.Automation.WindowsErrorReporting.FailFast(System.Exception)
   at Microsoft.PowerShell.UnmanagedPSEntry.Start(System.String, System.String[])
   at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
   at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
</Data>
  </EventData>
</Event>

For more information on FailFast see this interesting StackOverflow article: “What is Environment.FailFast?”
( http://stackoverflow.com/questions/564581/what-is-environment-failfast )

However when using LogonUser with a logon type of either LOGON32_LOGON_BATCH or LOGON32_LOGON_SERVICE the invoked “Task” process still had the original credentials but “[System.Security.Principal.WindowsIdentity]::GetCurrent($true);” showed a different logon token (the impersonated token from the LogonUser() call) than the “standard” invocation of “GetCurrent” (the “real” token).

So the final solution to this approach was to use the good old “CreateProcessWithLogonW()” Win32 function as described in this article (from some nearly 7 years ago) “How to call CreateProcessWithLogonW & CreateProcessAsUser in .NET” ( http://blogs.msdn.com/b/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx )

See https://github.com/dfch/biz.dfch.CS.System.Utilities for details on the source code.

Note: I also tried to use SecurityRules and SecurityCritical attributes on the assembly and class/method level without success:

[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]

[SecurityCritical]

Of course you can still resort to PSRemoting instead of using P/Invoke if you prefer. But all in all it appears this is not a PowerShell bug or error but something that is somehow messed up in the .NET /CLR runtime – or to phrase it differently: a security concept that I somehow do not understand. Call it whether you like it, but it is really a complete nightmare to invoke a process with different credentials from a .NET program (that is invoked from a batch or service session).
Any suggestions for improvement are still welcome…

Leave a comment

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