Question :
How do I delete a group from active directory?
What I have tried:
1. PrinipalContext
I am trying to delete an active directory group. I have this right now:
using (var ctx = new PrincipalContext(ContextType.Domain, myDomain, ldapUser, ldapPassword))
{
var group1 = new GroupPrincipal(ctx, groupName);
group1.Delete();
}
But I get an error: "Unpersisted Principal objects can not be deleted."
That lead me here, but I don't know what the invoke magic is all about and it scares me a little bit.
2. DirectoryEntry
http://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#33
But I just kept getting "The server is not operational" errors.
I just need to delete the AD group, is it even possible?
Turns out that the DirectoryEntry works fine, but my ldap urls were wrong. Here is the code that I ended up using:
using (var ou = new DirectoryEntry(ouPath, ldapUser, ldapPassword))
{
using (var group = new DirectoryEntry(groupPath, ldapUser, ldapPassword))
{
ou.Children.Remove(group);
group.CommitChanges();
}
}
WRONG OLD VALUES
ouPath LDAP://myDomain.local/OU=myTier1,DC=myDomain,DC=local
groupPath LDAP://groupname/OU=myTier3,OU=myTier2,OU=myTier1,DC=myDomain,DC=local
CORRECT NEW VALUES
ouPath LDAP://myDomain.local/OU=myTier2,OU=myTier1,DC=myDomain,DC=local
groupPath LDAP://myDomain.local/CN=groupName,OU=myTier3,OU=myTier2,OU=myTier1,DC=myDomain,DC=local
Related
I have this powershell function and i want to make it as a C# function.
How can i put it into C#?
Get-ADComputer -filter {Name -Like 'myComp'} -property * | select DistinguishedName
You should be able to do this quite easily. Add a reference to System.DirectoryServices.AccountManagement and then use this code:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, 'YourDomain'))
{
ComputerPrincipal computer = ComputerPrincipal.FindByIdentity (ctx, "name");
if (computer != null)
{
// do whatever you need to do with your computer principal
string distinguishedName = computer.DistinguishedName;
}
}
Update: if you don't know your domain ........ - you can also use:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
in which case the principal context is created for the current domain you're located in.
You can use C# in the following manner
Connect to the Domain controller and get the DomainContext
Use that to look up the computer objects based on a name.
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Environment.UserDomainName)
{
using (PrincipalSearcher srch = new PrincipalSearcher(new ComputerPrincipal(ctx) { Name = "ServerName"}))
{
return srch.FindAll().Cast<ComputerPrincipal>().ToList().Select(x => x.DistinguishedName);
}
}
Above returns a list of DistinguishedNames that matches the Server Name.
All the other answers suggest using the System.DirectoryServices.AccountManagement namespace. While that will work, it is really just a wrapper around the System.DirectoryServices namespace to make things a bit easier to use. It does make things easier (sometimes) but it does so at the cost of performance.
For example, in all the examples you've been given, your code will retrieve every attribute with a value from the computer object in AD, even though you only want one attribute.
If you use DirectorySearcher, you can make the search and retrieve only that one attribute that you want:
public string GetComputerDn(string computerName) {
var searcher = new DirectorySearcher {
Filter = $"(&(objectClass=computer)(sAMAccountName={computerName}$))",
PropertiesToLoad = { "distinguishedName" } //return only the distinguishedName attribute
};
var result = searcher.FindOne();
if (result == null) return null;
return (string) result.Properties["distinguishedName"][0];
}
Note that in AD, the sAMAccountName of computer objects is what you would normally refer to as the "computer name", followed by $, which is why the filter is what it is.
Please try this:
Add reference to Active Directory Services (%programfiles%\Reference Assemblies\Microsoft\Framework.NETFramework\\System.DirectoryServices.AccountManagement.dll)
public string GetComputerName(string computerName)
{
using (var context = new PrincipalContext(ContextType.Domain, "your domain name goes here"))
{
using (var group = GroupPrincipal.FindByIdentity(context, "Active Directory Group Name goes here"))
{
var computers = #group.GetMembers(true);
return computers.FirstOrDefault(c => c.Name == computerName).DistinguishedName;
}
}
return null; // or return "Not Found"
}
I am getting error while retrieving users under the group from active directory. Error description is
{"Information about the domain could not be retrieved (1355)"}. Tried with .Net 4.0 and .Net 4.5.
The line for which I am getting the error is commented with the error message.
public List<DirectoryUser> GetUsersUnderGroup(string groupName)
{
var directoryUserList = new List<DirectoryUser>();
string directoryServerIp="192.168.1.xxx";
string ouName="xxxOuName";
string domainComponents="DC=xxxComopnent1,DC=xxxComponent2";
string directoryAdminUserId="directoryAdminuser";
string directoryAdminPassword="directoryAdminPassword";
using (var principalContext = principalContext = new PrincipalContext(ContextType.Domain, directoryServerIp, string.Format("OU={0},{1}", ouName, domainComponents), directoryAdminUserId, directoryAdminPassword);)
{
using (var group = GroupPrincipal.FindByIdentity(principalContext, groupName))
{
if (group != null)
{
var users = group.GetMembers(true);
//Works fine till the above line. variable users is having not null value but
//exception while iterating through the loop.Following is the exception
//{"Information about the domain could not be retrieved (1355)."}
foreach(var user in users)
{
Console.Write(user.DistinguishedName);
}
}
}
}
return directoryUserList;
}
Code for creating PrincipalContext is working fine in other scenarios (like fetching list of groups and OU's etc )
This article describes reasons for your issue and possible solutions: link. Basically, you have to use DirectoryEntry class if you run your code not from machine, where domain controller lies.
This article should help you to understand that class: link
I'm not a programmer by nature so I apologize in advance. I've searched far and wide and have found bits and pieces of 10 different ways to do one thing. What I'm trying to do seems very simple but I'm missing it...I need to search Active Directory using a first name and last name and display all users who match in a listbox. Can someone point me in the right direction, or if someone has already asked the same question link me to it? Thanks in advance!
Try something like this:-
DirectorySearcher d = new DirectorySearcher(somevalue);
d.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(givenname={0})(sn={1}))", firstname, lastname);
Also from How to search for users in Active Directory with C#
//Create a shortcut to the appropriate Windows domain
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain,
"myDomain");
//Create a "user object" in the context
using(UserPrincipal user = new UserPrincipal(domainContext))
{
//Specify the search parameters
user.Name = "he*";
//Create the searcher
//pass (our) user object
using(PrincipalSearcher pS = new PrincipalSearcher())
{
pS.QueryFilter = user;
//Perform the search
using(PrincipalSearchResult<Principal> results = pS.FindAll())
{
//If necessary, request more details
Principal pc = results.ToList()[0];
DirectoryEntry de = (DirectoryEntry)pc.GetUnderlyingObject();
}
}
}
//Output first result of the test
MessageBox.Show(de.Properties["mail"].Value.ToString());
Of course shortly after posting I found my answer :)
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "servername","username","password");
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.GivenName = "fname";
qbeUser.Surname = "lname";
// qbeUser.DisplayName= "fname lname";
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach (var found in srch.FindAll())
{
lstUser.Items.Add(found.ToString());
}
here is the link: Search Users in Active Directory based on First Name, Last Name and Display Name
When I try to update the Name field (corresponds to the CN) on UserPrincipal (Principal, really), I get an error "The server is unwilling to process the request" on the call to UserPrincipal.Save().
I've checked to make sure there isn't another object in the same OU with the same Name (CN).
The PrincipalContext I'm operating at is the domain root (not exactly at the OU level where the user account exists).
What reason might there be for this error? Is it something that might be security policy related (even though I'm able to update all the other fields)?
using (var context = new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["domain"], ConfigurationManager.AppSettings["rootDN"], ContextOptions.Negotiate, ConfigurationManager.AppSettings["username"], ConfigurationManager.AppSettings["password"])) {
var user = UserPrincipal.FindByIdentity(context, IdentityType.Sid, "..."); // SID abbreviated
user.Name = "Name, Test";
user.Save();
}
The user I am using to create the PrincipalContext has the security rights to modify AD objects. If I update any other of the other fields (e.g. Surname, GivenName), everything works fine.
EDIT:
I've been able to accomplish what I need to do (using ADSI), but I have to run the following code under impersonation. The impersonation code is ugly, and the code below breaks away from the other way I'm updating AD data (using DirectoryServices.AccountManagement), so I'd like to get a better solution.
using (var companyOU = new DirectoryEntry("LDAP://" + company.UserAccountOU)) {
companyOU.Invoke("MoveHere", "LDAP://" + user.DistinguishedName, "cn=Name\, Test");
}
This is a cleaner way
using (var context = new PrincipalContext(ContextType.Domain))
{
var group = GroupPrincipal.FindByIdentity(context, groupName);
group.SamAccountName = newGroupName;
group.DisplayName = newGroupName;
group.Save();
var dirEntry = (DirectoryEntry)group.GetUnderlyingObject();
dirEntry.Rename("CN=" + newGroupName);
dirEntry.CommitChanges();
}
The only way I've found to do this is in the EDIT section in my question. Basically, you cannot use the UserPrincipal class. There is something special about the CN attribute, and you need to drop down a level and use DirectoryEntry, an LDAP string, and invoke the "MoveHere" ADSI command to rename the user account.
I use this code to get the groups of the current user. But I want to manually give the user and then get his groups. How can I do this?
using System.Security.Principal;
public ArrayList Groups()
{
ArrayList groups = new ArrayList();
foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
return groups;
}
If you're on .NET 3.5 or up, you can use the new System.DirectoryServices.AccountManagement (S.DS.AM) namespace which makes this a lot easier than it used to be.
Read all about it here: Managing Directory Security Principals in the .NET Framework 3.5
Update: older MSDN magazine articles aren't online anymore, unfortunately - you'll need to download the CHM for the January 2008 MSDN magazine from Microsoft and read the article in there.
Basically, you need to have a "principal context" (typically your domain), a user principal, and then you get its groups very easily:
public List<GroupPrincipal> GetGroups(string userName)
{
List<GroupPrincipal> result = new List<GroupPrincipal>();
// establish domain context
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
// find your user
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);
// if found - grab its groups
if(user != null)
{
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
// iterate over all groups
foreach(Principal p in groups)
{
// make sure to add only group principals
if(p is GroupPrincipal)
{
result.Add((GroupPrincipal)p);
}
}
}
return result;
}
and that's all there is! You now have a result (a list) of authorization groups that user belongs to - iterate over them, print out their names or whatever you need to do.
Update: In order to access certain properties, which are not surfaced on the UserPrincipal object, you need to dig into the underlying DirectoryEntry:
public string GetDepartment(Principal principal)
{
string result = string.Empty;
DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);
if (de != null)
{
if (de.Properties.Contains("department"))
{
result = de.Properties["department"][0].ToString();
}
}
return result;
}
Update #2: seems shouldn't be too hard to put these two snippets of code together.... but ok - here it goes:
public string GetDepartment(string username)
{
string result = string.Empty;
// if you do repeated domain access, you might want to do this *once* outside this method,
// and pass it in as a second parameter!
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
// find the user
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);
// if user is found
if(user != null)
{
// get DirectoryEntry underlying it
DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);
if (de != null)
{
if (de.Properties.Contains("department"))
{
result = de.Properties["department"][0].ToString();
}
}
}
return result;
}
GetAuthorizationGroups() does not find nested groups. To really get all groups a given user is a member of (including nested groups), try this:
using System.Security.Principal
private List<string> GetGroups(string userName)
{
List<string> result = new List<string>();
WindowsIdentity wi = new WindowsIdentity(userName);
foreach (IdentityReference group in wi.Groups)
{
try
{
result.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (Exception ex) { }
}
result.Sort();
return result;
}
I use try/catch because I had some exceptions with 2 out of 200 groups in a very large AD because some SIDs were no longer available. (The Translate() call does a SID -> Name conversion.)
First of all, GetAuthorizationGroups() is a great function but unfortunately has 2 disadvantages:
Performance is poor, especially in big company's with many users and groups. It fetches a lot more data then you actually need and does a server call for each loop iteration in the result
It contains bugs which can cause your application to stop working 'some day' when groups and users are evolving. Microsoft recognized the issue and is related with some SID's. The error you'll get is "An error occurred while enumerating the groups"
Therefore, I've wrote a small function to replace GetAuthorizationGroups() with better performance and error-safe. It does only 1 LDAP call with a query using indexed fields. It can be easily extended if you need more properties than only the group names ("cn" property).
// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain")
public static List<string> GetAdGroupsForUser2(string userName, string domainName = null)
{
var result = new List<string>();
if (userName.Contains('\\') || userName.Contains('/'))
{
domainName = userName.Split(new char[] { '\\', '/' })[0];
userName = userName.Split(new char[] { '\\', '/' })[1];
}
using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName))
using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName))
using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name)))
{
searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName);
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("cn");
foreach (SearchResult entry in searcher.FindAll())
if (entry.Properties.Contains("cn"))
result.Add(entry.Properties["cn"][0].ToString());
}
return result;
}
Within the AD every user has a property memberOf. This contains a list of all groups he belongs to.
Here is a little code example:
// (replace "part_of_user_name" with some partial user name existing in your AD)
var userNameContains = "part_of_user_name";
var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();
var allSearcher = allDomains.Select(domain =>
{
var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));
// Apply some filter to focus on only some specfic objects
searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains);
return searcher;
});
var directoryEntriesFound = allSearcher
.SelectMany(searcher => searcher.FindAll()
.Cast<SearchResult>()
.Select(result => result.GetDirectoryEntry()));
var memberOf = directoryEntriesFound.Select(entry =>
{
using (entry)
{
return new
{
Name = entry.Name,
GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString())
};
}
});
foreach (var item in memberOf)
{
Debug.Print("Name = " + item.Name);
Debug.Print("Member of:");
foreach (var groupName in item.GroupName)
{
Debug.Print(" " + groupName);
}
Debug.Print(String.Empty);
}
}
My solution:
UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, myDomain), IdentityType.SamAccountName, myUser);
List<string> UserADGroups = new List<string>();
foreach (GroupPrincipal group in user.GetGroups())
{
UserADGroups.Add(group.ToString());
}
In my case the only way I could keep using GetGroups() without any expcetion was adding the user (USER_WITH_PERMISSION) to the group which has permission to read the AD (Active Directory). It's extremely essential to construct the PrincipalContext passing this user and password.
var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS");
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
var groups = user.GetGroups();
Steps you may follow inside Active Directory to get it working:
Into Active Directory create a group (or take one) and under secutiry tab add "Windows Authorization Access Group"
Click on "Advanced" button
Select "Windows Authorization Access Group" and click on "View"
Check "Read tokenGroupsGlobalAndUniversal"
Locate the desired user and add to the group you created (taken) from the first step
The answer depends on what kind of groups you want to retrieve. The System.DirectoryServices.AccountManagement namespace provides two group retrieval methods:
GetGroups - Returns a collection of group objects that specify the groups of which the current principal is a member.
This overloaded method only returns the groups of which the principal is directly a member; no recursive searches are performed.
GetAuthorizationGroups - Returns a collection of principal objects that contains all the authorization groups of which this user is a member. This function only returns groups that are security groups; distribution groups are not returned.
This method searches all groups recursively and returns the groups in which the user is a member. The returned set may also include additional groups that system would consider the user a member of for authorization purposes.
So GetGroups gets all groups of which the user is a direct member, and GetAuthorizationGroups gets all authorization groups of which the user is a direct or indirect member.
Despite the way they are named, one is not a subset of the other. There may be groups returned by GetGroups not returned by GetAuthorizationGroups, and vice versa.
Here's a usage example:
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "MyDomain", "OU=AllUsers,DC=MyDomain,DC=Local");
UserPrincipal inputUser = new UserPrincipal(domainContext);
inputUser.SamAccountName = "bsmith";
PrincipalSearcher adSearcher = new PrincipalSearcher(inputUser);
inputUser = (UserPrincipal)adSearcher.FindAll().ElementAt(0);
var userGroups = inputUser.GetGroups();
This works for me
public string[] GetGroupNames(string domainName, string userName)
{
List<string> result = new List<string>();
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName))
{
using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(principalContext, userName).GetGroups())
{
src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
}
}
return result.ToArray();
}
In case Translate works locally but not remotly e.i group.Translate(typeof(NTAccount)
If you want to have the application code executes using the LOGGED IN USER identity, then enable impersonation. Impersonation can be enabled thru IIS or by adding the following element in the web.config.
<system.web>
<identity impersonate="true"/>
If impersonation is enabled, the application executes using the permissions found in your user account. So if the logged in user has access, to a specific network resource, only then will he be able to access that resource thru the application.
Thank PRAGIM tech for this information from his diligent video
Windows authentication in asp.net Part 87:
https://www.youtube.com/watch?v=zftmaZ3ySMc
But impersonation creates a lot of overhead on the server
The best solution to allow users of certain network groups is to deny anonymous in the web config
<authorization><deny users="?"/><authentication mode="Windows"/>
and in your code behind, preferably in the global.asax, use the HttpContext.Current.User.IsInRole :
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
If HttpContext.Current.User.IsInRole("TheDomain\TheGroup") Then
//code to do when user is in group
End If
NOTE: The Group must be written with a backslash \ i.e. "TheDomain\TheGroup"
This is quick and dirty but someone may find it helpful. You will need to add the reference to System.DirectoryServices.AccountManagement for this to work. This is just for getting user roles but can be expanded to include other things if needed.
using System.DirectoryServices.AccountManagement;
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "DaomainName");
UserPrincipal u = UserPrincipal.FindByIdentity(ctx, "Username");
List<UserRole> UserRoles = u.GetGroups().Select(x => new UserRole { Role = x.Name }).ToList();
public partial class UserRole
{
public string Role { get; set; }
}