SharePoint: Profile Sync and the “Domain Users” group – the Primary Group problem

Update 4/15/20: I tested this with AD Import and SharePoint 2019. It’s still the same story with SharePoint 2019.

This problem manifests itself in a few different ways:

  • You create an Audience based on “Member Of” the “Domain Users” group. You notice there are only a couple (or maybe even zero) members shown, whereas you may have hundreds or thousands of users in that group.
  • You have a SharePoint Add-In (previously known as Apps) that uses OAuth to connect to SharePoint sites and pull data. When the sites are permissioned using the Domain Users group, the Add-In fails with Access Denied, or if it’s a Search-based Add-In, the search results are security trimmed.

     

  • Other OAuth / Server-to-Server (S2S) authentication and claims augmentation scenarios like Workflows utilizing Workflow Manager and Office Web Apps may fail with 401 – Unauthorized (Access Denied) when you depend on the “Domain Users” group to provide access to the SharePoint content.

This is known as the “Primary Group” Problem.

All of these issues occur because SharePoint User Profile Synchronization (FIM Sync, AD Import, etc) does not include the “Primary Group” within a users group memberships.

What is this “Primary Group”?

In an Active Directory domain, each user will have a “Primary Group” listed. By default, it’s the “Domain Users” group. All users in the domain automatically have membership in this group when they are created.

Seems like something called “Primary Group” is important. Why doesn’t SharePoint Profile Sync include it with a users group memberships?

The Primary Group is different. It is more of an “implicit” group membership. It shows on the users “Member Of” tab in Active Directory, but is not actually listed within their MemberOf attribute. Instead, it’s stored in the users PrimaryGroupID attribute (in relative ID form). You can see that for yourself using this PowerShell:

#UserInfoFromAD
$userName = "test1"
$account = ([adsisearcher]"samAccountName=$($userName)").FindOne()
"SamAccountName: " + $account.Properties.samaccountname
"DisplayName: " + $account.Properties.displayname 
"UPN: " + $account.Properties.userprincipalname 
"DN: " + $account.Properties.distinguishedname 
"Primary Group ID: " + $account.Properties.primarygroupid
"Group Membership: " + $account.Properties.memberof

During Profile Sync / Import, the users MemberOf attribute is used to determine group membership. Since their Primary Group is not listed within the MemberOf AD attribute, it’s not included. The result is that the SharePoint User Profile Service Application (UPA) does not think the user is a member of their Primary Group, which in-turn creates the potential for the symptoms listed above.

Possible Solutions:

For the Audience issue, it’s probably best to just pick some other domain group to use for the audience. One could argue that with audience targeting, using a group that contains all members of an entire domain is not exactly “targeting”.

For the other scenarios, the solution is to secure the SharePoint content with something other than the users Primary Group (which by default, is the “Domain Users” group). Again, if you’re using “Domain Users” to provide access, you’re not exactly locking the site down. You might as well just use the “Everyone” group.

You can either change the SharePoint permissions to use something other than “Domain Users” to provide access, or change the Primary Group for all users in Active Directory. However, it’s unknown which other applications may rely on “Domain Users” being the Primary Group, so that’s probably not a great solution.

Workaround:

If you’re using Domain Users all over the place to give users access to content, and replacing that with some other security group would be a monumental task, then I have a workaround. You can use the below PowerShell script to manually add group membership to “Domain Users” for all of your user profiles within the User Profile Service App.

Caveats:

  • The script assumes you have one User Profile Service App. If you have multiple, it must be adjusted to grab the correct UPA.
  • It assumes you have a single domain. If you import profiles from multiple domains, it would need to be adjusted to only add a Domain Users membership for the domain the user belongs to.
  • While Incremental Imports do not seem to affect it, a Full Profile Import will wipe out and then recalculate all group memberships for all users. In that case, the “Domain Users” membership you added will be gone, and you’ll need to run the script again. To be thorough, it may be best to run the script daily, and certainly after any Full import.
  • Most of the testing I did with this script was using SharePoint 2016 and SharePoint 2019, using AD Import with only a few hundred user profiles. It has not been tested on other versions of SharePoint, or for scalability / performance.
  • As always, take backups, and use at your own risk.

Requirements:

  • You must be logged on as a user that has Full Control of the User Profile Service app to run the script successfully.
  • You must have a mysite host site collection created and defined within the “Set Up My Sites” section in the UPA. — If you don’t, you may get a “This operation requires personalization rights” error.
#Title: Add-DomainUsersMembership
#Author: Joroar
#Date: 5/23/18
#This script is provided as-is with no warranties expressed or implied. Use at your own risk.
#Synopsis: Use this to add all user profiles to a group in the User Profile Service App
#Inputs: Just set the top variable: $groupDN
$groupDN = "CN=Domain Users,CN=Users,DC=contoso,DC=com" #The Distinguished Name for the AD group you want to add as a membership for all user profiles
### Change nothing below this line ###
Add-PSSnapin *sharePoint*
$source= "A88B9DCB-5B82-41E4-8A19-17672F307B95" #This is a constant for AD security groups in SP 2013 and 2016. No need to change.
#Get the default UPA from the default proxy group
$profileManager = [Microsoft.Office.Server.UserProfiles.UserProfileManager]([Microsoft.Office.Server.ServerContext]::Default)
#Get the group from the UPA
$DU = $profileManager.GetMemberGroups().GetMemberGroupBySourceAndSourceReference($source, $GroupDN)
#Loop through every user profile and add them to the group
$profiles = $profileManager.GetEnumerator()
foreach($profile in $profiles)
{   
    try{
        Write-host "Adding Domain Users membership for: " $profile.AccountName
        $profile.Memberships.Create($DU, "DistributionList", "Domain Users", "Public")
        }
    catch [system.Exception] {Write-host "Member add fail for: " $profile.AccountName "Probably already a member"}
}

Other Tips:

You can use this PowerShell to list out all group memberships for a give user, according to the User Profile Service App. That is helpful to see if the UPA thinks the user is a member of all the same groups that Active Directory shows. If there is a discrepancy between AD and the UPA, it’s likely a Sync configuration problem, like you don’t have all the OUs that contain the AD groups selected in the Sync connection.

#List group memberships according to the UPA
$user = "contoso\test1" #specify the user
Add-PSSnapin *sharePoint*
$profileManager = [Microsoft.Office.Server.UserProfiles.UserProfileManager]([Microsoft.Office.Server.ServerContext]::Default)
$up = $profileManager.GetUserProfile($user)
Write-Host "Account: " $up.AccountName
Write-Host "Name: " $up.DisplayName
Write-Host "Groups:"
$groups = $up.Memberships.GetItems()
$groups | select id, title, group | sort title | ft -AutoSize

Example of Workflow error when “Domain Users” is used to give permission to the site / list that the Workflow is running on:

RequestorId: 078c065c-2780-add0-0000-000000000000. Details: An unhandled exception occurred during the execution of the workflow instance. Exception details: System.ApplicationException: HTTP 401 {“error”:{“code”:”-2147024891, System.UnauthorizedAccessException“,”message”:{“lang”:”en-US”,”value”:”Access denied. You do not have permission to perform this action or access this resource.”}}} {“Transfer-Encoding”:[“chunked”],”X-SharePointHealthScore”:[“0″],”SPClientServiceRequestDuration”:[“10″],”SPRequestGuid”:[“078c065c-2780-add0-9f75-a1c22fe33411″],”request-id”:[“078c065c-2780-add0-9f75-a1c22fe33411″],”X-FRAME-OPTIONS”:[“SAMEORIGIN”],”Cache-Control”:[“max-age=0, private”],”Server”:[“Microsoft-IIS\/8.0″],”WWW-Authenticate”:[“Negotiate”,”NTLM”],”X-AspNet-Version”:[“4.0.30319″],”X-Powered-By”:[“ASP.NET”],”MicrosoftSharePointTeamServices”:[“15.0.0.4420″],”X-Content-Type-Options”:[“nosniff”],”X-MS-InvokeApp”:[“1; RequireReadOnly”],”Date”:[“Thu, 01 Nov 2018 14:05:23 GMT”]} at Microsoft.Activities.Hosting.Runtime.Subroutine.SubroutineChild.Execute(CodeActivityContext context) at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)”

Add a Comment