Compare AD Object Direct Group Membership
Who can guess I am working on an AD migration project at the moment? And it presents an opportunity to cut my teeth further on Powershell scripting. Yes the AD Powershell scripts I have produced so far could have been done much easier if I had used the AD module provided with AD in Windows Server 2008 R2, but unfortunately not everyone is in a situation where they can take advantage of that module or even the Quest Modules that are also available. Hence some of the long winded scripts I have been producing.
Back to this problemette I was presented with. Following a user migration process\procedure the AD support team had discovered that a number of users were not able to access some resources and as one of the checks during remediation, wanted a quick way of comparing a users Group Membership with that of the source account. The following script is what I came up with for them. Basically, it gets all the direct Group memberships for the source object and destination objects (as specified at the command line) and runs a compare operation. The on screen output is of a table that displays the Group Memberships that are different between the objects with an arrow indicator showing what side of the evaluation the Group Membership exists. So for example if you ran the following command :
.\Compare-UserGroups.ps1 -srcDomain domain1.local -destDomain woodgrovebank.com -srcsAMAccountName carlh1 -destsAMAccountName carlh2
You would get 2 files that list each objects Group Membership and something like the table below, which shows that the entry for destsAMAccountName (carlh2) is a member of a group named "Password Policy Group", which the entry for srcsAMAccountName (carlh) is not a member of and vice versa for the other groups.
InputObject SideIndicator
----------- -------------
Password Policy Group =>
EventLogAccess <=
DnsAdmins <=
Backup Operators <=
It is true this is just a "like for like" sAMAccountName comparison and not a true SID to SID\SIDHistory comparison and it only compares Direct membership, but the requirement was for a quick check to ensure nothing was awry for odd troubleshooting instances and suits the needs where Groups have been migrated wholesale. If you need anything more funky that does Group Nesting then I advise you pop over to here https://www.rlmueller.net/freecode1.htm . Richards scripts are awesome.
<#
####################################################################
Compare-UserGroups.ps1
Syntax: Compare-UserGroups.ps1 -srcDomain <SourceDomain> -destDomain <DestinationDomain> -srcsAMAccountName <UserNetbiosName> -destsAMAccountName <UserNetbiosName>
Example: Compare-UserGroups.ps1 -srcDomain domain1.local -destDomain woodgrovebank.com -srcsAMAccountName carlh -destsAMAccountName carlh
Purpose: Compares the DIRECT Group Membership of 2 user accounts.
Be aware that it compares the Netbios names (sAMAccountName) of the groups
and is only useful either within a domain\forest or after a migration
of a user account between domains where the group names have not been changed
as a consequence of the migration.
Params: As shown in syntax above or by typing the script name at the command prompt
Req: Windows 2003 SP2 or above, Powershell V2.
run "set-executionpolicy remotesigned" in Powershell
https://blogs.technet.com/b/carlh
Author: Carl Harrison
This script is provided "AS IS" with no warranties, confers no rights and
is not supported by the authors or authors employer.
Use of this script sample is subject to the terms specified at
https://www.microsoft.com/info/copyright.mspx.
Version: 1.0 - First cut
####################################################################
#>
Param (
[Parameter()][string]$srcDomain='',
[Parameter()][String]$destDomain='',
[Parameter()][String]$srcsAMAccountName='',
[Parameter()][String]$destsAMAccountName='')
Function Compare-GroupsHelp () {
$helptext=@"
NAME: Compare-UserGroups.ps1
Compares the DIRECT Group Membership of 2 user accounts
Be aware that it compares the Netbios names (sAMAccountName) of the groups
and is only useful either within a domain\forest or after a migration
of a user account between domains where the group names have not been changed
as a consequence of the migration.
PARAMETERS:
-srcDomain Source Domain (Required)
-destDomain Destination Domain (Required)
-srcsAMAccountName Netbios name of the user account in the source domain (Required)
-destsAMAccountName Netbios name of the user account in the destination domain (Required)
SYNTAX:
Compare-UserGroups.ps1 -srcDomain domain1.local -destDomain woodgrovebank.com -srcsAMAccountName carlh -destsAMAccountName carlh2
Thsi compares the group memberships that carlh from domain1.local has in domain1.local
with the group memberships that carlh from woodgrovebank.com has in woodgrovebank.com
"@
$helptext
exit
}
Function Get-LDAPUser ($UserName, $SourceDomain) {
$domain1 = new-object DirectoryServices.DirectoryEntry ("LDAP://$SourceDomain")
$searcher = new-object DirectoryServices.DirectorySearcher($domain1)
$searcher.filter = "(&(objectClass=user)(sAMAccountName= $UserName))"
$searcher.findone().getDirectoryEntry()
$domain1 =""
}
if(!($srcDomain)) {"Source Domain Required";Compare-GroupsHelp}
if(!($destDomain)) {"Destination Domain Required";Compare-GroupsHelp}
if(!($srcsAMAccountName)) {"Netbios Name or Source Account Required";Compare-GroupsHelp}
if(!($destsAMAccountName)) {"Netbios Name or Destination Account Required";Compare-GroupsHelp}
$srcUserGroupsFile = '.\srcUserGroupsFile.txt'
$destUserGroupsFile = '.\destUserGroupsFile.txt'
Write-Host
$srcUser = get-ldapuser $srcsAMAccountName $srcDomain
Write-Host $srcUser.displayName "is a member of" $srcUser.memberOf.Count " groups in domain $srcDomain. The groups are:"
$srcUser.memberOf | ft
Write-Host
$destUser = get-ldapuser $destsAMAccountName $destDomain
Write-Host $destUser.displayName "is a member of" $destUser.memberOf.Count " groups in domain $destDomain. The groups are:"
$destUser.memberOf | ft
Write-Host
$srcUserGroups = @()
$srcGroupsDN = @()
$destUserGroups = @()
$destGroupsDN = @()
Foreach($Group in $srcUser.memberOf)
{
$GroupsAMAccountName = ([ADSI]"LDAP://$Group").sAMAccountName.value
#$GroupsAMAccountName
$srcUserGroups += "$GroupsAMAccountName"
$srcGroupsDN += $Group.tostring()
}
Foreach($Group in $destUser.memberOf)
{
$GroupsAMAccountName = ([ADSI]"LDAP://$Group").sAMAccountName.value
#$GroupsAMAccountName
$destUserGroups += "$GroupsAMAccountName"
$destGroupsDN += $Group.tostring()
}
$srcGroupsDN | Out-File $srcUserGroupsFile
$destGroupsDN | Out-File $destUserGroupsFile
Compare-Object $srcUserGroups $destUserGroups -SyncWindow 100
$destUser = ""
$srcUser = ""