Get First & Last Name of All AD Accounts - vb.net

I'm looking for the quickest and easiest way to retrieve all Active Directory account's first & last names. The intention is to have a string list which contains all names from the Active Directory, for the purpose of auto-completion in a textbox in a small Visual Basic application used for unlocking accounts.
Usage Scenario:
On form load the application generates the list of names from the AD. I'm expecting this to take around 10-15 seconds as there's 4,500 accounts on the AD. Each name is added to a string list for use with the auto completion.
The user types the name Garry into textbox1 and the auto complete suggests all Garry's in the AD, by using a string list. I know how to do this easily, I just dont know how to populate the list with user's names efficiently.
There's a lot of samples on accessing the AD, but none which show how to loop through it. I thought asking on here would help both myself and other users in a similar usage case.
The code I have so far for accessing a single account is shown below, however I need to loop through all AD accounts and retrieve their First & Last names.
Current Code to access single account:
'Domain is declared with the LDAP path
'UserName is declared with textbox1.text value
Dim ADEntry As New DirectoryServices.DirectoryEntry("LDAP://" & Domain)
Dim ADSearch As New System.DirectoryServices.DirectorySearcher(ADEntry)
ADSearch.Filter = ("(samAccountName=" & UserName & ")")
ADSearch.SearchScope = System.DirectoryServices.SearchScope.Subtree
Dim UserFound As System.DirectoryServices.SearchResult = ADSearch.FindOne()
If Not IsNothing(UserFound) Then
Log.AppendLine("Account found, loading checks...")
Dim Attrib As String = "msDS-User-Account-Control-Computed"
Dim User As System.DirectoryServices.DirectoryEntry
User = UserFound.GetDirectoryEntry()
User.RefreshCache(New String() {Attrib})
'Display user account details
txtLogin.Text = User.Properties("userPrincipalName").ToString
txtName.Text = User.Properties("givenName").ToString & " " & User.Properties("sn").ToString
else
'User not found
end if
Any help would be much appreciated, even in C#.

You can use the same ADEntry variable as above and do something like this. This only adds a user to the list if they have both a first and last name.
Dim listNames As New AutoCompleteStringCollection
Using ADSearch As New DirectoryServices.DirectorySearcher(ADEntry, "(&(objectCategory=person)(objectClass=user))", {"givenName", "sn"}, DirectoryServices.SearchScope.Subtree)
For Each user As DirectoryServices.SearchResult In ADSearch.FindAll
Try
listNames.Add(user.GetDirectoryEntry.Properties("givenName").Value.ToString + " " + user.GetDirectoryEntry.Properties("sn").Value.ToString)
Catch ex As Exception
End Try
Next
End Using
With TextBox1
.AutoCompleteCustomSource = listNames
.AutoCompleteMode = AutoCompleteMode.SuggestAppend
.AutoCompleteSource = AutoCompleteSource.CustomSource
End With

Related

Show/Hide text box for email input based on AD user result

I'm still a bit of a VB Noob and I am having some trouble getting this to work correctly. The code looks like it should work, but I am obviously missing something...
What I'm trying to do is to get the users email address from DirectoryServices when the form loads & If there is no AD user email available, show (unhide) a text box to allow the user to enter their email address. What I have will pull the users email from AD, but will not show the textbox if Directy services is not available. I have tried moving UserEmailAdd.Visible = False and lblEmail.Visible = False to the Form Load and the setting the value to True, but I'm not having much luck
Here is what I have so far:
Private Sub FormTicket_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.CenterToScreen()
BtnSCR.Hide()
LblScreenshot.Hide()
'Get current users email address from AD or input
Dim currentADUser As System.DirectoryServices.AccountManagement.UserPrincipal
currentADUser = System.DirectoryServices.AccountManagement.UserPrincipal.Current
Dim userEmail = currentADUser.EmailAddress
If currentADUser IsNot Nothing Then
' user exists
LblEmail.Visible = False
UserEmailAdd.Visible = False
txtSummary.Focus()
MailUser = userEmail
'Catch ex As Exception
Else
' user does *not* exist
UserEmailAdd.Visible = True
LblEmail.Visible = True
UserEmailAdd.Focus()
MailUser = UserEmailAdd.Text
'End Try
End If
So after some testing (trial & error), I was able to make this work. This may not be the best way, but it suits my purpose for now and I will continue to work on cleaning it up.
I determined if there was a local domain and if the user had an account & email address. If so, I populated the UserEmailAdd textbox with this value and made the label and field hidden. If no local domain/user was found, the label and textbox were then unhidden and the user was now able to enter their email address which in both cases provided the users mail.From address.
Here is what I did:
Try
' check if domain user exists
Dim currentADUser As System.DirectoryServices.AccountManagement.UserPrincipal
currentADUser = System.DirectoryServices.AccountManagement.UserPrincipal.Current
Dim userEmail = currentADUser.EmailAddress
If currentADUser.EmailAddress IsNot Nothing Then
LblEmail.Hide()
UserEmailAdd.Hide()
UserEmailAdd.Text = userEmail
txtSummary.Focus()
End If
' local domain user does not exist
Catch ex As Exception
UserEmailAdd.Show()
LblEmail.Show()
UserEmailAdd.Focus()
End Try
...
mail.From = New MailAddress(UserEmailAdd.Text)
jmcilhinney, thank you for prompting some more thought on my part. I'm obviously not any sort of programmer and I have A LOT to learn yet. But your comments helped me to take another look at this and figure out a different way.

How do I get only one result for each app instead of double?

Copy this into Visual Studio, add a textbox and it'll run.
Const NET_FW_ACTION_ALLOW = 1
Dim fwPolicy2 = CreateObject("HNetCfg.FwPolicy2")
Dim RulesObject = fwPolicy2.Rules
For Each rule In RulesObject
If rule.action = NET_FW_ACTION_ALLOW Then
TextBox1.Text += rule.name & vbnewline
End If
Next
This is an example of what I get but I only need each app to be listed once, not two times. What am I doing wrong or why does it behave like this?
qBittorrent
qBittorrent
Chrome
Chrome
Visual Studio
Visual Studio
and so on...
It behaves like this because rule.Name is not a unique identifier for a firewall rule. The same rule name may be used for different protocols (TCP, UDP), profiles (domain, private, public), direction (in, out), etc. If you are only interested in rule.Name, add them to a set, then print that set, as follows.
Const NET_FW_ACTION_ALLOW = 1
Dim fwPolicy2 = CreateObject("HNetCfg.FwPolicy2")
Dim RulesObject = fwPolicy2.Rules
Dim names As New HashSet(Of String)
' Create set of unique names.
For Each rule In fwPolicy2.Rules
If rule.action = NET_FW_ACTION_ALLOW Then
names.Add(rule.name)
End If
Next
' Add names to TextBox.
For Each name As String In names
TextBox1.Text += name & vbNewLine
Next
For Each rule In RulesObject
If rule.action = NET_FW_ACTION_ALLOW AndAlso TextBox1.Text.Contains(rule.name.ToString) = False Then
TextBox1.Text += rule.name & vbnewline
End If
Next
The above is one way to do it. It simply checks whether it's already added to the textbox. Btw, I don't know offhand whether or not rule.name is already a string so I added .ToString; if it's already a string, you don't need to add that.
Also, most of us would recommend using Option Strict, and declaring your variables as a type. i.e. Dim myVar as String = "some string"

vb .net search AD and get samaccountname by filtering employeeID property

I can't seem to load do anything with the employee id using this code
Private Sub AAA(badge As String)
Console.WriteLine("AD search for " & badge)
Dim rootEntry As New DirectoryEntry("GC://dc=contoso,dc=com", "username", "password")
Dim searcher As New DirectorySearcher(rootEntry)
searcher.Filter = "(&(objectCategory=person)(employeeid='" + badge + "'))"
searcher.PropertiesToLoad.Add("EmployeeID")
searcher.PropertiesToLoad.Add("cn")
searcher.PropertiesToLoad.Add("sAMAccountName")
Dim results As SearchResultCollection = searcher.FindAll()
Console.WriteLine("Results: " & results.Count)
For Each result As SearchResult In results
Console.WriteLine(result.Properties("sAMAccountName")(0))
Console.WriteLine(result.Properties("cn")(0))
Console.WriteLine(result.Properties("employeeID")(0))
'list fields
For i As Integer = 0 To result.Properties.Count
Console.WriteLine(result.Properties.PropertyNames(i))
Next
Next
End Sub
I have tried removing (employeeid=' + badge +') from filter and list all users to display each users employee id and maby loop through them but no luck
this code returns samaccountname, cn, adspath (which i didn't ask for) but no employeeid
There is one user who has an employee id value and i can get the user using the following powershell command
Get-ADUser -LDAPFilter "(employeeID=*)"
Can anyone see the error?
I have remembered that, a few years ago, I have written a simple AD query&export tool for ad that also shows a complete list of LDAP fields and when I ran it just now, I saw the EmployeeID field in the list.
Here is the code that I used to solve the (current) problem.
Dim searcher As DirectorySearcher = New DirectorySearcher(New DirectoryEntry("LDAP://" & Environment.UserDomainName), "(&(objectCategory=person)(employeeid=" + badge + "))")
searcher.PropertiesToLoad.Add("EmployeeID")
searcher.PropertiesToLoad.Add("cn")
searcher.PropertiesToLoad.Add("sAMAccountName")
Dim foundUser As SearchResultCollection = searcher.FindAll
For Each result As SearchResult In foundUser
Console.WriteLine(result.Properties("sAMAccountName")(0))
Console.WriteLine(result.Properties("cn")(0))
Console.WriteLine(result.Properties("employeeID")(0))
Next
I don't know why, but passing the filter at initialization works properly.
I am also not passing the username and password to the DirectoryEntry object (even though the user that I was using is in the Domain Administrators group.
How to use below PowerShell code in the VB.net directory searcher.filter option
Get-ADUser -Filter {Enabled -eq "True"}

"IF" code is not working inside for each?

so im learning to use socket and thread things in the networking software. so far, the software (which is not created by me) is able to chat in multiple group, but i'm tasked to allow user to code whisper feature. However, im stuck in the coding area, which im sure will work if the "if" function work inside "for each" function, anyhow here is my code mainly
Private clientCollection As New Hashtable()
Private usernameCollection As New Hashtable()
clientCollection.Add(clientID, CData)
usernameCollection.Add(clientID, username)
oh and before i forgot, the code above and below is on the server form page
on the client side, i write the code:
writer.write("PMG" & vbnewline & txtReceiverUsername & Message)
then next is the checking part on the server reading the message:
ElseIf message.Substring(0, 3) = "PMG" Then
'filter the message, check who to send to
Dim newMessage As String = message.Substring(3)
Dim messagearray As String() = newMessage.Split(vbNewLine)
Dim receiver As String = messagearray(1)
'0 = "", 1 = receiver, 2 = message
as i write before, clientcollection contain (clientID , connection data*) and usernamecollection contain (clientID, username). In my case, i only have the username data, and i need to trace it until the connection data on clientcollection hash table.
'find realid from usernamecollection, then loop clientcollection
Dim clientKey As String = 0
For Each de As DictionaryEntry In usernameCollection
'''''
'this if part Is Not working
If de.Value Is receiver Then
clientKey = de.Key
End If
'''''
Next de
'match objKey with clientcollection key
For Each dec As DictionaryEntry In clientCollection
If dec.Key = clientKey Then
Dim clients As ClientData = dec.Value
If clients.structSocket.Connected Then
clients.structWriter.Write("PMG" & messagearray(2))
End If
End If
Next dec
End If
so, how do i know that the if part is the wrong one? simply i tried these code before the "next de" code
For Each client As ClientData In clientCollection.Values
If client.structSocket.Connected Then
client.structWriter.Write("PMG" & "receiver:" & messagearray(1))
client.structWriter.Write("PMG" & "loop username: " & de.Value)
client.structWriter.Write("PMG" & "loop key: " & de.Key)
client.structWriter.Write("PMG" & "receiver key:" & clientKey)
End If
Next
the code allow me to check the de.key and de.value. they were correct, however the only thing that did not work is the code inside the "if" area.
Can anyone suggest other code maybe beside "if de.key = receiver"? I've also tried using the if de.key.equal(receiver) and it did not work too

ASP.NET - Deleting Computer Accounts Within AD

I'm building out a decommissioning application that will allow an individual to provide an computer name and the utility will go out and purge the computer record from various locations. I'm running into a problem when attempting to delete a computer account from Active Directory. I'm impersonating a service account that only has rights to "Delete All Child Objects" within a particular OU structure. The code below works if I run it with my domain admin account; however fails with an "Access Denied" when I run it with the impersonated service account. I have verified that the permissions are correct within AD as I can launch Active Directory Users and Computers using a "runas" and providing the service account credentials and I can delete computer objects perfectly fine.
Wondering if anyone has run into this before or has a different way to code this while still utilizing my current OU permissions. My gut tells me the "DeleteTree" method is doing more then just deleting the object.
Any assistance would be appreciated.
Sub Main()
Dim strAsset As String = "computer9002"
Dim strADUsername As String = "serviceaccount#domain.com"
Dim strADPassword As String = "password"
Dim strADDomainController As String = "domaincontroller.domain.com"
Dim objDirectoryEntry As New System.DirectoryServices.DirectoryEntry
Dim objDirectorySearcher As New System.DirectoryServices.DirectorySearcher(objDirectoryEntry)
Dim Result As System.DirectoryServices.SearchResult
Dim strLDAPPath As String = ""
Try
objDirectoryEntry.Path = "LDAP://" & strADDomainController
objDirectoryEntry.Username = strADUsername
objDirectoryEntry.Password = strADPassword
objDirectorySearcher.SearchScope = DirectoryServices.SearchScope.Subtree
objDirectorySearcher.Filter = "(&(ObjectClass=Computer)(CN=" & strAsset & "))"
Dim intRecords As Integer = 0
For Each Result In objDirectorySearcher.FindAll
Console.WriteLine(Result.Path)
Diagnostics.Debug.WriteLine("DN: " & Result.Path)
Dim objComputer As System.DirectoryServices.DirectoryEntry = Result.GetDirectoryEntry()
objComputer.DeleteTree()
objComputer.CommitChanges()
intRecords += 1
Next
If intRecords = 0 Then
Console.WriteLine("No Hosts Found")
End If
Catch e As System.Exception
Console.WriteLine("RESULT: " & e.Message)
End Try
End Sub
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
' set up domain context
Dim ctx As New PrincipalContext(ContextType.Domain, "DOMAIN", strADUsername, strADPassword)
' find a computer
Dim computerToDelete As ComputerPrincipal = ComputerPrincipal.FindByIdentity(ctx, strAsset)
If computerToDelete IsNot Nothing Then
' delete the computer, if found
computerToDelete.Delete()
End If
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Delete Tree is different from delete. You're going to need the Delete Subtree permission on the child computer objects for this to work.