I'm working on making a ASP.NET (C#) app that is basically a gateway for running Powershell scripts for routine admin tasks. Some of these scripts use the ActiveDirectory RSAT module and I'm finding that some of these cmdlets will not run correctly when invoked via the gateway and the trace seems to imply that the connection to the domain controller is successful, but is then being shut down by the DC.
The following code is an ASP.NET web form that has one text input to specify a username. Basically, it does the following:
- Assumes the identity of the web user (confirmed to be inherited by powershell)
- Creates a powershell runspace and a pipeline within that runspace
- Invokes the Get-ADUser cmdlet and passes the username as the Identity parameter
Confirms success by reading the user's name into an output element on the form.
protected void LookupButton_Click( object sender, EventArgs e ) { WindowsImpersonationContext impersonationContext = ((WindowsIdentity)User.Identity).Impersonate(); Runspace runspace; Pipeline pipe; try { runspace = new_runspace(); runspace.Open(); pipe = runspace.CreatePipeline(); Command cmd = new Command("Get-ADUser"); cmd.Parameters.Add(new CommandParameter("Identity", text_username.Text)); pipe.Commands.Add(cmd); PSObject ps_out = pipe.Invoke().First(); output.Text = ps_out.Properties["Name"].Value.ToString(); } catch( Exception ex ) { error.Text = ex.ToString(); } finally { impersonationContext.Undo(); } } private Runspace new_runspace( ) { InitialSessionState init_state = InitialSessionState.CreateDefault(); init_state.ThreadOptions = PSThreadOptions.UseCurrentThread; init_state.ImportPSModule(new[] { "ActiveDirectory" }); return RunspaceFactory.CreateRunspace(init_state); }
The interesting part is the specific wording in the error message exposed in the catch block (emphasis mine):
System.Management.Automation.CmdletInvocationException: Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running. ---> Microsoft.ActiveDirectory.Management.ADServerDownException: Unable to contact the server. This may be because this server does not exist, it is currently down, or it does not have the Active Directory Web Services running. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:01:59.6870000'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:01:59.6870000'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
The upper-level exceptions suggest there was a timeout, but there was no timeout indicated by the lower exceptions (and it took only a few seconds to return from the command). In cases where the server is not reachable, the lowest-level exception message says as much, but this specific wording is making me think there is some kind of authentication (or other security) problem at work here.
UPDATE Feb 19, 2013: When using the impersonation method described here, the script runs as expected. This is making me think that the issue may be that the WindowsIdentity object provided by Windows authentication is perhaps unsuitable for a script that essentially makes RPC calls against AD. Unfortunately, it's not really desirable for me to abandon windows auth because I would have to handle the users' passwords in my application code (and that's not a responsibility that I want).
I haven't been able to find any documentation about what exactly windows auth is doing or what kind of impersonation is allowed to result from its use. Is it possible to do this while using windows auth or am I going to have to require the user to give me their password?