I need to delete a user from Domain Users in AD using VB

Posted on 2005-04-25
Medium Priority
Last Modified: 2008-01-09
For security reasons, I need to delete a user from the Domain Users group in AD. I already have them in an OU with their Primary Group set to a special group.

Here's the code I am using to create them, and assign them to the new group.

    Private Sub AddUserToAD()
        'This code adds a NEW USER to a specified OU.
        'Useful in constructing LDAP "Paths":
        'cn = Computer, Contact, Container, Group, Print Queue, and User objects
        'ou = Organizational Unit
        'dc = Domain controller name (example dc=mydomain,dc=com for mydomain.com)
        'o = Domain name

        'First, connecto to the Active Directory.
        Dim sDomain As String = "gafcon"
        Dim sUsername As String = "admin"
        Dim sPassword As String = "password"

        Dim dom As DirectoryEntry = New DirectoryEntry("LDAP://" & sDomain, sUsername, sPassword)

        'Connect to the specific OU
        Dim ouToAddTo As DirectoryEntry = dom.Children.Find("OU=SHAREPOINT")

        'Use the Add method to add a user in an organizationalUnit.
        Dim newUser As DirectoryEntry = ouToAddTo.Children.Add("CN=Cliff", "User")

        'Set the core AD properties
        ' Pre-Windows 2000 Name
        newUser.Properties("sAMAccountName").Value = "Cliff"
        ' User Logon Name
        ' Email
        newUser.Properties("mail").Value = "Cliff@gafcon.local"
        ' First Name
        ' Initials
        ' Last Name
        ' Display Name
        newUser.Properties("displayName").Add("Cliff West")
        ' Description
        newUser.Properties("description").Add("Added by " & _

        'Create the account

        'set the password for the new user
        newUser.Invoke("setPassword", "password")

        Dim userACFlags As Object = newUser.Properties("userAccountControl").Value
        newUser.Properties("userAccountControl").Value = userACFlags Or &H200 Or &H10000 Xor &H2

        'Commit the changes

        Dim grp As DirectoryEntry = New DirectoryEntry("LDAP://" & sDomain, sUsername, sPassword)
        Dim addMe As DirectoryEntry = grp.Children.Find("CN=Extranet", "group")


        'Dim addMe As DirectoryEntry = grp.Children.Find("CN=Extranet", "group")
        If Not addMe.Properties.Contains("PrimaryGroupToken") Then
            addMe.RefreshCache(New String() {"PrimaryGroupToken"})
        End If

        newUser.Properties("PrimaryGroupID").Value = addMe.Properties("PrimaryGroupToken").Value

    End Sub
Question by:jawhitmoyer
  • 3
  • 3

Author Comment

ID: 13861851
I tried adding this, but I get an error saying unknown object:

Dim grp2 As DirectoryEntry = New DirectoryEntry("LDAP://" & sDomain, sUsername, sPassword)
addMe = grp2.Children.Find("CN=Domain Users", "group")
LVL 20

Expert Comment

ID: 13865911
Jim, you are almost there. But first you need to understand a couple of things. Let say you want SecurityGroupA to replace "Domain Users" primary group for UserA. You're allowed to do that if SecurityGroupA already has UserA as its member. Other than that you'll get exception. So essentially your code needs to check whether the above condition is satisfied or manually set UserA to be SecurityGroupA's member. Then the next step is to bind to "Domain Users" group and manually remove UserA from its "member" attribute.

Pseudo code:

securityGroupA = getGroupFromAD()
If userA is not a member of securityGroupA Then
   add UserA as member of securityGroupA
End If
Replace "Domain User" with securityGroupA
domainUsers = getGroupFromAD()
Remove UserA from domainUsers membership.

Please try that and let me know if you have any problem.

Author Comment

ID: 13869343
By default all users are created in the Domain Users group. That's why I tought I would just need the code above to remove them. I get an error because I am not finding the group in AD. Is Domain Users group in the root of AD, or do I need to look for it something like "CN=USERS"?

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

LVL 20

Expert Comment

ID: 13875621
Ok, you have the code to replace primary group with another group and you know how to remove a user's membership from a group. Now I'll tell you how to get a user's primary group. "Domain Users" group as the default primary group for all user objects exists in every domain in the forest. That means, if you have multiple domains in your AD forest you can't exactly tell what "Domain Users" group map to what user without knowing where the user comes from. The "shortest way" (and the best performance can be achieved) to determine the primary group of a user is to build an sid bind string for the group object from the domain rid component of the user's sid and the PrimaryGroupID attribute on the user object. Then use the sid string to bind to the group using DirectoryEntry class.

I guess, there's no easy way to explain this without actually posting some code. I have the code handy and it does exactly what you need, but...the code is written in c# and it's involving some pointer operations. For that I'm not sure how you're going to convert it to VB.NET. But I have something for your consideration:

1. Compile the c# code into a separate assembly and call it from your VB.NET project.
2. If point 1. is not an option, throw another question to VB.NET TA about the code conversion problem, someone might be able to show you a workaround
3. Forget about the code, open adsiedit via mmc snap-ins and copy the "distinguishedName" attribute of the default "Domain Users" group and hard code it in your code (this won't be so simple if you have multiple domains).
4. Comeback here, there's another workaround. No pointer operation, whatsoever but I don't have it handy and I can't promise you'll see any  good performance from it. Another bad news is you have to write and test more lines of code if you want to go with this.

Please reply and let me know which one are you able to go with.
LVL 20

Accepted Solution

ihenry earned 2000 total points
ID: 13875643
And of course the code:

      using PSID = System.IntPtr;
      using BOOL = System.Int32;

      [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
      static extern BOOL ConvertSidToStringSid(
            PSID pSID,
            out IntPtr pStringSid

      private static String hostName = "";
      private static String provider = "LDAP";
      private static String ldapPath = String.Format( "{0}://{1}", provider, hostName );

      public DirectoryEntry GetPrimaryGroup( DirectoryEntry user )
            if ( user == null )
                  throw new ArgumentNullException("user");
            if ( !user.Properties.Contains("objectSid") || !user.Properties.Contains("primaryGroupID") )
                  user.RefreshCache( new String[] {"primaryGroupID", "objectSid"} );
            int primaryGroupId = (int) user.Properties["PrimaryGroupID"].Value;
            byte[] userSid = user.Properties["objectSid"].Value as byte[];
            byte[] primaryGroupSid = BuildPrimaryGroupSID( userSid, primaryGroupId );
            String adsPath = String.Format( "{0}/<SID={1}>", ldapPath, UnsafeConvertSidToStringSid(primaryGroupSid) );

            DirectoryEntry pg = new DirectoryEntry();
            pg.Path = adsPath;
            pg.Username = user.Username;
            pg.Password = user.Password;
            pg.AuthenticationType = user.AuthenticationType;
            String dnpg;
                  dnpg = (String) pg.Properties["distinguishedName"].Value;
                  pg.Close(); pg.Dispose();
            return new DirectoryEntry(String.Format("{0}/{1}", ldapPath, dnpg), user.Username, user.Password, user.AuthenticationType );

      private byte[] BuildPrimaryGroupSID( byte[] userSid, int primaryGroupId )
            byte[] rid = BitConverter.GetBytes( primaryGroupId );
            for ( int i=0; i < rid.Length; i++ )
                  userSid.SetValue(rid[i], new long[] {userSid.Length - (rid.Length - i)} );
            return userSid;

      private unsafe String UnsafeConvertSidToStringSid( object sid )
            String sidString = null;
            IntPtr strPtr;
            fixed ( byte *psid = (byte[]) sid )
                  IntPtr psidPtr = (IntPtr)psid;
                  BOOL rc = ConvertSidToStringSid( psidPtr, out strPtr );
                        sidString = Marshal.PtrToStringAuto( strPtr );      
            return sidString;

Author Comment

ID: 13875684
I can definitely work with the C#. You have been a big help.


Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

If you're writing a .NET application to connect to an Access .mdb database and use pre-existing queries that require parameters, you've come to the right place! Let's say the pre-existing query(qryCust) in Access takes a Date as a parameter and l…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question