How to Get a List of Users from Active Directory

How can I get a list of users from active directory?

If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.

Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory

The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.

There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.

For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.

using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
}
}
}
Console.ReadLine();

Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.

How to efficiently obtain list of users from windows active directory using C#

I had a crack at this using a number of different approaches, as a learning experience.

What I found for myself was that all methods could list a set of adspath values pretty quickly, but once introducing a Console.WriteLine in the iteration caused the performance to drastically vary.

My limited C# knowledge led me to experiment with various methods such as IEnumerator straight over the DirectoryEntry, PrincipleSearcher with context, but both of these methods are slow, and vary greatly depending on what is being done with the information

In the end, this is what I ended up with. It was far and away the fastest, and doesn't take any noticeable performance hit when increasing the options to parse.

Note: this is actually a complete copy/paste powershell wrapper for the class, as I am not currently near a VM with Visual Studio.

$Source = @"
// " " <-- this just makes the code highlighter work
// Syntax: [soexample.search]::Get("LDAP Path", "property1", "property2", "etc...")
// Example: [soexample.search]::Get("LDAP://CN=Users,DC=mydomain,DC=com","givenname","sn","samaccountname","distinguishedname")

namespace soexample
{
using System;
using System.DirectoryServices;

public static class search
{
public static string Get(string ldapPath, params string[] propertiesToLoad)
{
DirectoryEntry entry = new DirectoryEntry(ldapPath);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.SearchScope = SearchScope.OneLevel;
foreach (string p in propertiesToLoad) { searcher.PropertiesToLoad.Add(p); }
searcher.PageSize = 100;
searcher.SearchRoot = entry;
searcher.CacheResults = true;
searcher.Filter = "(sAMAccountType=805306368)";
SearchResultCollection results = searcher.FindAll();

foreach (SearchResult result in results)
{
foreach (string propertyName in propertiesToLoad)
{
foreach (object propertyValue in result.Properties[propertyName])
{
Console.WriteLine(string.Format("{0} : {1}", propertyName, propertyValue));
}
}
Console.WriteLine("");

}
return "";
}
}
}
"@
$Asem = ('System.DirectoryServices','System')
Add-Type -TypeDefinition $Source -Language CSharp -ReferencedAssemblies $Asem

I ran this on a particular domain that had 160 users, and here is the outcome;

Using the example command in the code comments:

PS > Measure-Command { [soexample.search]::Get(args as above..) }

Output:

givenname : John
sn : Surname
samaccountname : john.surname
distinguishedname : CN=John Surname,CN=Users,DC=mydomain,DC=com

etc ... 159 more ...

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 431
Ticks : 4317575
TotalDays : 4.99719328703704E-06
TotalHours : 0.000119932638888889
TotalMinutes : 0.00719595833333333
TotalSeconds : 0.4317575
TotalMilliseconds : 431.7575

Every additional string argument given, seems to increase the total processing time by about 100ms.

Running it with only samaccountname takes only 0.1s to list 160 users, parsed into the console.

Using Microsoft's example here, and modifying it to just list one property, took over 3 seconds, and every additional property took about a second.

A couple notes:

  • (sAMAccountType=805306368) turns out to be more efficient than (&(objectClass=user)(objectCategory=person)) (see https://stackoverflow.com/a/10053397/3544399) and many other examples

  • searcher.CacheResults = true; didn't seem to make any difference (in my domain anyway) whether it was true or explicitly false.

  • searcher.PageSize = 100; makes a measurable difference. I believe the default MaxPageSize on a 2012R2 DC is 1000 (https://technet.microsoft.com/en-us/library/cc770976(v=ws.11).aspx)

  • The properties are not case sensitive (i.e. whatever is given to the searcher is returned in result.Properties.PropertyNames, hence why the foreach loop simply iterates those propertiesToLoad)

  • The three foreach loops at first glance seem un-necessary, but every successful removal of a loop ended up costing much more overhead in cast conversions and running through method extensions.

There may be better ways still, I've seen some elaborate examples with threading and result caching that I just wouldn't know what to do with, but the tuned DirectorySearcher does seem to be the most flexible, and this code here only requires System and System.DirectoryServices namespaces.

Not sure exactly what you do with your "//do stuff" as to whether this would help or not, but I did find this an interesting exercise as I didn't know there were so many ways to do something like this.

Get List of all users of specific ActiveDirectoryGroup

As @Chris Pratt mentioned in his comment, there is no build in way to solve this issue with asp.net core 2.0, but there is an easy way, doing it with C#.

So what I did is very simple, first I created the following class (heavily inspired by: https://stackoverflow.com/a/19604001/9641435)

using System.DirectoryServices.AccountManagement; //can be downloaded via NUGET Package manager
using System.Collections.Generic;

namespace MYNAMESPACE
{
public static class ActiveDirectoryHelper
{
public static List<string> GetAllUserRealNamesFromAdGroup(string i_activeDirectyGroup)
{
var users = new List<string>();

using (var context = new PrincipalContext(ContextType.Domain, "MY.DOMAIN.NAME"))
{
using (var group = GroupPrincipal.FindByIdentity(context, i_activeDirectyGroup))
{
if (group != null)
{
var usersPrincipals = group.GetMembers(true);
foreach (UserPrincipal user in usersPrincipals)
{
//There are also other properties available, but in my case I just need the first and surname:
users.Add($"{user.GivenName} {user.Surname}");
}
}
}
return users;
}
}
}
}

And now from my Controller I simply do the following:

[HttpGet]
public IActionResult MyAction()
{
var myVm = new MyViewModel();

List<string> userList = ActiveDirectoryHelper.GetAllUserRealNamesFromAdGroup("MYGROUP");

//do whatever you want with this list right here:


return View(myVm);
}

I hope this post might help someone else in the future, that's why I posted it as an answer.

How can I get all Active Directory Users username and display name

I did it this way it worked great:

1- Add reference to Active Directory services DLL

2- Add using in your controller:

 using System.DirectoryServices.AccountManagement;

than I created a function to store All Active directory users in database table
bellow is the code hope help someone needs it.

    public ActionResult Create()
{


List<MyADUsers> TheAllADUsers = new List<MyADUsers>();

var context = new PrincipalContext(ContextType.Domain, "MyDoman.org");
var searcher = new PrincipalSearcher(new UserPrincipal(context));

foreach (var result in searcher.FindAll())
{

TheAllADUsers.Add(new MyADUsers { ADUserName = result.SamAccountName, AD_IsMemberOf = result.UserPrincipalName, FullName = result.Name });

}

db.MyADUsersContext.AddRange(TheAllADUsers);
db.SaveChanges();


return View();
}

Add a value to all users in AD

You can use the PowerShell ActiveDirectory module, which is included in the Remote Server Administration Tools (RSAT). Details on how to install it are here.

Then you can use Get-ADUser and pipe the results into Set-ADUser. Something like this:

$badusers = Get-ADUser -Filter "company -ne 'Your Company Name'"
$badusers | Set-ADUser -Company "Your Company Name"

You could do this in one line, but I split it into two so you can inspect the $badusers collection to actually see the users that you changed (just type $badusers into the PowerShell prompt and hit enter).

It may be wise to limit it to, say, 5 or 10 users just to make sure it works the way you want before attempting to change every user. You can do this by adding -ResultSetSize 5 to the Get-ADUsers line.

This also assumes you want to change all user objects to have your company name, even administrative accounts. Keep in mind that this will stop processing users if it hits one that you don't have permission to modify. If you want to limit it to a single OU, you can use the -SearchBase parameter of Get-ADUsers, like this:

$badusers = Get-ADUser -Filter "company -ne 'Your Company Name'" -SearchBase "OU=Users,DC=example,DC=com"

How to filter users based on several criteria in Powershell when using Get-AdUser

As Abraham pointed out in his helpful comment, you can do the filtering using exclusively the AD Filter / LDAP Filter.

Here is a -LDAPFilter alternative:

$map = @{
department = @(
'Sales & Admin - All'
'Field - Support'
'HKLM - All'
'SOD - 1'
'Home - 1080'
)
title = @(
'Client Manager'
'Local Sales'
'Outside Sales'
'Region Manager'
'Deployment Manager'
)
}

$ldapfilter = "(&"
foreach($key in $map.Keys) {
$clause = "(|"
foreach($value in $map[$key]) {
$clause += "($key=$value)"
}
$clause += ")"
$ldapfilter += $clause
}
$ldapfilter += ")"

Get-ADUser -LDAPFilter $ldapfilter -Properties Department, Title, SamAccountName |
Export-Csv path\to\export.csv -NoTypeInformation

The title filter is an exact match of each clause, hence the "get rid of / filter that list further of any other Titles that have - in their name" should be covered.

The generated LDAP String would look like this after formatting for readability:

(&
(|
(department=Sales & Admin - All)
(department=Field - Support)
(department=HKLM - All)
(department=SOD - 1)
(department=Home - 1080)
)
(|
(title=Client Manager)
(title=Local Sales)
(title=Outside Sales)
(title=Region Manager)
(title=Deployment Manager)
)
)

How can i get List of users from Azure AD?

using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;
using System;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("application id")
.WithTenantId("tenant id")
.WithClientSecret("application secret")
.Build();

ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);
var result = graphServiceClient.Users.Request().GetAsync();
foreach (var item in result.Result)
{
Console.WriteLine(item.DisplayName);
}

Console.ReadKey();

}
}
}

UPDATE

1.https://github.com/microsoftgraph/msgraph-training-aspnet-core

You can download this project directly

2.https://github.com/microsoftgraph/msgraph-training-aspnet-core/tree/master/demo

This is the documentation required to establish the above.

3.The screenshot below is an example of me.This asp.net core mvc project using microsoft graph is feasible.

Sample Image

This is an example of a page-only lists ad users.Just put the following access token in var accessToken="your access token******"***;and you can run it.

<html>

<style>
.userItem {
background-color:lightgrey;
list-style-type: none;
margin: 0;
padding: 0;
}

.userItem p {
display: inline-block;
width:25%
}

.LicenseItem {
background-color: lightgoldenrodyellow;
margin: 0;
padding: 0;
height: 45%;
overflow: auto;
}

.LicenseItem p {
display: inline-block;
width: 20%;

}


.loadMore {
background-color: aqua;
font-style: italic;
text-align: center;
padding: 15px;
}
</style>

<body>
<div id="message"></div>
<div id="userListView">
<div class="userItem" style="background-color: aqua;font-style: italic;">
<hr>
<p> user display name </p>
<p> user email address </p>
<p> user principal name </p>
</div>
<div id="userList"></div>
<div id="loadMore" class="loadmore"></div>
</div>
</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<script>
var accessToken="your access token*********";
var ItemView = " <div id='@id' class='userItem'><hr><p id='@id-obj' style='display:none'>@obj</p><p > @displayName </p><p > @mail </p><p > @upn </p></div>"
function initPage(){
$("#loadMore").empty();
$.ajax({
url: 'https://graph.microsoft.com/v1.0/users?$top=10',
type: 'get',
headers: {
"Content-type": 'application/json',
"Authorization":"Bearer " + accessToken
},
success: function (data) {
var userlist = data.value;
var nextpageUrl = data['@odata.nextLink'];
if(nextpageUrl){
$("#loadMore").append("<div onclick='loadMore(\""+ nextpageUrl +" \")'>load more...</div>");
}
userlist.forEach(element => {
var view = ItemView.replace(/@id/g,element.id).replace("@displayName",element.displayName).replace("@mail",element.mail).replace("@upn",element.userPrincipalName).replace("@obj",JSON.stringify(element));
console.log(JSON.stringify(element));

$("#userList").append(view);
});
},
error:function(data){
var response = JSON.parse(data.responseText)
document.getElementById("message").innerHTML="ERROR:" + response.error.message;
}
});
}

function loadMore(url){

$.ajax({
url: url,
type: 'get',
headers: {
"Content-type": 'application/json',
"Authorization":"Bearer " + accessToken
},
success: function (data) {
var userlist = data.value;
var nextpageUrl = data['@odata.nextLink'];

if(nextpageUrl){
$("#loadMore").empty();
$("#loadMore").append("<div onclick='loadMore(\""+ nextpageUrl +" \")'>load more...</div>");
}
userlist.forEach(element => {
var view = ItemView.replace(/@id/g,element.id).replace("@displayName",element.displayName).replace("@mail",element.mail).replace("@upn",element.userPrincipalName).replace("@obj",JSON.stringify(element));
$("#userList").append(view);
});
},
error:function(data){
var response = JSON.parse(data.responseText)
document.getElementById("message").innerHTML="ERROR:" + response.error.message;
}
});
}
initPage();

</script>

</html>


Related Topics



Leave a reply



Submit