2

I'm trying to implement a function that displays some ActiveDirectory information onto a webpage, however when I try to display certain information (the user's manager) for some reason it started throwing the below error at me when it hadn't done that in the past. Can anyone explain what's going on here or suggest a fix?

I'm not writing anything to the StringBuilder after disposal during the using() block for the PowerShell, so I have no idea why this is suddenly throwing the exception when the manager_name string is calling the GetManager function, which I know returns the correct value based on VisualStudio's debugging tool showing the proper value when it rolls into line 752

PrintADInfo_displayresults.InnerHtml = html_results;

which is just prior to the disposal of all the objects and return outside the function. What is bizarre is that when setting manager_name to the empty string or just "test", the code will run fine but using the GetUsersManager function will cause the exception to fire.

Below you'll find the methods in question and the error text.

Error Text

System.ObjectDisposedException: Cannot write to a closed TextWriter. at System.IO.__Error.WriterClosed() at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder) at System.IO.StreamWriter.Flush() at System.Management.Automation.Host.TranscriptionOption.Dispose() at System.Management.Automation.Host.PSHostUserInterface.StopAllTranscribing() at System.Management.Automation.Runspaces.LocalRunspace.DoCloseHelper() at System.Management.Automation.Runspaces.LocalRunspace.CloseHelper(Boolean syncCall) at System.Management.Automation.Runspaces.RunspaceBase.CoreClose(Boolean syncCall) at System.Management.Automation.Runspaces.LocalRunspace.Close() at System.Management.Automation.Runspaces.LocalRunspace.Dispose(Boolean disposing) at System.Management.Automation.PowerShell.Dispose(Boolean disposing) at System.Management.Automation.PowerShell.Dispose() at _Default.PrintADInfo(String username) in h:\Visual Studio 2015\WebSites\WebSite2\Default.aspx.cs:line 753

private void PrintADInfo(string username)
{
    //Print AD information for given username
    AD_PrintADInfoDiv.Attributes["style"] = "display:block"; //Move to the account display
    try
    {
        using (PowerShell ps = PowerShell.Create())
        {
            var sb = new StringBuilder();
            sb.Append("Grabbing AD info for " + username.ToUpper() + " ...");

            //Run command Get-ADUser to get user information from AD
            //Command Information -> https://technet.microsoft.com/en-us/library/hh852208(v=wps.630).aspx
            // Use -Properties to get specific proprties we want, then use Select to ignore some additional ones
            ps.AddScript("Get-ADUser -Properties SamAccountName, CN, EmailAddress, Title, Department, Manager, OfficePhone, Mobile, PasswordLastSet, EmployeeID, co, physicalDeliveryOfficeName " + username + " | select SamAccountName, CN, Title, Department, EmailAddress, Manager, OfficePhone, Mobile, PasswordLastSet, EmployeeID, co, physicalDeliveryOfficeName | ConvertTo-Html");
            Collection<PSObject> psOutput = ps.Invoke();

            //Add user informtion to StringBuilder for output
            foreach (PSObject p in psOutput)
            {
                sb.Append(p.ToString());
            }

            //Evil regex hacking
            //Quotes are doubled to account for C# literals
            //https://stackoverflow.com/questions/9289357/javascript-regular-expression-for-dn
            string pattern = @"(?:[A-Za-z][\w-]*|\d+(?:\.\d+)*)=(?:#(?:[\dA-Fa-f]{2})+|(?:[^,=\+<>#;\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*|""(?:[^\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*"")(?:\+(?:[A-Za-z][\w-]*|\d+(?:\.\d+)*)=(?:#(?:[\dA-Fa-f]{2})+|(?:[^,=\+<>#;\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*|""(?:[^\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*""))*(?:,(?:[A-Za-z][\w-]*|\d+(?:\.\d+)*)=(?:#(?:[\dA-Fa-f]{2})+|(?:[^,=\+<>#;\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*|""(?:[^\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*"")(?:\+(?:[A-Za-z][\w-]*|\d+(?:\.\d+)*)=(?:#(?:[\dA-Fa-f]{2})+|(?:[^,=\+<>#;\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*|""(?:[^\\""]|\\[,=\+<>#;\\""]|\\[\dA-Fa-f]{2})*""))*)*";
            Regex rgx = new Regex(pattern);

            //Replace the user's manager field that's normally in CN format to NTID
            string manager_name = GetUsersManager(username);
            //string manager_name = ""; // <-- Making the manager blank runs fine
            string html_results = rgx.Replace(sb.ToString(), manager_name);


            PrintADInfo_displayresults.InnerHtml = html_results;

            return; // <--- Error gets thrown here
        }
    }
    catch (Exception e)
    {
        PrintADInfo_displayresults.InnerHtml = "Exception caught:" + e;
        return;
    }
}

private string GetUsersManager(string username)
{
    //Returns the NTID of the user's manager, instead of the CN
    string manager_name = "";
    using (PowerShell ps = PowerShell.Create())
    {
        ps.AddScript("(Get-ADUser (Get-ADUser " + username + " -Properties manager).manager).samaccountName");
        Collection<PSObject> psOutput = ps.Invoke();
        foreach (PSObject p in psOutput)
        {
            manager_name = manager_name + p.ToString();
        }
    }
    return manager_name;
}
Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
dizbuster
  • 67
  • 7
  • 1
    Does it work if you start at the `string manager_name = GetUsersManager(username);`, outside a `using (Powershell...) ` block, by supplying a sample username? E.g. set `username` to a valid user name and see if you can call it from there? I think your issue might be that you essentially have a using Powershell block inside another one. – C. Helling Oct 06 '17 at 20:27
  • That seems to have done it, thank you! Moving the GetUsersManager() call to be inside the try() but still before the using() block appears to have solved the issue. – dizbuster Oct 06 '17 at 20:53

1 Answers1

2

I believe you need to move the call to string manager_name = GetUsersManager(username); to outside of the using (PowerShell ps = PowerShell.Create()) {} block. You have a using inside a using, and I think your object is being disposed prematurely.

C. Helling
  • 1,394
  • 6
  • 20
  • 34