52

I am writing the following methods to add and remove users from active directory in C#.

void AddUserToGroup(string userId, string groupName);
void RemoveUserFromGroup(string userId, string groupName);

How best to implement these methods?

Here is some code from CodeProject. I can't see where the AD server is specified in these examples though? (is it implicitly supplied by the .NET framework when using the LDAP protocol?). Are these examples worth following?

public void AddToGroup(string userDn, string groupDn)
{
    try
    {
        DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn);
        dirEntry.Properties["member"].Add(userDn);
        dirEntry.CommitChanges();
        dirEntry.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //doSomething with E.Message.ToString();

    }
}


public void RemoveUserFromGroup(string userDn, string groupDn)
{
    try
    {
        DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn);
        dirEntry.Properties["member"].Remove(userDn);
        dirEntry.CommitChanges();
        dirEntry.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //doSomething with E.Message.ToString();

    }
}
Ben Aston
  • 53,718
  • 65
  • 205
  • 331

4 Answers4

102

Ugh. LDAP. If you're using the .Net Framework 3.5 or above, I highly recommend using the System.DirectoryServices.AccountManagement namespace. That makes things so much easier.

public void AddUserToGroup(string userId, string groupName) 
{ 
    try 
    { 
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "COMPANY"))
        {
            GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
            group.Members.Add(pc, IdentityType.UserPrincipalName, userId);
            group.Save();
        }
    } 
    catch (System.DirectoryServices.DirectoryServicesCOMException E) 
    { 
        //doSomething with E.Message.ToString(); 

    } 
} 

public void RemoveUserFromGroup(string userId, string groupName)
{   
    try 
    { 
        using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "COMPANY"))
        {
            GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
            group.Members.Remove(pc, IdentityType.UserPrincipalName, userId);
            group.Save();
        }
    } 
    catch (System.DirectoryServices.DirectoryServicesCOMException E) 
    { 
        //doSomething with E.Message.ToString(); 

    }
}
BJ Myers
  • 6,617
  • 6
  • 34
  • 50
Jacob Proffitt
  • 12,664
  • 3
  • 41
  • 47
  • 2
    System.DirectorServices.AccountManagement is only available in >= 3.5, rather than 3.0 – regex Sep 25 '12 at 23:48
  • 4
    Below code worked for me ***group.Members.Remove(UserPrincipal.FindByIdentity(pc, userId));*** instead of ***"group.Members.Remove(pc, IdentityType.UserPrincipalName, userId);"*** . Note: my user id is just "USERNAME" without appending with domain name – Ram Feb 04 '13 at 06:02
  • Yeah, that overload works, too, it's just an extra call into the LDAP service to get the user identity before sending the remove call. Frankly, it's possible that they're equivalent in function as the API probably calls into the LDAP for the identity based on user name before doing the remove, too. – Jacob Proffitt Feb 04 '13 at 17:36
  • 7
    Had a similar issue to the described above. I had to change the line that removes the user from the group from IdentityType.UserPrincipalName to IdentityType.SAMAccountName – Ju66ernaut Jun 30 '15 at 18:17
  • 7
    What is **userId** ? – Kiquenet Feb 16 '16 at 08:39
  • The only issue with the *System.DirectorServices.AccountManagement* is that if the machine is not in the domain it gets me an error >. – forcewill Sep 28 '16 at 09:45
  • 6
    I know it was said in the above comments, but it wasn't clarified. If you send in `DOMAIN\someUserId` as your user, that's when you have to change it to `IdentityType.SamAccountName`, instead of `UserPrincipalName`. The latter will give you an exception: `No principal matching the specified parameters was found`. But note, too, it will also give you an exception with `SamAccountName`, if the user is already in the group. – vapcguy Oct 24 '16 at 14:53
  • 1
    On the removal from the group - if the user was never in the group to begin with, it doesn't provide an exception - it will just go through the `.Save()` operation, regardless. You have to check if the `UserPrincipal` object for the user `.IsMemberOf(group)`, to discover this. Submitted an edit. `userId` should be in `DOMAIN\username` form to send to `UserPrincipal.FindByIdentity(pc, userId);`, however, which would require `SamAccountName` in the `IdentityType`. – vapcguy Oct 24 '16 at 15:23
  • @Rama I would post that as a separate answer--that helped me a ton! – codeMonkey Apr 11 '17 at 22:25
  • What does `company` means in that both method? – Aravin Nov 05 '17 at 12:00
  • @vapcguy Getting this error- No principal matching the specified parameters was found.' – vbhosale Sep 01 '20 at 06:57
  • @vbhosale Read my comments above. I specified why that happens. Use `IdentityType.SamAccountName`, instead, but then you have to make sure they are not in the group, to begin with, too. Do you know they are in the group, but are not getting found? Then you can look for them by their SSID. – vapcguy Sep 17 '20 at 17:00
  • 1
    I kept getting `System.DirectoryServices.DirectoryServicesCOMException (0x8007052E): The user name or password is incorrect.` To fix it, I had to provide a user and password in the `new PrincipalContext(ContextType.Domain ....)` constructor. I think this is because I am running as a local account, trying to add a domain user to a local group. It works now, thanks! – aampere Feb 02 '23 at 04:11
  • This solution is not working if login user is non owner of AD group do we have any solution that non owner can add user in AD Group? – Jasmin Solanki Jul 28 '23 at 06:17
4

The server is part of the groupDn variable value. For example:

LDAP://myServer/CN=MyGroup,CN=Groups,CN=MyContainer,DN=mydomain.com

The whole thing is the LDAP path for the group. The first part (myServer) is the server name.

The part after the server name (e.g. CN=...) is the DN (distinguished name) of the group.

mhu
  • 17,720
  • 10
  • 62
  • 93
Mike Marshall
  • 7,788
  • 4
  • 39
  • 63
  • The only thing I would say is that in a good AD setup, you should not have to specify the server. The .NET AD/low level AD calls should resolve the nearest available server for you. But this is more AD/domain setup and not so much code. If your AD setup is solid, you should be able to exclude the server (e.g. LDAP://CN=MyGroup,CN=Groups,CN=MyContainer,DN=mydomain.com) – Mike Marshall Jan 26 '10 at 22:22
  • 1
    Sorry didn't really answer your questions. yes, the examples do seem clean. If you are still unsure, I highly recommend the .NET Developer's Guide to Directory Services Programming (http://www.amazon.com/gp/product/0321350170) – Mike Marshall Jan 26 '10 at 22:29
2

When deleting a member in public void RemoveUserFromGroup(string userDn, string groupDn)

dirEntry.Properties["member"].Remove(userDn) does not work for me.

dirEntry.Properties["member"].RemoveAt(dn.IndexOf(dn)) works.

shanethehat
  • 15,460
  • 11
  • 57
  • 87
Andy
  • 29
  • 1
  • What is the `dn` variable? – fripp13 Mar 28 '17 at 11:39
  • @fripp13, in the context of an Active Directory, dn almost always means DistinguishedName. – Mathias Maes Feb 26 '18 at 14:00
  • @Andy The `RemoveAt(int)` method requires an index. If you call `userDn.IndexOf(userDn)`, the method will *always* return 0 (Example: `"TEST".IndexOf("TEST")` will return 0). The 2nd proposal will always remove the *first entry* in the list. – Daniel Müller Dec 14 '22 at 09:32
1

You can put the LDAP server in the path argument to DirectoryEntry, so "LDAP://" + ldapServer + ldapQuery.

Use the DirectoryEntry(String path, String userId, String password) if you need to authenticate

Mason
  • 8,767
  • 10
  • 33
  • 34