Active Directory and C#
System.DirectoryServices.AccountManagement Namespace
"Provides uniform access and manipulation of user, computer, and group security principals across the multiple principal stores: Active Directory Domain Services (AD DS), Active Directory Lightweight Directory Services (AD LDS), and Machine SAM (MSAM). System.DirectoryServices.AccountManagement manages directory objects independent of the System.DirectoryServices namespace."
Accessing and managing AD in .Net is alot simplier then was previously available. Most of this code we are going to go through can easily be accomplished in Powershell. But powershell integration can be clunky and not as smooth at times for widely used apps.
For instance you might want to post a Blazor app to your local IIS that HR users can access to get a quick glimpse of some users permissions. IE the No-Internet-Access group or the Social-Media Access Group. Blazor is lighting fast when making these queries and anyone who has delt with constant HR requests can affirm anything to make them call you less is a real plus :-)
So i will try and step through and explain the creation process of this.
Create a Blazor Server Side app.
Create a Folder in solutions explorer called Models
In The newly created models folder create a class called AdUsers.
AdUser.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.DirectoryServices;using System.DirectoryServices.AccountManagement; namespace BlazorReport.Models{ public class ADModel { public String EmailAddress { get; set; } public String SurName { get; set; } public String GivenName { get; set; } public String SamAccountName { get; set; } public String UserPrincipalName { get; set; } public String password { get; set; } public String LastLogon { get; set; } }} |
@page "/Data/AdminReports"@using BlazorReport@using BlazorReport.Models@using System.DirectoryServices.AccountManagement @inject Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor@inject AuthenticationStateProvider AuthenticationStateProvider <h3>Domain Admin Reports</h3><p></p><h4>Showing Report For @tData</h4><p></p> @if (isRole == true) //Check if user is in the Report OU(no one else can see this) {<p>Select Report Type</p><select @bind="ReportType"> <option value="All">All Users</option> <option value="Internal">Users With Only Internal Email</option> <option value="External">Users With External Email</option> <option value="NOInternet">Users With NO Internet Access</option> <option value="ResInternet">Users With Restricted Access (No Social Media)</option> <option value="SocialInternet">Users With Social Media Access </option> </select> @if (@tData != "none selected") { <table class="table"> <thead> <tr> <th>First Name</th> <th>Last Name</th> <th>Logon Name</th> <th>Email</th> <th>Last Logon</th> </tr> </thead> <tbody> @foreach (var userName in myDomainUsers) { <tr> <td>@userName.GivenName</td> <td>@userName.SurName</td> <td>@userName.SamAccountName</td> <td>@userName.EmailAddress</td> <td>@userName.LastLogon</td> </tr> } </tbody></table> } } else{<p><em>You do not have the secret decoder ring you need to view this site. <br />please call IT at 867-5309 and ask for jenny</em></p>} @code { string users; private string Username; Boolean isRole; private List<ADModel> myDomainUsers; private string tData = "none selected"; // Select Value public string Report; public string ReportType { get {return Report;} set { ChangeEventArgs selectedEventArgs = new ChangeEventArgs(); selectedEventArgs.Value = value; OnChangeSelected(selectedEventArgs); } } private void OnChangeSelected(ChangeEventArgs e) { if (e.Value.ToString() != string.Empty) { switch (e.Value.ToString()) { case "All": { tData = e.Value.ToString(); myDomainUsers = ALLUsers(); break; } case "External": { tData = "Users With External Email Access"; myDomainUsers = Email("*domain.org"); break; } case "Internal": { tData = "Users With Internal Email Access"; myDomainUsers = Email("*domain.local"); break; } case "NOInternet": { tData = "Users with NO Internet Access"; myDomainUsers = Internet("Internet Users No Access"); break; } case "ResInternet": { tData = "Users with Restricted Internet Access"; myDomainUsers = Internet("Internet Users Restricted Access"); break; } case "SocialInternet": { tData = "Users with Social Media Internet Access"; myDomainUsers = Internet("Internet Users Social Networking"); break; } default: break; } } }//Search with a string filter *domain.local or *domain.net or *domain2.net public List<ADModel> Email(string Filt) { List<ADModel> useString = new List<ADModel>(); PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain.local", "dc=domain,dc=local"); UserPrincipal PrinUser = new UserPrincipal(ctx); PrinUser.EmailAddress = Filt; PrincipalSearcher srch = new PrincipalSearcher(PrinUser); srch.QueryFilter = PrinUser; foreach (UserPrincipal found in srch.FindAll()) { if (string.IsNullOrWhiteSpace(found.GivenName)) { //no users found in the query...Error or ignore } else { // do whatever here - "found" is of type "Principal" - it could be user, group, computer..... useString.Add(new ADModel() { SamAccountName = found.SamAccountName, EmailAddress = found.EmailAddress, SurName = found.Surname, GivenName = found.GivenName, UserPrincipalName = found.UserPrincipalName, LastLogon = found.LastLogon.ToString() }); } } return useString; } public List<ADModel> Internet(string name) //search AD by group { List<ADModel> useString = new List<ADModel>(); PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain.local", "OU=Global Groups,dc=domain,dc=local"); GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, name); foreach (var p in group.Members) { useString.Add(new ADModel() { SamAccountName = p.SamAccountName }); } useString = useString.OrderBy(x => x.SamAccountName).ToList(); return useString; } public List<ADModel> ALLUsers() { List<ADModel> useString = new List<ADModel>(); PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain.local", "OU=DomainUsers,dc=domain,dc=local"); UserPrincipal PrinUser = new UserPrincipal(ctx); PrincipalSearcher srch = new PrincipalSearcher(PrinUser); foreach (UserPrincipal found in srch.FindAll()) { // do whatever here - "found" is of type "Principal" - it could be user, group, computer..... useString.Add(new ADModel() { SamAccountName = found.SamAccountName, EmailAddress = found.EmailAddress, SurName = found.Surname, GivenName = found.GivenName, UserPrincipalName = found.UserPrincipalName, LastLogon = found.LastLogon.ToString() }); } useString = useString.OrderBy(x => x.GivenName).ToList(); return useString; } } |