1
votes

We are attempting to automate a large number of our azure/service maintenance tasks using a combination of Azure Service Bus queues and Azure Worker Roles. In short, the concept is as follows....

  1. Maintenance task is posted to SB Queue
  2. Worker role listens for tasks on the SB Queue
  3. Worker role connects to desired VM/Web Role/Cloud service and executes a remote powershell command

In practice, this works as expected when operating within a development environment, however after the worker role is published, the remote powershell connection fails with the response "Access is denied". The code to establish the connection is as follows...

PSCredential cred = new PSCredential(config.Username, config.PrimaryPassword);
WSManConnectionInfo connection = new WSManConnectionInfo(true, config.PrimaryServer, 5986, "/wsman", "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", cred);

using (Runspace runspace = RunspaceFactory.CreateRunspace(connection))
{
    runspace.Open();
    using (PowerShell shell = PowerShell.Create())
    {
        shell.Runspace = runspace;
        // DO SOMETHING HERE
        shell.Invoke();
    }
    runspace.Close();
}

Initially, I had suspected that this was a CA certificate issue, however I have since connected to the worker role via RDP and confirmed that the certificates are being deployed correctly. In addition I have also managed to acheive a connection to the target server via a "winrs -r:" command also using the remote desktop connection.

As confirmation, the worker role is also running with elevated permissions.

Any help with this would be much appreciated

Thanks in advance

1

1 Answers

0
votes

After lots of experimentation, it would appear that the Runspace.Open() command needs to run under an account with administrative access (running the worker role with elevated permissions doesn't achieve this), therefore in order to resolve the issue, I did the following...

Using a startup task on the role, I created an account using the following command...

net user roleusername rolepassword /add
net localgroup Administrators roleusername /add
exit /B 0

I then impersonated the user with the following code, to ensure the role was running as the newly created local administrator account.

[DllImport("advapi32.DLL", SetLastError = true)]
public static extern int LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

[DllImport("advapi32.DLL")]
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken);  

[DllImport("advapi32.DLL")]
public static extern bool RevertToSelf();

public static object Impersonate(string username, string password)
{
    string domainname = ".";
    if (username.Contains(@"\"))
    {
        domainname = username.Substring(0, username.IndexOf(@"\"));
        username = username.Substring(username.IndexOf(@"\") + 1);
    }

    IntPtr securityToken;

    LogonUser(username, domainname, password, 9, 0, out securityToken);
    if (securityToken != IntPtr.Zero)
    {
        var newIdentity = new WindowsIdentity(securityToken);
        WindowsImpersonationContext impersonationContext = newIdentity.Impersonate();

        return impersonationContext;
    }

    throw new InvalidOperationException("The username or password combination was invalid, please verify your settings");
}

public static void UndoImpersonation(object impersonationContext)
{
    var context = impersonationContext as WindowsImpersonationContext;
    if (context != null) context.Undo();
}

I hope this helps anybody else running into the same problem.