locked
Copying Group Membership from one user to another RRS feed

  • Question

  • I work for a large organization, with many thousands of users, and many
    hundreds of ActiveDirectory security groups.  Quite often, when a user
    is reassigned from one enterprise unit to another, the personnel
    department only adds them to a unit-basic AD SG. Later, the user finds
    out that he doesn't have the rights to certain facilities that he
    should, and puts in a ticket for correction with the IT support desk
    (which is what they should do).

    Because of the number of security groups that are often involved in
    straightening out a user's rights, our IT support desk (where I work)
    often asks for the name of a user that already has the same rights as
    the "problem" user needs, so that they have a complete and correct list
    of the relevant security groups, rather than having to dig through some
    very thick bound listings. Even so, making the correction through the
    GUI takes a minute or two each, and that adds up to a significant chunk
    of a person's time.  If I could cut that down to less than a minute per
    - and make it batch-able to boot - it'd help us keep our workload
    manageable.  So, I'm looking to write a script/cmdlet that would allow
    me to issue the a command like "Set-ADEnterpriseGroupMembership
    Jones-Paula -Like Smith-Jason", or maybe "Copy-ADGroups -From
    Smith-Jason -To Jones-Paula". Or something like that.

    When I run PowerShell (2.0) with the AD modules imported, I can see what
    groups a user (e.g., login name Smith-Jason) is a member of by issuing

    Get-ADUser Smith-Jason -Properties MemberOf

    and I can assign it to a variable by issuing

    $foo=Get-ADUser Smith-Jason -Properties MemberOf

    Once I've done the latter, $foo.MemberOf has the list of groups that
    Smith-Jason is a member of.

    Now, Jones-Paula needs to be in exactly the same security groups as
    Smith-Jason.  It is not clear to me how to achieve this; can someone
    point me at appropriate examples/references, or otherwise provide an
    explanation?


    -- Jeff Zeitlin
    Thursday, October 20, 2011 9:21 PM

Answers

  • First, I found a mistake in the second script I posted (using Get-ADUser and PowerShell V2). I used $TargetUser.ADsPath, but $TargetUser is not defined and has no ADsPath property. It should be: "LDAP://" + $TargetGroup.distinguishedName. But I see the terminology I've used is confusing. $SourceGroup and $TargetGroup are not just collections of group DN's. They are objects representing users, one property of which is memberOf, the collection of group DN's. When I use Get-ADUser, I need to include -Properties because memberOf is not in the default collection of attribute values retrieved for the user. I'm thinking it would be more clear if I used variable names $SourceUser and $TargetUser, to  indicate they are collections of properties of the users. Even here, these objects are not the same as the user objects $SourceUser and $TargetUser I used in the first script (PowerShell V1), which are ADSI objects and expose methods and many more attributes of the user objects in AD. In any case, my corrected PowerShell V2 script follows:

     

    # Script to copy group memberships from a source user to a target user.

    Param ($Source, $Target)
    If ($Source -ne $Null -and $Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # Retrieve user information, including group memberships.
    $SourceUser = Get-ADUser $Source -Properties memberOf
    $TargetUser = Get-ADUser $Target -Properties memberOf

    # Hash table of source user groups.
    $List = @{}

    #Enumerate direct group memberships of source user.
    ForEach ($SourceDN In $SourceUser.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN, $True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember("LDAP://" + $TargetUser.distinguishedName) -eq $False)
        {
            # Add the target user to this group.
            Add-ADGroupMember -Identity $SourceDN -Members $Target
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDN In $TargetUser.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN) -eq $False)
        {
            # Source user not a member of this group.
            # Remove target user from this group.
            Remove-ADGroupMember $TargetDN $Target
        }
    }

    -----

     

    I looked into doing this in a pipeline, but if it is possible I don't know how. Note that in the first ForEach that enumerates source user groups, I require a property of the target user. If it possible, I don't see the advantage.

     


    Richard Mueller - MVP Directory Services
    Sunday, October 23, 2011 10:32 PM

All replies

  • Something like this?

     

     

    $OriginalUser = Get-QADUser UserName |select memberof  
    foreach($user in $OriginalUser.memberof)  
    {  
    Add-QADGroupMember -Identity $user -Member  <New User>
    } 
    

     

     

    This wont get nest groups however, you will need to use get/set-qadmemberof as below

     

    $OriginalUser = Get-QADUser <User Name>
    
    foreach($User in $OriginalUser)
    {
       Get-QADUser -identity $User | Get-QADMemberOf -indirect | Set-QADMemberOf -member <New Name>
    }
    


     

     

     


    • Edited by MrWasabi1 Thursday, October 20, 2011 10:48 PM syntax!!!
    Thursday, October 20, 2011 10:44 PM
  • On Thu, 20 Oct 2011 22:44:57 +0000, MrWasabi1 wrote: >Something like this? >[code] >$OriginalUser = Get-QADUser UserName |select memberof >foreach($user in $OriginalUser.memberof) >{ >Add-QADGroupMember -Identity $user -Member <New User> >} > >[/code] No go on the Quest cmdlets; I'm not allowed to install them on the system (though at first glance, it looks like I can just drop the Q and I'll have what I need). But to analyze: 0. From previous reading in this group, I know that the Quest AD cmdlets often mirror Microsoft cmdlets with the same name minus the 'Q'. On that assumption, a little work with Get-Help found the Add-ADGroupMember and the related Add-ADPrincipalGroupMembership cmdlets, which look like what I want. Subsequent questions will address issues relevant to those cmdlets. 1. In the code block above, is $user in the foreach actually the GROUP that I'm going to be adding the new user to? Or am I completely misunderstanding something here? 2. In my original posting, I was using the Logon Name for the user, as this is the information that's easiest to obtain FROM the user (or the user's supervisor). Is this the same as the SAMAccountName, as it appears to be? 3. I also need to remove the new user from any old (pre-transfer) groups. Would I duplicate the above code block, but use the "new user", and with a Remove-ADGroupMember instead of an Add-ADGroupMember? (If this isn't clear, consider the code block below: [code] $NewUser=Get-ADUser Smith-Jason -Properties MemberOf $OldUser=Get-ADUser Jones-Paula -Properties MemberOf foreach($group in $NewUser.MemberOf) { Remove-ADGroupMember $group -Members Smith-Jason } foreach($group in $OldUser.MemberOf) { Add-ADGroupMember $group -Members Smith-Jason } [/code] If I'm understanding what you've written, then this appears to be the core of what I want to do. The question focuses on whether the first foreach block is actually the correct thing to do to remove Smith-Jason from all of his old groups. Specifically, should I be excluding 'Domain Users' from this processing? Also, will any errors be thrown if I attempt to remove a user from a group that he is not a member of?) 4. Going back to my original post, I noted that I could get the original user's group membership into a variable by issuing the command $olduser=Get-ADUser Smith-Jason -Properties MemberOf After having done so, is $olduser.MemberOf in a suitable format for passing to the -MemberOf parameter of Add-ADPrincipalGroupMembership? e.g., would the following code be legitimate, though incomplete for my needs? [code] $OldUser=Get-ADUser Jones-Paula -Properties MemberOf Add-ADPrincipalGroupMembership Smith-Jason -MemberOf $OldUser.MemberOf [/code] A similar question applies to the cmdlet Remove-ADPrincipalGroupMembership >This wont get nest groups however, you will need to use get/set-qadmemberof as below Could you clarify what you mean by "nested groups", with an example of a situation where it would be relevant? >[code] >$OriginalUser = Get-QADUser <User Name> > >foreach($User in $OriginalUser) >{ > Get-QADUser -identity $User | Get-QADMemberOf -indirect | Set-QADMemberOf -member <New Name> >} > >[/code]
    -- Jeff Zeitlin
    Friday, October 21, 2011 9:25 PM
  • I've used code similar to below to copy group memberships from one user to another, which works in PowerShell V1.0 with no addins:

     

    # Bind to users.
    $Template = [ADSI]"LDAP://cn=Template user,ou=West,dc=MyDomain,dc=com"
    $User = [ADSI]"LDAP://cn=Jim Smith,ou=Sales,ou=West,dc=MyDomain,dc=com"

    # Enumerate direct group memberships if template user.
    ForEach ($GroupDN In $Template.memberOf)
    {
        # Bind to each group object.
        $Group = [ADSI]"LDAP://$GroupDN"
        # Check if user is already a member of this group.
        If ($Group.IsMember($User.ADsPath) -eq $False)
        {
            # Add the user to this group.
            $Group.Add($User.ADsPath)
            "User added to group $GroupDN"
        }
        Else
        {
            "User already member of group $GroupDN"
        }
    }

    -----

     


    Richard Mueller - MVP Directory Services
    Friday, October 21, 2011 10:44 PM
  • I apologise for the mess that my previous posting was; hopefully this
    will be better...

    On Thu, 20 Oct 2011 22:44:57 +0000, MrWasabi1 wrote:

    Something like this?

    $OriginalUser = Get-QADUser UserName |select memberof  foreach($user in $OriginalUser.memberof)  {  Add-QADGroupMember -Identity $user -Member  <New User>
    }

    No go on the Quest cmdlets; I'm not allowed to install them on the
    system (though at first glance, it looks like I can just drop the Q and
    I'll have what I need).  But to analyze:

    0. From previous reading in this group, I know that the Quest AD cmdlets
       often mirror Microsoft cmdlets with the same name minus the 'Q'.  On
       that assumption, a little work with Get-Help found the
       Add-ADGroupMember and the related Add-ADPrincipalGroupMembership
       cmdlets, which look like what I want. Subsequent questions will
       address issues relevant to those cmdlets.

    1. In the code block above, is $user in the foreach actually the GROUP
       that I'm going to be adding the new user to?  Or am I completely
       misunderstanding something here?

    2. In my original posting, I was using the Logon Name for the user, as
       this is the information that's easiest to obtain FROM the user (or
       the user's supervisor). Is this the same as the SAMAccountName, as it
       appears to be?

    3. I also need to remove the new user from any old (pre-transfer)
       groups. Would I duplicate the above code block, but use the "new
       user", and with a Remove-ADGroupMember instead of an
       Add-ADGroupMember?  (If this isn't clear, consider the code block
       below:

         $NewUser=Get-ADUser Smith-Jason -Properties MemberOf
         $OldUser=Get-ADUser Jones-Paula -Properties MemberOf
         foreach($group in $NewUser.MemberOf)
         {
         Remove-ADGroupMember $group -Members Smith-Jason
         }
         foreach($group in $OldUser.MemberOf)
         {
         Add-ADGroupMember $group -Members Smith-Jason
         }

       If I'm understanding what you've written, then this appears to be the
       core of what I want to do.  The question focuses on whether the first
       foreach block is actually the correct thing to do to remove
       Smith-Jason from all of his old groups.  Specifically, should I be
       excluding 'Domain Users' from this processing? Also, will any errors
       be thrown if I attempt to remove a user from a group that he is not a
       member of?)

    4. Going back to my original post, I noted that I could get the original
       user's group membership into a variable by issuing the command
         $olduser=Get-ADUser Smith-Jason -Properties MemberOf

       After having done so, is $olduser.MemberOf in a suitable format for
       passing to the -MemberOf parameter of Add-ADPrincipalGroupMembership?
       e.g., would the following code be legitimate, though incomplete for
       my needs?

      $OldUser=Get-ADUser Jones-Paula -Properties MemberOf
      Add-ADPrincipalGroupMembership Smith-Jason -MemberOf $OldUser.MemberOf

       A similar question applies to the cmdlet
       Remove-ADPrincipalGroupMembership

    This wont get nest groups however, you will need to use get/set-qadmemberof as below

    Could you clarify what you mean by "nested groups", with an example of a
    situation where it would be relevant?


    -- Jeff Zeitlin
    Saturday, October 22, 2011 12:58 PM
  • First, what you call logon name is probably the sAMAccountName. This is the "pre-Windows 2000 logon" name of the user and uniquely identifies the user. If, however, you meant the "Name" in ADUC, the is the Common Name of the user (the value of the cn attribute), which does not uniquely identify the user.

    Second, groups can be nested. If user "Jim" is a member of group "West, and group "West" is itself a member of "East", then "Jim" gets all permissions granted to both groups. "Jim" is effectly a member of group "East" due to group nesting. However, there is no need to account for this here. We can ignore it. If we make "Jim" a direct member of "West", then he will automatically get the required permissions. No need to also make "Jim" a direct member of "East", which would only be confusing.

    Third, the "primary" group is never included in most methods that reveal group membership. It is best to assume that everyone has the group "Domain Users" designated as their primary (which is the default). There is almost never a reason to change the primary group membership. And, it would require more code to deal with primary group memberships.

    I have modified the script I posted earlier to accept two user logon names, a source and a target, convert each name into a DN (and halt if either name is not found), check that both are users, make sure the target user is a member of groups the source is a member of, and also make sure the source is not a member of any other groups.

     

    # Script to copy group memberships from a source user to a target user.

    Param ($Source, $Target)
    If ($Source -ne $Null -and $Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # If any user not found, abort.
    Trap {"Error $_"; Break}

    # Retrieve Distinguished Name of current domain.
    $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    $Root = $Domain.GetDirectoryEntry()
    $Base = ($Root.distinguishedName)

    # Use the NameTranslate object.
    $objTrans = New-Object -comObject "NameTranslate"
    $objNT = $objTrans.GetType()

    # Initialize NameTranslate by locating the Global Catalog.
    $objNT.InvokeMember("Init", "InvokeMethod", $Null, $objTrans, (3, $Null))

    # Retrieve NetBIOS name of the current domain.
    $objNT.InvokeMember("Set", "InvokeMethod", $Null, $objTrans, (1, "$Base"))
    $strNetBIOSDomain = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 3)

    # Retrieve DN of source user.
    $objNT.InvokeMember("Set", "InvokeMethod", $Null, $objTrans, (3, "$strNetBIOSDomain$Source"))
    $SourceDN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 1)

    # Bind to source user object.
    $SourceUser = [ADSI]"LDAP://$SourceDN"

    If ($SourceUser.Class -ne "user")
    {
        "Source is not a user"
        Break
    }

    # Retrieve DN of target user.
    $objNT.InvokeMember("Set", "InvokeMethod", $Null, $objTrans, (3, "$strNetBIOSDomain$Target"))
    $TargetDN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 1)

    # Bind to target user object.
    $TargetUser = [ADSI]"LDAP://$TargetDN"

    If ($TargetUser.Class -ne "user")
    {
        "Target is not a user"
        Break
    }

    # Hash table of source user groups.
    $List = @{}

    # Enumerate direct group memberships of source user.
    ForEach ($SourceDN In $SourceUser.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN, $True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember($TargetUser.ADsPath) -eq $False)
        {
            # Add the target user to this group.
            $SourceGroup.Add($TargetUser.ADsPath)
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDN In $TargetUser.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN) -eq $False)
        {
            # Source user not a member of this group.
            # Bind to group object.
            $TargetGroup = [ADSI]"LDAP://$TargetDN"
            # Remove target user from this group.
            $TargetGroup.Remove($TargetUser.ADsPath)
        }
    }

    -----

     

    I've tested this briefly in my domain. It works in PowerShell V1.0. You can probably do the same with the AD cmdlets you refer to, but I would need to test to check on the consequences if you attempt to add a user to a group they are already a member of, or remove a user from a group they are not a member of. The AD modules would also be slower, although that should not matter here with so few AD objects involved.

     


    Richard Mueller - MVP Directory Services
    Saturday, October 22, 2011 4:29 PM
  • I haven't tested this version, but it uses the AD modules. I added steps to check for membership before adding or removing.

     

    # Script to copy group memberships from a source user to a target user.

    Param ($Source, $Target)
    If ($Source -ne $Null -and $Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # Retrieve group memberships.
    $SourceGroups = Get-ADUser $Source -Properties memberOf
    $TargetGroups = Get-ADUser $Target -Properties memberOf

    # Hash table of source user groups.
    $List = @{}

    #Enumerate direct group memberships of source user.
    ForEach ($SourceDN In $SourceGroups.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN, $True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember($TargetUser.ADsPath) -eq $False)
        {
            # Add the target user to this group.
            Add-ADGroupMember -Identity $SourceDN -Members $Target
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDN In $TargetGroups.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN) -eq $False)
        {
            # Source user not a member of this group.
            # Remove target user from this group.
            Remove-ADGroupMember -Identity $TargetDN -Members $Target
        }
    }

    -----

     


    Richard Mueller - MVP Directory Services
    Saturday, October 22, 2011 5:24 PM
  • On Sat, 22 Oct 2011 16:29:27 +0000, Richard Mueller [MVP] wrote:

    First, what you call logon name is probably the sAMAccountName. This is the "pre-Windows 2000 logon" name of the user and uniquely identifies the user. If, however, you meant the "Name" in ADUC, the is the Common Name of the user (the value of the cn attribute), which does not uniquely identify the user.

    OK, then yes, it is the SAMAccountName; it's what the user types in for
    User Name when logging in at the workstation, and in our environment,
    it's a combination of the user's surname and employee number.  The
    attribute you are referring to (the CN) is what appears on the Start
    Menu, and is generally something like "Smith, Jason (xxx)" where XXX is
    the 3LSD of the employee number.

    Second, groups can be nested. If user "Jim" is a member of group "West, and group "West" is itself a member of "East", then "Jim" gets all permissions granted to both groups. "Jim" is effectly a member of group "East" due to group nesting. However, there is no need to account for this here. We can ignore it. If we make "Jim" a direct member of "West", then he will automatically get the required permissions. No need to also make "Jim" a direct member of "East", which would only be confusing.

    OK, this is what I expected, and it matches my understanding of the way
    security policies are applied.  It is indeed not relevant for my
    purposes.

    Third, the "primary" group is never included in most methods that reveal group membership. It is best to assume that everyone has the group "Domain Users" designated as their primary (which is the default). There is almost never a reason to change the primary group membership. And, it would require more code to deal with primary group memberships.

    I know that we do not change this; it's good that I won't have to worry
    about the Domain Users group.

    I have modified the script I posted earlier to accept two user logon names, a source and a target, convert each name into a DN (and halt if either name is not found), check that both are users, make sure the target user is a member of groups the source is a member of, and also make sure the source is not a member of any other groups.

    Thank you; I'll study this script further. Other than the compatibility
    with Powershell 1.0, is there any benefit to doing it as you've done it
    vs. using the MS AD cmdlets?

    # Script to copy group memberships from a source user to a target user.

    Param ($Source,$Target)
    If ($Source -ne $Null -and$Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # If any user not found, abort.
    Trap {"Error $_"; Break}

    # Retrieve Distinguished Name of current domain.
    $Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    $Root = $Domain.GetDirectoryEntry()
    $Base = ($Root.distinguishedName)

    # Use the NameTranslate object.
    $objTrans = New-Object-comObject "NameTranslate"
    $objNT = $objTrans.GetType()

    # Initialize NameTranslate by locating the Global Catalog.
    $objNT.InvokeMember("Init","InvokeMethod", $Null, $objTrans, (3, $Null))

    # Retrieve NetBIOS name of the current domain.
    $objNT.InvokeMember("Set","InvokeMethod", $Null, $objTrans, (1, "$Base"))
    $strNetBIOSDomain =$objNT.InvokeMember("Get","InvokeMethod", $Null, $objTrans, 3)

    # Retrieve DN of source user.
    $objNT.InvokeMember("Set","InvokeMethod", $Null, $objTrans, (3, "$strNetBIOSDomain$Source"))
    $SourceDN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 1)

    # Bind to source user object.
    $SourceUser =[ADSI]"LDAP://$SourceDN"

    If ($SourceUser.Class-ne "user")
    {
        "Source is not a user"
        Break
    }

    # Retrieve DN of target user.
    $objNT.InvokeMember("Set","InvokeMethod", $Null, $objTrans, (3, "$strNetBIOSDomain$Target"))
    $TargetDN = $objNT.InvokeMember("Get", "InvokeMethod", $Null, $objTrans, 1)

    # Bind to target user object.
    $TargetUser =[ADSI]"LDAP://$TargetDN"

    If ($TargetUser.Class-ne "user")
    {
        "Target is not a user"
        Break
    }

    # Hash table of source user groups.
    $List = @{}

    # Enumerate direct group memberships of source user.
    ForEach ($SourceDNIn $SourceUser.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN,$True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember($TargetUser.ADsPath)-eq $False)
        {
            # Add the target user to this group.
            $SourceGroup.Add($TargetUser.ADsPath)
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDNIn $TargetUser.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN)-eq $False)
        {
            # Source user not a member of this group.
            # Bind to group object.
            $TargetGroup= [ADSI]"LDAP://$TargetDN"
            # Remove target user from this group.
            $TargetGroup.Remove($TargetUser.ADsPath)
        } }

    -----

     

    I've tested this briefly in my domain. It works in PowerShell V1.0. You can probably do the same with the AD cmdlets you refer to, but I would need to test to check on the consequences if you attempt to add a user to a group they are already a member of, or remove a user from a group they are not a member of. The AD modules would also be slower, although that should not matter here with so few AD objects involved.

     


    -- Jeff Zeitlin
    Sunday, October 23, 2011 8:14 PM
  • On Sat, 22 Oct 2011 17:24:58 +0000, Richard Mueller [MVP] wrote:

    I haven't tested this version, but it uses the AD modules. I added steps to check for membership before adding or removing.

    Thank you for this as well; it, too, will be studied further.  I
    presume, from this, that the .MemberOf attribute as returned by
    Get-ADUser -Properties... is NOT in a form suitable for passing to
    Set-ADPrincipalGroupMembership. Or is there a benefit from looping
    through and using Add-ADGroupMember that I'm not seeing?

    # Script to copy group memberships from a source user to a target user.

    Param ($Source,$Target)
    If ($Source -ne $Null -and$Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # Retrieve group memberships.
    $SourceGroups = Get-ADUser$Source -Properties memberOf
    $TargetGroups = Get-ADUser$Target -Properties memberOf

    # Hash table of source user groups.
    $List = @{}

    #Enumerate direct group memberships of source user.
    ForEach ($SourceDNIn $SourceGroups.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN,$True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember($TargetUser.ADsPath)-eq $False)
        {
            # Add the target user to this group.
            Add-ADGroupMember -Identity $SourceDN -Members $Target
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDNIn $TargetGroups.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN)-eq $False)
        {
            # Source user not a member of this group.
            # Remove target user from this group.
            Remove-ADGroupMember -Identity $TargetDN -Members $Target
        } }

    -----

     


    -- Jeff Zeitlin
    Sunday, October 23, 2011 8:17 PM
  • First, I found a mistake in the second script I posted (using Get-ADUser and PowerShell V2). I used $TargetUser.ADsPath, but $TargetUser is not defined and has no ADsPath property. It should be: "LDAP://" + $TargetGroup.distinguishedName. But I see the terminology I've used is confusing. $SourceGroup and $TargetGroup are not just collections of group DN's. They are objects representing users, one property of which is memberOf, the collection of group DN's. When I use Get-ADUser, I need to include -Properties because memberOf is not in the default collection of attribute values retrieved for the user. I'm thinking it would be more clear if I used variable names $SourceUser and $TargetUser, to  indicate they are collections of properties of the users. Even here, these objects are not the same as the user objects $SourceUser and $TargetUser I used in the first script (PowerShell V1), which are ADSI objects and expose methods and many more attributes of the user objects in AD. In any case, my corrected PowerShell V2 script follows:

     

    # Script to copy group memberships from a source user to a target user.

    Param ($Source, $Target)
    If ($Source -ne $Null -and $Target -eq $Null)
    {
        $Target = Read-Host "Enter logon name of target user"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter logon name of source user"
        $Target = Read-Host "Enter logon name of target user"
    }

    # Retrieve user information, including group memberships.
    $SourceUser = Get-ADUser $Source -Properties memberOf
    $TargetUser = Get-ADUser $Target -Properties memberOf

    # Hash table of source user groups.
    $List = @{}

    #Enumerate direct group memberships of source user.
    ForEach ($SourceDN In $SourceUser.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN, $True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target user is already a member of this group.
        If ($SourceGroup.IsMember("LDAP://" + $TargetUser.distinguishedName) -eq $False)
        {
            # Add the target user to this group.
            Add-ADGroupMember -Identity $SourceDN -Members $Target
        }
    }

    # Enumerate direct group memberships of target user.
    ForEach ($TargetDN In $TargetUser.memberOf)
    {
        # Check if source user is a member of this group.
        If ($List.ContainsKey($TargetDN) -eq $False)
        {
            # Source user not a member of this group.
            # Remove target user from this group.
            Remove-ADGroupMember $TargetDN $Target
        }
    }

    -----

     

    I looked into doing this in a pipeline, but if it is possible I don't know how. Note that in the first ForEach that enumerates source user groups, I require a property of the target user. If it possible, I don't see the advantage.

     


    Richard Mueller - MVP Directory Services
    Sunday, October 23, 2011 10:32 PM
  • Hi Jeff,

    Please feel free to let us know if the information was helpful to you.

    Regards,

    Tiger Li

    TechNet Subscriber Support in forum
    If you have any feedback on our support, please contact  tnmff@microsoft.com.


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Thursday, October 27, 2011 9:53 AM
  • I work for a company in the entertainment industry and we regularly run into this problem.  A Colorist, Artist, Film Processor, etc. will get hired and, more often than not, there are several other users in the department that already have the access he/requires.  Assuming the user account already exists and that you have the Quest AD cmdlets installed, the one-liner below uses pipelining and has worked quite well.  However, I'm no expert and would certainly appreciate any feedback (i.e. limitations, possible problems, etc.) you find with it.

    Get-QADUser 'john.doe' | Get-QADMemberOf | Add-QADGroupMember -Member 'jane.doe'
    


    Thursday, October 27, 2011 6:03 PM
  • On Thu, 27 Oct 2011 18:03:42 +0000, r3tic3nc3 wrote:

    I work for a company in the entertainment industry and we regularly run into this problem.  A Colorist, Artist, Film Processor, etc. will get hired and, more often than not, there are several other users in the department that already have the access he/requires.  Assuming the user account already exists and that you have the Quest AD cmdlets installed, the one-liner below uses pipelining and has worked quite well.  However, I'm no expert and would certainly appreciate any feedback (i.e. limitations, possible problems, etc.) you find with it.


    Get-QADUser 'john.doe' | Get-QADMemberOf | Add-QADGroupMember -Member 'jane.doe'

    I can't speak to how well it works, because the Quest AD cmdlets are
    unavailable to me.  However, even in theory, this only does half of the
    job I need; it doesn't DELETE access from jane.doe for groups that she's
    currently a member of that she should no longer be.

    I'm negotiating with my server admins for a test container and test
    users that I can use without risking problems with live users, so until
    they agree, I can't actually test any of the proposed solutions.

    My thanks to those who have contributed to this thread.


    -- Jeff Zeitlin
    Wednesday, November 2, 2011 1:16 PM
  • Richard:

     

    Would any of your scripts work for contacts and distribution lists?

     

    Tuesday, January 24, 2012 11:17 PM
  • Distribution lists are just another type of group. No adjustments needed. However, the script I posted used Get-ADUser, which cannot deal with contacts. We must use Get-ADObject instead. Also, contacts do not have a sAMAccountName attribute, so we must identify them by name (the value of the cn attribute). However, this may not uniquely identify the object. If the Name is unique in the domain, the Get-ADObject cmdlet will retrieve the object. However, when we add and remove contacts to or from groups, we must use the distinguishedName of the contact object. I've made these adjustments in this version of the script. However, I have not tested.

    '

    # Script to copy group memberships from a source contact to a target contact.

    Param ($Source, $Target)
    If ($Source -ne $Null -and $Target -eq $Null)
    {
        $Target = Read-Host "Enter name of target contact"
    }
    If ($Source -eq $Null)
    {
        $Source = Read-Host "Enter name of source contact"
        $Target = Read-Host "Enter name of target contact"
    }

    # Retrieve group memberships.
    $SourceContact = Get-ADObject -LDAPFilter "(Name=$Source)" -Properties memberOf
    $TargetContact = Get-ADObject -LDAPFilter "(Name=$Target)" -Properties memberOf

    # Hash table of source Contact groups.
    $List = @{}

    #Enumerate direct group memberships of source Contact.
    ForEach ($SourceDN In $SourceContact.memberOf)
    {
        # Add this group to hash table.
        $List.Add($SourceDN, $True)
        # Bind to group object.
        $SourceGroup = [ADSI]"LDAP://$SourceDN"
        # Check if target Contact is already a member of this group.
        If ($SourceGroup.IsMember("LDAP://" + $TargetContact.distinguishedName) -eq $False)
        {
            # Add the target Contact to this group.
            Add-ADGroupMember -Identity $SourceDN -Members $TargetContact.distinguishedName
        }
    }

    # Enumerate direct group memberships of target Contact.
    ForEach ($TargetDN In $TargetContact.memberOf)
    {
        # Check if source Contact is a member of this group.
        If ($List.ContainsKey($TargetDN) -eq $False)
        {
            # Source Contact not a member of this group.
            # Remove target Contact from this group.
            Remove-ADGroupMember $TargetDN $TargetContact.distinguishedName
        }
    }

    -----

    The same script could be used for users, but the users must be identified by Common Name (not logon name, which is sAMAccountName) and again this must uniquely identify the object in the domain (which is not otherwise required). Otherwise, the users and contacts must be identified by distinguishedName.

     


    Richard Mueller - MVP Directory Services
    Wednesday, January 25, 2012 12:16 AM
  • I am in a similar position at my work and was looking for PowerSehll script that would do this for me. This script is perfect. Thanks heaps
    Wednesday, August 15, 2012 12:23 AM