locked
Using LDAP to get all users in Active Directory RRS feed

  • Question

  • Hey All -

    I'm designing a program to search through all users in Active Directory and get the lastLoginTimeStamp attribute.  From there I'm having a report(.csv) genererated based on who hasn't logged in 30,60, 0r 90 days.  I believe I'm having a problem in my loop somewhere.  My code works when I LDAP an OU with users inside but when I try to get it to seach subcontainers I get several exceptions thrown at me.  Here is a sample of my loop code: 

            'Initialize and open report file
            ReportFile = My.Computer.FileSystem.OpenTextFileWriter(My.Settings.ReportsPath & "\LoginAuditReport.csv", False)

            'LDAP properties from active directory and run search
            Dim domain As DirectoryEntry = New DirectoryEntry("LDAP://OU=Schools,OU=Forsyth,DC=domain,DC=com")
            Dim dirSearcher As DirectorySearcher = New DirectorySearcher(domain)
            dirSearcher.Filter = ("(objectClass=user)")
            dirSearcher.PropertiesToLoad.Add("Department")
            dirSearcher.PropertiesToLoad.Add("lastLogonTimestamp")
            Dim dirSearchResults As SearchResult

            If MainForm.AuditUsersRB.Checked = True Then
                For Each dirSearchResults In dirSearcher.FindAll()
                    Try
                        'Get User Name, LastLogon, and Department Name from AD
                        TempName = dirSearchResults.GetDirectoryEntry().Name.ToString()
                        LastLogon = dirSearchResults.GetDirectoryEntry().Properties("lastLogonTimestamp").Value
                        DepartmentName = dirSearchResults.GetDirectoryEntry().Properties("Department").Value

    Thanks for any help you can provide!

    • Edited by Big Irish Friday, May 1, 2009 8:12 PM
    Friday, May 1, 2009 8:05 PM

Answers

  • Imports System
    Imports System.Collections.Generic
    Imports System.DirectoryServices
    Module ListAdUsers
    	Sub Main(ByVal ParamArray Args() As String)
    		Console.Clear()
    		Dim userList As List(Of String) = New List(Of String)
    		Dim badEntries As Integer = 0
    		Dim domainName As String = String.Empty
    		If (Args.Length > 0) Then
    			domainName = Args(0)
    		Else
    			Console.Write(String.Format("{0}Please enter your Active Directory domain name: ", vbCrLf))
    			domainName = Console.ReadLine()
    		End If
    		Console.Write(String.Format("{0}Attempting to build user list for {1} ...{0}{0}", vbCrLf, domainName))
    		Try
    			If Not String.IsNullOrEmpty(domainName) Then
    				Dim myDirectoryEntry As DirectoryEntry = New DirectoryEntry(String.Format("LDAP://{0}", domainName))
    				Dim mySearcher As DirectorySearcher = New DirectorySearcher(myDirectoryEntry)
    				Dim mySort As SortOption = New SortOption("sn", SortDirection.Ascending)
    				mySearcher.Filter = ("(objectClass=user)")
    				mySearcher.Sort = mySort
    				For Each resEnt As SearchResult In mySearcher.FindAll()
    					Try
    						If Not String.IsNullOrEmpty(resEnt.Properties("Mail")(0).ToString()) _
    							AndAlso System.Text.RegularExpressions.Regex.IsMatch(resEnt.Properties("DisplayName")(0).ToString(), " |admin|test|service|system|[$]", System.Text.RegularExpressions.RegexOptions.IgnoreCase) Then
    							Dim space As Integer = resEnt.Properties("DisplayName")(0).ToString().IndexOf(" ")
    							Dim formattedName As String = String.Format("{0}{1}{2}", _
    																		resEnt.Properties("DisplayName")(0).ToString().Substring(space).PadRight(25), _
    																		resEnt.Properties("DisplayName")(0).ToString().Substring(0, space).PadRight(15), _
    																		resEnt.Properties("Mail")(0).ToString() _
    																		)
    							userList.Add(formattedName)
    						End If
    					Catch
    						badEntries = badEntries + 1
    					End Try
    				Next
    				If (userList.Count > 0) Then
    					Console.WriteLine(String.Format("=========== Listing of users in the {0} domain{1}", domainName, vbCrLf))
    					Console.WriteLine(String.Format("{0}{1}{2}{3}", "Surname".PadRight(25), "First Name".PadRight(15), "Email Address", vbCrLf))
    					For i = 0 To userList.Count - 1
    						Console.WriteLine(userList(i).ToString())
    					Next
    					Console.WriteLine(String.Format("{0}=========== {1} users found in the {2} domain", vbCrLf, userList.Count.ToString(), domainName))
    				Else
    					Console.WriteLine(String.Format("{0}=========== 0 users found in the {1} domain", vbCrLf, userList.Count.ToString()))
    				End If
    				Console.WriteLine(String.Format("=========== {0} objects could not be read", badEntries.ToString()))
    				Console.WriteLine("=========== End of Listing")
    			Else
    				Console.WriteLine("Please enter a domain name next time!")
    			End If
    		Catch ex As Exception
    			' in a production app you wouldn't show the user the exception details
    			Console.Write(String.Format("A critical error occurred.{0}Details: {1}", vbCrLf, ex.Message.ToString()))
    		End Try
    	End Sub
    End Module
    
    
    

    http://digitalformula.net/net-geekery/vbnet-list-active-directory-users-re-write/
    Thanks, A.m.a.L | [Remember to click "mark as answered" when you get a correct reply to your question]
    • Marked as answer by Xingwei Hu Friday, May 8, 2009 3:01 AM
    Friday, May 1, 2009 8:17 PM

All replies

  • Imports System
    Imports System.Collections.Generic
    Imports System.DirectoryServices
    Module ListAdUsers
    	Sub Main(ByVal ParamArray Args() As String)
    		Console.Clear()
    		Dim userList As List(Of String) = New List(Of String)
    		Dim badEntries As Integer = 0
    		Dim domainName As String = String.Empty
    		If (Args.Length > 0) Then
    			domainName = Args(0)
    		Else
    			Console.Write(String.Format("{0}Please enter your Active Directory domain name: ", vbCrLf))
    			domainName = Console.ReadLine()
    		End If
    		Console.Write(String.Format("{0}Attempting to build user list for {1} ...{0}{0}", vbCrLf, domainName))
    		Try
    			If Not String.IsNullOrEmpty(domainName) Then
    				Dim myDirectoryEntry As DirectoryEntry = New DirectoryEntry(String.Format("LDAP://{0}", domainName))
    				Dim mySearcher As DirectorySearcher = New DirectorySearcher(myDirectoryEntry)
    				Dim mySort As SortOption = New SortOption("sn", SortDirection.Ascending)
    				mySearcher.Filter = ("(objectClass=user)")
    				mySearcher.Sort = mySort
    				For Each resEnt As SearchResult In mySearcher.FindAll()
    					Try
    						If Not String.IsNullOrEmpty(resEnt.Properties("Mail")(0).ToString()) _
    							AndAlso System.Text.RegularExpressions.Regex.IsMatch(resEnt.Properties("DisplayName")(0).ToString(), " |admin|test|service|system|[$]", System.Text.RegularExpressions.RegexOptions.IgnoreCase) Then
    							Dim space As Integer = resEnt.Properties("DisplayName")(0).ToString().IndexOf(" ")
    							Dim formattedName As String = String.Format("{0}{1}{2}", _
    																		resEnt.Properties("DisplayName")(0).ToString().Substring(space).PadRight(25), _
    																		resEnt.Properties("DisplayName")(0).ToString().Substring(0, space).PadRight(15), _
    																		resEnt.Properties("Mail")(0).ToString() _
    																		)
    							userList.Add(formattedName)
    						End If
    					Catch
    						badEntries = badEntries + 1
    					End Try
    				Next
    				If (userList.Count > 0) Then
    					Console.WriteLine(String.Format("=========== Listing of users in the {0} domain{1}", domainName, vbCrLf))
    					Console.WriteLine(String.Format("{0}{1}{2}{3}", "Surname".PadRight(25), "First Name".PadRight(15), "Email Address", vbCrLf))
    					For i = 0 To userList.Count - 1
    						Console.WriteLine(userList(i).ToString())
    					Next
    					Console.WriteLine(String.Format("{0}=========== {1} users found in the {2} domain", vbCrLf, userList.Count.ToString(), domainName))
    				Else
    					Console.WriteLine(String.Format("{0}=========== 0 users found in the {1} domain", vbCrLf, userList.Count.ToString()))
    				End If
    				Console.WriteLine(String.Format("=========== {0} objects could not be read", badEntries.ToString()))
    				Console.WriteLine("=========== End of Listing")
    			Else
    				Console.WriteLine("Please enter a domain name next time!")
    			End If
    		Catch ex As Exception
    			' in a production app you wouldn't show the user the exception details
    			Console.Write(String.Format("A critical error occurred.{0}Details: {1}", vbCrLf, ex.Message.ToString()))
    		End Try
    	End Sub
    End Module
    
    
    

    http://digitalformula.net/net-geekery/vbnet-list-active-directory-users-re-write/
    Thanks, A.m.a.L | [Remember to click "mark as answered" when you get a correct reply to your question]
    • Marked as answer by Xingwei Hu Friday, May 8, 2009 3:01 AM
    Friday, May 1, 2009 8:17 PM
  • This worked perfect.  Now here my newest thing.  I msgbox'd the total and when I do my loop through AD I'm searching through 83,000 objects.  When I run my program it takes a while to run (understandably) but midway through I get the following popup:

    ContextSwitchDeadlock was detected
    Message: The CLR has been unable to transition from COM context 0x4dbc88 to COM context 0x4dbdf8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

    Then after running a while longer I get the following:

    System.OutOfMemoryException was unhandled
      Message="Exception of type 'System.OutOfMemoryException' was thrown."
      Source="System.Windows.Forms"
      StackTrace:
           at System.Windows.Forms.UnsafeNativeMethods.EnumThreadWindows(Int32 dwThreadId, EnumThreadWindowsCallback lpfn, HandleRef lParam)    at System.Windows.Forms.Application.ThreadWindows..ctor(Boolean onlyWinForms)    at System.Windows.Forms.Application.ThreadContext.DisableWindowsForModalLoop(Boolean onlyWinForms, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.BeginModalMessageLoop(ApplicationContext context)    at System.Windows.Forms.Application.BeginModalMessageLoop()    at System.Windows.Forms.MessageBox.ShowCore(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options, Boolean showHelp)    at System.Windows.Forms.MessageBox.Show(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options)    at Microsoft.VisualBasic.Interaction.MsgBox(Object Prompt, MsgBoxStyle Buttons, Object Title)    at Meatball.LDAPScripts.GetUser90Day()    at Meatball.MainForm.Button1_Click(Object sender, EventArgs e)    at System.Windows.Forms.Control.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)    at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)    at System.Windows.Forms.Control.WndProc(Message& m)    at System.Windows.Forms.ButtonBase.WndProc(Message& m)    at System.Windows.Forms.Button.WndProc(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)    at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.Run(ApplicationContext context)    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)    at Meatball.My.MyApplication.Main(String[] Args)    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.ThreadHelper.ThreadStart()
      InnerException:

    I see there is a way to turn this warning off but is there a way to code around it?
    Monday, May 18, 2009 2:19 PM
  • This worked perfect.  Now here my newest thing.  I msgbox'd the total and when I do my loop through AD I'm searching through 83,000 objects.  When I run my program it takes a while to run (understandably) but midway through I get the following popup:

    ContextSwitchDeadlock was detected
    Message: The CLR has been unable to transition from COM context 0x4dbc88 to COM context 0x4dbdf8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

    Then after running a while longer I get the following:

    System.OutOfMemoryException was unhandled
      Message="Exception of type 'System.OutOfMemoryException' was thrown."
      Source="System.Windows.Forms"
      StackTrace:
           at System.Windows.Forms.UnsafeNativeMethods.EnumThreadWindows(Int32 dwThreadId, EnumThreadWindowsCallback lpfn, HandleRef lParam)    at System.Windows.Forms.Application.ThreadWindows..ctor(Boolean onlyWinForms)    at System.Windows.Forms.Application.ThreadContext.DisableWindowsForModalLoop(Boolean onlyWinForms, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.BeginModalMessageLoop(ApplicationContext context)    at System.Windows.Forms.Application.BeginModalMessageLoop()    at System.Windows.Forms.MessageBox.ShowCore(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options, Boolean showHelp)    at System.Windows.Forms.MessageBox.Show(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options)    at Microsoft.VisualBasic.Interaction.MsgBox(Object Prompt, MsgBoxStyle Buttons, Object Title)    at Meatball.LDAPScripts.GetUser90Day()    at Meatball.MainForm.Button1_Click(Object sender, EventArgs e)    at System.Windows.Forms.Control.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnClick(EventArgs e)    at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)    at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)    at System.Windows.Forms.Control.WndProc(Message& m)    at System.Windows.Forms.ButtonBase.WndProc(Message& m)    at System.Windows.Forms.Button.WndProc(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)    at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)    at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)    at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)    at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)    at System.Windows.Forms.Application.Run(ApplicationContext context)    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()    at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)    at Meatball.My.MyApplication.Main(String[] Args)    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)    at System.Threading.ThreadHelper.ThreadStart()
      InnerException:

    I see there is a way to turn this warning off but is there a way to code around it?
    Monday, May 18, 2009 2:20 PM