title: Impersonate user in Windows Service
body: <p>I am trying to impersonate a domain user in a windows service with the service logged in as the Local System Account.</p>
  
  <p>So far, I am only able to get this to work by logging the service and set the process using the user credentials, like the following.</p>
  
  <pre><code>        ProcessStartInfo startInfo = new ProcessStartInfo();
          startInfo.FileName = CommandDetails.Command;
          startInfo.WorkingDirectory = Settings.RoboCopyWorkingDirectory;
          startInfo.Arguments = commandLine;
  
          startInfo.UseShellExecute = false;
          startInfo.CreateNoWindow = true;
          startInfo.RedirectStandardError = true;
          startInfo.RedirectStandardOutput = true;
  
          // Credentials
          startInfo.Domain = ImperDomain;
          startInfo.UserName = ImperUsername;
          startInfo.Password = ImperPasswordSecure;
  
          process = Process.Start(startInfo);
  </code></pre>
  
  <p>My goal is to not have the service log in a domain user but rather as local system since the domain accounts passwords get reset.</p>
  
  <p>When I use the local system, I get <strong>Access is denied</strong> </p>
  
  <p>Any ideas how how to accomplish this?</p>
  
  <p>StackTace</p>
  
  <pre><code>Access is denied
  
     at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
     at System.Diagnostics.Process.Start()
     at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
     at Ace.WindowsService.ProcessCmd.ProcessCommand.StartProcess(ProcessStartInfo startInfo) in 
  </code></pre>
  
  <p>I have tried wrapping the code in the Impersonate code listed below with no success.</p>
  
  <p><strong>Impersonate Code</strong></p>
  
  <pre><code>public class Impersonation2 : IDisposable
  {
      private WindowsImpersonationContext _impersonatedUserContext;
  
      // Declare signatures for Win32 LogonUser and CloseHandle APIs
      [DllImport("advapi32.dll", SetLastError = true)]
      static extern bool LogonUser(
        string principal,
        string authority,
        string password,
        LogonSessionType logonType,
        LogonProvider logonProvider,
        out IntPtr token);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      static extern bool CloseHandle(IntPtr handle);
  
      [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      static extern int DuplicateToken(IntPtr hToken,
          int impersonationLevel,
          ref IntPtr hNewToken);
  
      [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      static extern bool RevertToSelf();
  
      // ReSharper disable UnusedMember.Local
      enum LogonSessionType : uint
      {
          Interactive = 2,
          Network,
          Batch,
          Service,
          NetworkCleartext = 8,
          NewCredentials
      }
      // ReSharper disable InconsistentNaming
      enum LogonProvider : uint
      {
          Default = 0, // default for platform (use this!)
          WinNT35,     // sends smoke signals to authority
          WinNT40,     // uses NTLM
          WinNT50      // negotiates Kerb or NTLM
      }
      // ReSharper restore InconsistentNaming
      // ReSharper restore UnusedMember.Local
  
      /// &lt;summary&gt;
      /// Class to allow running a segment of code under a given user login context
      /// &lt;/summary&gt;
      /// &lt;param name="user"&gt;domain\user&lt;/param&gt;
      /// &lt;param name="password"&gt;user's domain password&lt;/param&gt;
      public Impersonation2(string domain, string username, string password)
      {
          var token = ValidateParametersAndGetFirstLoginToken(username, domain, password);
  
          var duplicateToken = IntPtr.Zero;
          try
          {
              if (DuplicateToken(token, 2, ref duplicateToken) == 0)
              {
                  throw new Exception("DuplicateToken call to reset permissions for this token failed");
              }
  
              var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
              _impersonatedUserContext = identityForLoggedOnUser.Impersonate();
              if (_impersonatedUserContext == null)
              {
                  throw new Exception("WindowsIdentity.Impersonate() failed");
              }
          }
          finally
          {
              if (token != IntPtr.Zero)
                  CloseHandle(token);
              if (duplicateToken != IntPtr.Zero)
                  CloseHandle(duplicateToken);
          }
      }
  
      private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
      {
  
  
          if (!RevertToSelf())
          {
              throw new Exception("RevertToSelf call to remove any prior impersonations failed");
              ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
  
          }
  
          IntPtr token;
  
          var result = LogonUser(domain, username,
                                 password,
                                 LogonSessionType.Interactive,
                                 LogonProvider.Default,
                                 out token);
          if (!result)
          {
              var errorCode = Marshal.GetLastWin32Error();
              ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user.  LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
              throw new Exception("Logon for user " + username + " failed.");
          }
          return token;
      }
  
      public void Dispose()
      {
          // Stop impersonation and revert to the process identity
          if (_impersonatedUserContext != null)
          {
              _impersonatedUserContext.Undo();
              _impersonatedUserContext = null;
          }
      }
  </code></pre>
  
  <p><strong>Update</strong></p>
  
  <p>This works fine if I am just running if I am just executing it. But when it is running as a service, it will not work</p>
  
  <p><strong>Update 2</strong></p>
  
  <p>I am not getting the access denied from the Process.Start when I change the impersonating login to LogonSessionType.NewCredentials and remove the crednetials from the process.  But I now see an error when running the robocopy command. When I do have the credentials on the process it does not produce a log file from the robocopy command</p>
  
  <p><strong>Error</strong></p>
  
  <pre><code>2016/07/16 09:19:12 ERROR 5 (0x00000005) 
  Accessing Source Directory \\[server]\[path]\
  Access is denied.
  </code></pre>
  
  <p><strong>Change</strong></p>
  
  <pre><code>var result = LogonUser(domain, username,
     password,
     LogonSessionType.NewCredentials,
     LogonProvider.Default,
     out token);
  </code></pre>
  
  <p>Update 3</p>
  
  <p>The copy and move functions are working.  But creating sub process is not.    I have been playing with CreateProcessAsUser as Hary Johnston has suggested.</p>