Have you ever wanted to keep the members of a SharePoint group in sync with those of an Active Directory Group?
If so, you’re in luck, I happen to have just such as script.
Just a quick note, this was written for and tested on a 2010 site – in 2013, the default authentication is Claims – those funny looking strings like this: “i:0#.w|domain\user” (more info here) and you’ll need to work this script over a few times to make that work.
That said, this script will keep a SharePoint group in sync with an AD group.
The AD group is considered the “master”
That is to say, if the AD group has extra users that aren’t in SharePoint, they will be added to SharePoint.
If a user is removed from the AD group, they will also be removed from the SharePoint Group.
If a user is added to the SP Group, but isn’t in the AD group? They will be removed from the SP group.
This is one of those scripts that makes sense to run as a scheduled task once you get it working. If you need help with that, see my post: Schedule your PowerShell scripts using the Windows task scheduler.
#AD to SharePoint Sync Script #Jack Fruh 2014 Sharepointjack.com write-host "NOTE: This has only been tested on NTLM authentication - it will likely need work to work with claims based authentication in 2013" -foregroundcolor red function main() { #setup the environment so this can run as a schedule task: #note that the AD PowerShell Commands must be installed on the SharePoint Server for this to work. Write-host "note that the AD PowerShell Commands must be installed on the SharePoint Server for this to work." -foregroundcolor blue Write-host "Adding SharePoint Snapin" $Host.Runspace.ThreadOptions = "ReuseThread" Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue Write-host "Importing AD module" import-module activedirectory write-host "Starting work..." #Hard coded variables for testing, # we need the name of the AD Group, the name of the corresponding group in Sharepoint to sync with, and the URL of the SPWeb where the SP group resides. $ADgroupname = "TheGroup" $SPGroupName = "SyncMeWithAD" $spweburl = "http://sharepoint2010" #note that it's reasonably easy to turn this hardcoded list into a CSV import and then loop through multiple groups #get a list of the AD Users in the AD Group #$ADGroupMembers = get-adgroupmember -Identity $ADgroupname | select @{name="LoginName";expression={$_.samaccountname}} $ADGroupMembers = get-adgroupmember -Identity $ADgroupname | select @{name="LoginName";expression={$(getDomain($_.distinguishedName)) + "\" + $_.samaccountname.toupper()}} if ($ADGroupMembers -eq $null) { write-host "The AD Group we're syncing with is empty - this is usually a problem or typo - the SP group will be left alone" -foregroundcolor red exit } #get the list of users in the SharePoint Group $web = get-spweb $spweburl $group = $web.groups[$SPGroupName] if ($group -eq $null) {write-host "SPGroup Not found" ; exit } $spusers = $group.users | select @{name="LoginName";expression={$_.LoginName.toupper()}} write-host "Debug: at this point we should have a list of user ID's from SharePoint in domain\user format, uppercase" foreach($x in $spusers) { write-host $x.LoginName -foregroundcolor green } if($spusers -eq $null) { write-host "The SPgroup is empty" -foregroundcolor cyan write-host "Adding all AD group members to the SP group" foreach ($ADGroupMember in $ADGroupMembers) { #add the AD group member to the SP group write-host "Adding $($ADGroupMember.LoginName)" write-host "new-spuser -useralias $($ADGroupMember.LoginName) -web $($web.url) -group $SPGroupName" -foregroundcolor green new-spuser -useralias $ADGroupMember.LoginName -web $web.url -group $SPGroupName # $web.site.rooteweb.ensureUser($ADGroupMember.loginname) set-SPuser -identity $ADGroupMember.LoginName -web $web.url -group $spgroupname } write-host "Done adding users - script will now exit" -foregroundcolor magenta exit } #use compare-object to get a listing of what's different between AD and SP write-host "Comparing AD Group Users to SP group Users" $result = compare-object -referenceobject $adgroupmembers -differenceobject $spusers -includeequal -property LoginName Write-host "Result of comparison is:" $result write-host "-------------------------" #filter the results of the comparison to show only the users that need to be added Write-host "Looking for users in AD that we need to add to SharePoint" $missingSPusers = $result | Where {$_.SideIndicator -eq '<='} | select LoginName #users in AD that are missing from SharePoint if ($missingSPusers -ne $Null) { foreach ($missingSPuser in $missingSPusers) { write-host "Adding $($missingSPUser.LoginName) to sharepoint" write-host "new-spuser -useralias $($missingSPuser.LoginName) -web $($web.url) -group $SPGroupName" -foregroundcolor green new-spuser -useralias $missingSPuser.LoginName -web $web.url -group $SPGroupName set-SPuser -identity $missingSPuser.LoginName -web $web.url -group $spgroupname } } #now do the reverse #Filter the results of the comparison to show 'extra' users that need to be removed write-host "Looking for Extra users in SharePoint that are not in AD" $extraSPusers = $result | Where {$_.SideIndicator -eq '=>'} | select LoginName #users in AD that are missing from SharePoint if ($extraSPusers -ne $null) { foreach ($extraSPuser in $extraSPusers) { write-host "Removing $($extraSPuser.LoginName) to sharepoint" Remove-SPUser -useralias $($extraSPuser.LoginName) -Web $web -group $SPGroupName -confirm:$false } } } # end main function function getDomain($distinguishedname) { $dn = $distinguishedname #extract Domain name from Distinguished name #Domain name should be the first DN= Entry from left to right. #Examples: CN=BOB,OU=Users,DN=DomainShortName,DN=Com #Examples: CN=BOB,OU=Users,DN=DomainShortName,DN=Forrest,DN=Com $start = $dn.indexof(",DC=")+4 #find the first occurance of DC in the Distinguishedname $end = $dn.indexof(",",$start) #find the first comma, this is the end of the domain name $domain = $dn.substring($start, $end-$start).toupper() return $domain } main
Now for a friendly reminder and some advice…
#1) Always test code you find online before using it in production…
#2) when you test this code, follow this advice:
Testing this code
When you test the code, you might make a mistake I made during development – I’ll share that mistake with you to save you an hour of time and some frustration.
Here’s what I did…
While testing, I wanted to try adding users to an AD group and wanted to make sure they added in correctly.
For one test I wanted to remove ALL the users from the SharePoint Group, and confirm that they came back ok.
To do this I used the UI to remove all the users – I checked each user, then clicked “actions->remove users from group” like this:
I then ran my Super Awesome AD Sync PowerShell Script which Added the groups back in.
Now here’s where it got ugly.
When I checked the UI, they weren’t there.
In fact, if I ran the powershell script again it indicated that they were being added back a second time (the script should have told me there was nothing to change!)
What was the cause?
It was my use of the refresh button…
Recall that the very last thing I did was remove users using that screen.
Now interestingly, you know how we all click “OK” on a screen without paying attention?
After I hit refresh, I got this, and ignored it:
See what I did there?
I was refreshing the delete in the UI!
Don’t make that mistake!
Instead of clicking the refresh button, it’s easier (and safer) to click the group name on the left:
Lessons learned:
- Pay attention to dialog boxes, they may save you an hour.
- Don’t ever click ‘refresh’ after performing a delete!