[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 440
  • Last Modified:

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

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
fruitloopy
Asked:
fruitloopy
  • 6
  • 5
1 Solution
 
sAMAccountNameSr. Systems EngineerCommented:
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
 
it_saigeDeveloperCommented:
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
 
fruitloopyAuthor Commented:
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
NEW Veeam Backup for Microsoft Office 365 1.5

With Office 365, it’s your data and your responsibility to protect it. NEW Veeam Backup for Microsoft Office 365 eliminates the risk of losing access to your Office 365 data.

 
it_saigeDeveloperCommented:
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
 
fruitloopyAuthor Commented:
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
 
it_saigeDeveloperCommented:
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
 
fruitloopyAuthor Commented:
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
 
it_saigeDeveloperCommented:
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
 
fruitloopyAuthor Commented:
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
 
it_saigeDeveloperCommented:
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
 
fruitloopyAuthor Commented:
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
 
fruitloopyAuthor Commented:
Awesome job. Thanks very much for your help.
0

Featured Post

Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

  • 6
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now