Solved

VB.NET - Search Active Directory computers description attribute and return computer name

Posted on 2016-10-13
12
51 Views
Last Modified: 2016-10-17
Background explanation:
We have a logon script that updates the Description attribute of the current logged on computer with the username of the user who just logged in. When a user logs in the computer description attribute will change to display the username:
ExampleI am trying to find the correct way to search all active directory computers description field and return the computer name. This will help us to immediately help a user who phones for support. We dont have to waste time asking them for their computer name, we just ask for their username, type that into the app and it will return the computer name which we can remote to.
This is my code so far:
Dim searchRoot As New DirectoryEntry("LDAP://DC=DOMAIN,DC=local")
        Dim searcher As New DirectorySearcher(searchRoot) With { _
            .Filter = "(objectClass=computer)" _
        }

        searcher.PropertiesToLoad.Add("name")
        searcher.PropertiesToLoad.Add("description")

        searcher.Filter = "(&(objectClass=computer))"
        Dim results = searcher.FindAll

        For Each result In results
            If result.Properties.Contains(txtUsernameSc.Text) Then
                txtHostName.Text.ToString(result.GetDirectoryEntry().Properties("name").Value())
            End If



        Next

Open in new window

Nothing is returned though.
0
Comment
Question by:fruitloopy
  • 6
  • 5
12 Comments
 
LVL 5

Expert Comment

by:sAMAccountName
ID: 41842229
This is so simple using Powershell:

Get-ADComputer -filter "description -like '<some_username>'"

Open in new window


I'm curious why you would use vb for something like this?
0
 
LVL 32

Expert Comment

by:it_saige
ID: 41842241
Considering that there is the possibility that a user can be logged into multiple workstations (or have multiple entries in the description field), either of these will return all associated computer names:
Function GetComputers(term As String) As IEnumerable(Of String)
	Using searcher As New DirectorySearcher("(&(objectClass=computer))")
		searcher.PropertiesToLoad.Add("name")
		searcher.PropertiesToLoad.Add("description")
		Return (From computer As SearchResult In searcher.FindAll()
			   Let entry = computer.GetDirectoryEntry()
			   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
			   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
			   Select CType(entry.Properties("name").Value, String))
	End Using
End Function

Open in new window

-Or-
Iterator Function GetComputers(term As String) As IEnumerable(Of String)
	Using searcher As New DirectorySearcher("(&(objectClass=computer))")
		searcher.PropertiesToLoad.Add("name")
		searcher.PropertiesToLoad.Add("description")
		For Each computer As SearchResult In searcher.FindAll()
			Dim entry = computer.GetDirectoryEntry()
			If Not String.IsNullOrEmpty(entry.Properties("description").Value) AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1 Then
				Yield entry.Properties("name").Value.ToString()
			End If
		Next
	End Using
End Function

Open in new window


Each of which produce the same results.

Proof of concept -
Imports System.DirectoryServices

Module Module1
	Sub Main()
		For Each computer In GetComputers(Environment.GetEnvironmentVariable("USERNAME"))
			Console.WriteLine("{0} is logged into: {1}", Environment.GetEnvironmentVariable("USERNAME"), computer)
		Next
		Console.ReadLine()
	End Sub

	Function GetComputers(term As String) As IEnumerable(Of String)
		Using searcher As New DirectorySearcher("(&(objectClass=computer))")
			searcher.PropertiesToLoad.Add("name")
			searcher.PropertiesToLoad.Add("description")
			Return (From computer As SearchResult In searcher.FindAll()
				   Let entry = computer.GetDirectoryEntry()
				   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
				   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
				   Select CType(entry.Properties("name").Value, String))
		End Using
	End Function
End Module

Open in new window

Produces the following output -Capture.JPG
-saige-
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 41843492
That. is. AWESOME!

It works great. You're right, there are instances where a user is logged onto more than one PC.
What I would like to do is return the result "User is logged onto more than one computer" but when I tried this with an IF statement it failed.
0
 
LVL 32

Expert Comment

by:it_saige
ID: 41843629
If that is the case then we would change a couple of things (I would also add a third returnable value that would indicate when a user is not described as being logged into a computer).  Proposed changes:
Imports System.DirectoryServices

Module Module1
	Sub Main()
		Console.WriteLine(GetComputer(Environment.GetEnvironmentVariable("USERNAME")))
		Console.ReadLine()
	End Sub

	Function GetComputer(term As String) As String
		Using searcher As New DirectorySearcher("(&(objectClass=computer))")
			searcher.PropertiesToLoad.Add("name")
			searcher.PropertiesToLoad.Add("description")
			Dim computers = (From computer As SearchResult In searcher.FindAll()
				   Let entry = computer.GetDirectoryEntry()
				   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
				   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
				   Select CType(entry.Properties("name").Value, String))

			If computers.Count = 0 Then
				Return String.Format("{0} is not currently logged into any computers", term)
			ElseIf computers.Count > 1 Then
				Return String.Format("{0} is logged into {1} computers", term, computers.Count)
			Else
				Return String.Format("{0} is logged into: {1}", term, computers.First())
			End If
		End Using
	End Function
End Module

Open in new window


-saige-
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 41843718
Fantastic work again. Thank you.

I am struggling to get this to return the results to the txtHostName.Text text box.

I used...
If computers.Count = 0 Then
                txtHostName.Text = "No results"
            ElseIf computers.Count > 1 Then
                txtHostName.Text = computers.Count & " Found"
            Else
                txtHostName.Text = computers.First()
            End If

Open in new window

But this tells me I am logged into 236 computers! I'm assuming this is how many computer objects we have in our AD
0
 
LVL 32

Expert Comment

by:it_saige
ID: 41843754
Possibly (you would have to get a count of the ad objects with a class of computer in order to verify).  The only possible explanation for the discrepancy (so long as you are using the code as posted) is that the search term is in each of the description fields.  What is the full code you are using?

-saige-
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 2

Author Comment

by:fruitloopy
ID: 41843759
Here's all the code:
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
        Cursor = Cursors.WaitCursor
        GetComputers(txtUsernameSc.Text)
        Cursor = Cursors.Default

    End Sub

    Function GetComputers(term As String) As IEnumerable(Of String)

        Application.DoEvents()

        Using searcher As New DirectorySearcher("(&(objectClass=computer))")
            searcher.PropertiesToLoad.Add("name")
            searcher.PropertiesToLoad.Add("description")
            Dim computers = (From computer As SearchResult In searcher.FindAll()
                   Let entry = computer.GetDirectoryEntry()
                   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
                   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
                   Select CType(entry.Properties("name").Value, String))

            If computers.Count = 0 Then
                txtHostName.Text = "No results"
            ElseIf computers.Count > 1 Then
                txtHostName.Text = computers.Count & " Found"
            Else
                txtHostName.Text = computers.First()
            End If
        End Using
    End Function

Open in new window

0
 
LVL 32

Expert Comment

by:it_saige
ID: 41843790
Make sure that txtUsernameSc.Text is not null or empty (which I assume that it is not if you are entering it on a form).  If you are getting all of the domain computers as results then that means that the text you are entering is in the description field of the computers returned.

To troubleshoot, let's do this:
Function GetComputers(term As String) As IEnumerable(Of String)
	Using searcher As New DirectorySearcher("(&(objectClass=computer))")
		searcher.PropertiesToLoad.Add("name")
		searcher.PropertiesToLoad.Add("description")
		Dim computers = (From computer As SearchResult In searcher.FindAll()
			   Let entry = computer.GetDirectoryEntry()
			   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
			   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
			   Select New With {.Name = CType(entry.Properties("name").Value, String), .Description = CType(entry.Properties("description").Value, String)})

		If Not System.IO.File.Exists("descriptions.txt") Then
			System.IO.File.WriteAllLines("descriptions.txt", computers.Select(Function(x) x.ToString()).ToArray())
			Process.Start("descriptions.txt")
		End If

		If computers.Count = 0 Then
			txtHostName.Text = "No Results"
		ElseIf computers.Count > 1 Then
			txtHostName.Text = String.Format("{0} computers Found", computers.Count)
		Else
			txtHostName.Text = computers.First().Name
		End If
		Return computers.Select(Function(x) x.Name)
	End Using
End Function

Open in new window


-saige-
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 41843813
The first test was to search for a username that I know is currently logged into 3 computers. This opened the "description.txt" file with all 3 PCs listed.
The second test was with a username with an exact match in the description field of the computers attribute. The first time I clicked the button it populated the txtHostName.Text field with "236 computers found"
The second time I clicked the button it entered the correct computer name in txtHostName.Text
0
 
LVL 32

Accepted Solution

by:
it_saige earned 500 total points
ID: 41843835
Ok let's make this revision then and try again:
Function GetComputers(term As String) As IEnumerable(Of String)
	Using searcher As New DirectorySearcher("(&(objectClass=computer))")
		searcher.PropertiesToLoad.Add("name")
		searcher.PropertiesToLoad.Add("description")
		Dim computers = (From computer As SearchResult In searcher.FindAll()
			   Let entry = computer.GetDirectoryEntry()
			   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
			   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
			   Select New With {.Name = CType(entry.Properties("name").Value, String), .Description = CType(entry.Properties("description").Value, String)})

		Try
			System.IO.File.WriteAllLines("descriptions.txt", computers.Select(Function(x) x.ToString()).ToArray())
			Process.Start("descriptions.txt")
		End Try

		If computers.Count = 0 Then
			txtHostName.Text = "No Results"
		ElseIf computers.Count > 1 Then
			txtHostName.Text = String.Format("{0} computers Found", computers.Count)
		Else
			txtHostName.Text = computers.First().Name
		End If
		Return computers.Select(Function(x) x.Name)
	End Using
End Function

Open in new window

Make sure to close the text file after each lookup.  It would be interesting to see what is causing the additional computers being found.

Once we get the resolved, you can remove the method to write the text file and delete the file from your system.

-saige-
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 41846762
That seems to have done it, thanks it_saige. Here's the final code:
Function GetComputers(term As String) As IEnumerable(Of String)

        Application.DoEvents()

        Using searcher As New DirectorySearcher("(&(objectClass=computer))")
            searcher.PropertiesToLoad.Add("name")
            searcher.PropertiesToLoad.Add("description")
            Dim computers = (From computer As SearchResult In searcher.FindAll()
                   Let entry = computer.GetDirectoryEntry()
                   Where Not String.IsNullOrEmpty(entry.Properties("description").Value) _
                   AndAlso entry.Properties("description").Value.ToString().IndexOf(term, StringComparison.OrdinalIgnoreCase) > -1
                   Select New With {.Name = CType(entry.Properties("name").Value, String), .Description = CType(entry.Properties("description").Value, String)})


            If computers.Count = 0 Then
                txtHostName.Text = "No Results"
            ElseIf computers.Count > 1 Then
                txtHostName.Text = String.Format("{0} computers Found", computers.Count)
            Else
                txtHostName.Text = computers.First().Name
            End If
            Return computers.Select(Function(x) x.Name)
        End Using

    End Function

Open in new window

0
 
LVL 2

Author Closing Comment

by:fruitloopy
ID: 41846763
Awesome job. Thanks very much for your help.
0

Featured Post

Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

Join & Write a Comment

Do you have users whose passwords are expiring and they are constantly calling you?  Well I sure did and needed a way to put an end to this.  We have a lot of remote users which would not be notified that their passwords were expiring since they wer…
Mapping Drives using Group policy preferences Are you still using old scripts to map your network drives if so this article will show you how to get away for old scripts and move toward Group Policy Preference for mapping them. First things f…
This tutorial will walk an individual through the steps necessary to join and promote the first Windows Server 2012 domain controller into an Active Directory environment running on Windows Server 2008. Determine the location of the FSMO roles by lo…
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles from a Windows Server 2008 domain controller to a Windows Server 2012 domain controlle…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now