Synchronize users from Azure AD to WordPress

Before you start

  • You have installed the WPO365 | SYNC or WPO365 | INTRANET bundle (plus the WPO365 | LOGIN plugin that provides the basic functionality needed for either bundle to operate as expected).
  • You are a Global Administrator for your company‚Äôs Office 365 tenant / Azure AD directory (or have at least the ability to create and / or update Azure Active Directory App registrations).
  • You are an Administrator for your WordPress website.
  • You already configured Single Sign-on by following the steps documented in this article.
  • You already configure the Integration portion by following the steps in this article.

Related documentation

Please check out this video for a video tutorial for this article.

Support for Azure AD B2C

Starting with version 21, the premium SYNC and INTRANET bundles can also be used to synchronize users from Azure AD B2C to WordPress. Assuming that you already configure Azure AD B2C based Single Sign-on (SSO) for WordPress, you must update the corresponding App registration in Azure AD by performing the following steps.

  • Navigate to Azure Portal > Azure AD B2C > App registrations and select the application that you created to register the WordPress website.
  • Continue to the API Permissions page.
  • Click + Add a permission.
  • Select Microsoft Graph > Application permissions > User.Read.All.
  • Click Add permissions.
  • Click Grant admin consent for ...

 Then return to the plugin's Integration page.

  • Click Use app-only token.
  • Check Use existing App registration From the Single Sign-on tab.
  • Click Save configuration.

With this configuration in place, you should be able to perform the steps in this article and configure user synchronization for Azure AD B2C.

How it works

Even when you are in a hurry, you should read this paragraph. It gives you a high level understanding, of how the WPO365 plugin works and how it decides what users it should (soft) delete, once a job completes successfully.

The WPO365 user synchronization feature helps you to create a job that will synchronize users. This job can be started in one of the following ways.

  • Manually, by clicking Save + Run now.
  • Scheduled, by selecting a (WordPress CRON based) schedule e.g. once per day at hh:mm and then clicking Save + Schedule.
  • External link, by copying the link and call it from an external script / task service.

When a job is triggered, the plugin will retrieve a first batch of users from Microsoft Graph. To retrieve users from Microsoft Graph, the plugin needs a query. A query is a request, sent to Microsoft Graph, that will return users that match with the query's $filter parameter e.g. accountEnabled eq true. The plugin also expects the query to define at least two other parameters: $count (the total number of results, so that the plugin can track progress) and $top. An example of a valid query that returns all users in your organization would be:

myorganization/users?$filter=accountEnabled eq true&$top=10&$count=true.

To prevent the plugin from taking too long to process all users, the size of a batch of users returned should be pretty small e.g. 10 users. If the batch is too big, the server will experience a time-out error and processing will stop. The size of the batch is defined by the $top parameter, e.g. $top=10.

After the plugin processed the first batch, it prepares itself to request the next batch. To do so, it creates a new WordPress CRON job. This job will be processed as soon as WordPress CRON is checking for jobs to execute. Since WordPress CRON is not very reliable, we strongly advise you to put measures in place that improve the reliability (see for details). The plugin will keep creating batches until all users are retrieved from Microsoft Graph (or until an error occurs, in which case the job is stopped).

When the plugin processes a batch of users, it performs the following actions:

  • It tries to look up the user in WordPress by Azure AD Object ID, username, email address or (optionally, not by default enabled) short login name (e.g. johnd for a user with Azure AD user name johnd@domain.tld).
  • If it finds the corresponding WordPress user, it will update that user, if you configured the job to update users. Updating a user includes updating any automatically assigned roles, custom user attributes e.g. jobTitle or employeeID etc. and the WordPress avatar. Always when the plugin updates a user, it will tag the user with its Azure AD Object (OID).
  • If it does not find a corresponding WordPress user, it will create a new user if you configured the job to create users. As soon as a new WordPress user is created, it will update that user accordingly.
  • Finally, all WordPress users that the plugin finds and creates will be tagged (even if you did not check the option to update users in WordPress). This way, the plugin can tell, after the job completes eventually, which users were affected by it.

If the plugin has finished processing all users, it will complete the job by handling all WordPress users that were not affected by it. Starting with version 21 of the plugin, you can choose how the plugin decides what users should be (soft) deleted:

  • If you configure the job so that it should not Skip (the) domain check, the plugin will check for each WordPress user the username and email address and if it has a domain suffix e.g. @domain.tld that matches with a custom domain that you entered into the Custom domains list on the plugin's User registration page. If a WordPress user appears to belong to any of the custom domains that you configured and if the user is not affected by the current job, then the plugin will (soft) delete the user. This was the default behavior until version 21.
  • Opposite, if you choose to Skip (the) domain check, the plugin will select all WordPress users that have a tag with their Azure AD Object ID (OID) and that are not affected by the current job. All those users will then be (soft) deleted.
Important Please be aware that  you should not choose to skip the domain check if you did not configure the job to update the users. Because only if you configured the job, to update users, it will tag users with their Azure AD Object ID (OID).

Plugin configuration

To start synchronizing users it is important to ensure the correct configuration of the plugin. The table below summarizes all configuration options and their recommended value. 

The correct configuration can be verified using the plugin's built-in self-test tool. To self-test the plugin go to WP Admin > WPO365 > Plugin self-test and click to Start self-test. The self-test offers to fix most of the issues for you. However, some options require you to copy data from Azure Active Directory.
Option Page Value Action
Enable user sync User sync Checked Replace
Pages freed from authentication Single Sign-on /wp-json/wpo365/v1/ Add
Pages freed from authentication
Single Sign-on wp-cron.php Add
Graph version Integration beta Replace
Prevent WordPress to send "email changed" emails Miscellaneous Checked Replace
Enable WPO365 API for Microsoft Graph * Integration Checked Replace
Require users to sign-in [...] ** Integration [...] be signed in Select
Allow apps to request any [...] endpoint Integration Unchecked Replace
Allowed endpoints Integration *** Add
Custom Domains User registration See Azure Portal Add

* The WPO365 API for Microsoft Graph is only used when you test your WPO365 User synchronization query and is not required for normal operation of the WPO365 User synchronization processor. If you do not want to keep the WPO365 API for Microsoft Graph enabled you can thus disable it again immediately after you tested your query successfully.

** If you do not want to grant application-level permissions to read all users in your tenant to the registered application in Azure AD then you - as an administrator - must sign in with Microsoft yourself or else you cannot test the Microsoft Graph query during the configuration of the WPO365 user synchronization job.

*** Add the endpoint corresponding to the User synchronization query e.g. e.g. or

The image below should help you to configure the WPO365 API for Microsoft Graph (on the plugin's Integration configuration page) for testing purposes (assuming that you have granted application-level in Azure AD to your App registration.

API Permissions (App

To synchronize users from Azure AD to WordPress the WPO365 plugin will attempt to retrieve those users from Microsoft's unified API called Microsoft Graph. To do so, the plugin requires permissions to read all users (and optionally also all Azure AD groups) from Microsoft Graph. Those permissions can be made available to the plugin's User synchronization processor as delegated  (granted to a signed-in user) as well as application  (granted to an application) permissions. With delegated  permissions you can only manually start User synchronization. With application  permissions you can choose to trigger User synchronization manually as well as from an external task scheduling service plus from a schedule (using WP-Cron jobs). We therefore strongly recommend that you configure app-only integration. Please note  that you must configure application permissions when you configured SAML 2.0 based Single Sign-on since SAML 2.0 does not support authorization scenarios through granting permissions.

Delegated scenario (permissions granted to the logged-in user)

  • Review the API Permissions you previously granted in Azure Portal to the App registration that you created to enable OpenID Connect based Single Sign-on (see and ensure that you have assigned delegated User.Read.All (and optionally delegated GroupMember.Read.All) permissions to your App.

Application scenario (recommend)

  • Review the API Permissions you previously granted in Azure Portal to the App registration that you created to enable app-only integration (see and ensure that you have assigned application User.Read.All (and optionally application GroupMember.Read.All) permissions to your App.
Granting GroupMember.Read.All permissions is not optional if you intend to implement Azure AD group based access restrictions and / or want to dynamically assign WordPress roles based on Azure AD groups.

Plugin self-test

At this point the plugin should be able to synchronize users. Perform the following steps to test your configuration.

  • Navigate to WP Admin > WPO365 > Plugin self-test and click to Start self-test.
  • Carefully inspect the section INSTALLED EXTENSIONS and make sure you are using the latest version of the WPO365 | SYNC or WPO365 | INTRANET bundle.
  • Scroll down to CONFIGURATION and make sure all but the following two tests are passed successfully:
    • Debug log disabled It may even be helpful to enable the debug log in the beginning.
    • Domain hint configured This configuration is not important for successfully synchronizing users.
  • Continue scrolling so SYNC and make sure that all tests are passed successfully. You can click the link view in the Data column to see the test data retrieved by the plugin.
At this point you'll may notice that the test WP-Cron is disabled is not passed. Since User synchronization heavily relies on WP-Cron and WP-Cron not runs continuously it is strongly recommended that you hook WP-Cron into a n external task scheduling service. You can follow the steps explained in  this article to achieve this

Adding a new User synchronization job

Now that the plugin is configured for user synchronization you must add at least one User synchronization job. To do so go to WP Admin > WPO365 > User sync and click Add new job.

Perform the following steps to configure a User synchronization job.

  • Give your job a meaning full name e.g. ALL INTERNAL EMPLOYEES or WEBSITE ADMINS.
  • Update the query so it selects exactly those users that you want to synchronize to your WordPress website. You can click Show sample queries to see examples of such queries. Each time when you change your query you must test the query. You cannot save the job if the query is not tested successfully.
Please note that you can also test your query with the help of Microsoft's Graph Explorer.
The default query filters for userType eq 'member' but when you synchronize users from Active Directory (on-premise) the userType may actually be null and the query does not return any users.
You may notice that the plugin automatically adds a $count= parameter which you should not remove. The count (= total rows) helps the plugin being able to track progress
If you customize the query you must include the $top= query parameter e.g. $top=10 or $top=20. The $top parameter defines the page size and will force Microsoft Graph to return the full result in pages. The user sync processor is aware of such pages and after processing a result page will request the next page until there are no pages left. Omitting the query parameter or defining pages with too many results may cause a time-out error and the user sync job will not be completed as expected.
The plugin automatically detects a Microsoft Graph $count query and automatically adds the ConsistencyLevel = True header and thus allowing for advanced queries with $filter using endsWith and $search (see this article if you want to build advanced queries).This means that you can write a User sync query that includes all users from a specific domain as follows: myorganization/users?$count=true&$filter=endsWith(userPrincipalName,$top=10. 
  • Select the actions that should be performed when users are being synchronized e.g. 
Please note that if you do not check any of the options then user synchronization will simply log / preview the action it would have otherwise applied (see the next paragraph on monitoring progress. When you configure user synchronization for the first time it is strongly recommended that you preview the results by leaving any of the actions unchecked.
    • Create new users.
    • Update (custom user fields and / or dynamically assigned WordPress roles of) existing users.
    • Delete users.
    • Soft-delete users (only available if you previously selected to delete users).
    • Re-assign posts to (only available if you previously unchecked the option to soft-delete users and please note that you must have checked the user successfully to be able to save the job).
WordPress users are deleted at the end of a user synchronization job. The user synchronization processor then analyzes all WordPress users that were not affected by the job that is about to complete and for each of those users checks whether its user name or email address has a domain component that matches with any of the domains in the Custom domains list on the plugin's User registration configuration page.
  • Decide whether or not to ignore external identities (= Azure AD guest users) that may be included in the query result.
  • Select a trigger e.g.
    • Run manually by clicking the Save + Run now button at the bottom of the job's form.
    • External link which you can use to externally start user synchronization e.g. when you want to use an external scheduler. This is especially useful if you want to run the job more than once per day, which is not possible if you select WordPress Cron.
    • WordPress Cron which allows you to define a schedule e.g. once per day at a specific time.
  • Decide whether you want to send a log i.e. a summary each time the job completes. If you check this option, you'll be able to configure a specific email address where the log will be sent to.

Monitoring progress

Once a user synchronization job is running or has finished you can monitor progress as shown below.

After a user synchronization job has finished you click the View results button at the bottom of the job's form.

The table with results provides you with the following information:

  • All WordPress users.
  • WordPress users that have been created during the last run.
  • WordPress users that have been updated during the last run.
  • WordPress users that have been earmarked as deactivated (= soft-deleted) during the last run.
  • WordPress users that have been deleted during the last run.
  • Azure AD users that are not synchronized due to an error during the last run.
  • Logged Azure AD users that otherwise would have been created or updated or (soft-deleted) but none of those actions had been activated.


  • The external link trigger may not work as expected if caching is enabled for your website. In that case the link will work for the very first time or each time after your purged / refreshed the cache. In such a case you should add a dynamic parameter to the URL with a value that is dynamically generated to break the cache.
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us