Auto provisioning of Dynamics / DataVerse Users

Adding users to the Power Platform universe is usually something handled by IT support, which is not what they want to do.

Power Automate offers all the connections needed to:

  • Create the user in Azure AD
  • Add users to Azure security groups which can bestow licences
  • Add users to any Azure groups required
  • Add security roles to users
  • Place enabled users into DataVerse teams

To enable a user in DataVerse we need a trigger. Most organisations probably already have a new starter process in place in some way, and appending the create in DataVerse is the natural place for this (a good use of a child Flow).

For this example, I’ll use adding a user to a team in Teams as my trigger. I built this originally for a DataVerse Power App, where I want the admin level users of that app to manage user access to the app rather than IT Support.

I’ll post a full guide to adding users from a new starter app to Azure AD soon.

The full flow to start, to show it is not too much:

What we will need (assuming the users are already in Azure AD with some kind of Office licence):

  1. A team in Teams. Ensure the users who will add members are owners of this Team.
  2. An Azure security group.
  3. The security roles required in DataVerse
  4. Any DataVerse teams required.
  1. Create a team in Teams; here I have created a team called Provisioning Users:

2. Create a new cloud Flow (preferably in a solution), and use the trigger of “When a new team member is added (preview)”. Note this is a preview connector so take precautions. Another reason to think about the trigger.

Choose the relevant Team in teams; being a trigger it cannot be a variable of any sort but a Team has to be chosen. The trigger throws out the user GUID (User ID triggerOutputs()?[‘body/id’] ), which is then added to a Azure security group. The security group is set in an environmental variable in the below case so I can change it per environment. In Azure, get the object ID of the security group and use it here.

Note that the trigger runs every 15 minutes which cannot currently be changed, and there is no (simple) way of distinguishing between owners and members of a team (An HTTP request into SharePoint can distinguish).

Adding a user to an Azure security group will fail if the user already exists there, and this will happen (users seem to return frequently in the real world). To get around this, error handling is required. A simple way is to allow the Flow to continue on failure of the Azure AD group, by configuring Run After in the next step:

There is always some kind of wait for users to sync in Azure, so a delay is required. I have found 5 minutes is not enough to be safe. 10 minutes is 97% fine but of course it depends on Azure cloud load. In practice, as the Teams poll happens every 15 minutes, often batches come through together, and it seems 3 or 4% or so fail at 10 minutes as they haven’t synced through to Enabled Users in DataVerse. Incrementing to a 10 minute pause helped, but still saw failures. Now the delay is randomized between 10 and 15 minutes. Generally, adding new users will not be time sensitive, so if possible extend the delay. Alternatively, add a try catch so on failure can retry again in 5 minutes.

Now the user is licenced in the DataVerse world, they will become an enabled user in any environment which is not locked down by a security group. Generally, environments will have an Azure security group restriction on them. It may be a case that the user will need to be added to further groups than the licence group, so just add in another step above in the Add Groups.

Now we get to the adding security roles and the user to DataVerse teams. First get the user principal name, as the user GUID for Azure AD and DataVerse are different.

Note: You can link a DataVerse team up to an Azure team, but this does not really work as you probably want. This team can take a security role, but team security roles do not work the same way as individual security roles, as the team may own records and not the user, and this can get confusing depending on the business processes. (“If a User has no Access to a particular Privilege in any of their own Roles, and they have a Team Role which grants only “User” level access to that Privilege, then this only allows the User to do that action to records which are owned by the Team).

Then use the DataVerse action to list matching users with the User Principal Name

Then get the security roles:

If you need to get more than one, then use:

name eq 'Basic User' or name eq 'Delegate

and then filter on the role you need.

The key part of the security role is getting the ‘odata.id’ from it to relate to the user. This is the full API URL which will be used in the relate rows.

"@odata.type": "#Microsoft.Dynamics.CRM.role",            

"@odata.id": https://domain.crm11.dynamics.com/api/data/v9.1/roles(bd575865-29b0-eb11-8236-000d3a874bd8)

The DataVerse action Relate Rows can relate pretty much anything, so here it will relate the user to the security role.

The security role id is the first result from the List rows in Security Roles for the Relate with box:

first(outputs('List_rows_in_Security_Roles')?['body/value'])?['@odata.id']

and the Row ID of the user is

first(outputs('List_rows_in_users')?['body/value'])?['systemuserid']

Now the user has the required security role(s), the user can easily be added to teams in DataVerse, using the same logic of listing rows in Teams matching the required team(s), and then relating the user to that team. Quite likely here a loop may be required if multiple teams are required.

Again, the odata.id is required from this output as before.

The Team membership role id is the first result from the List rows Teams for the Relate with box:

first(outputs('List_rows_Teams')?['body/value'])?['@odata.id']

and the Row ID of the user is as before:

first(outputs('List_rows_in_users')?['body/value'])?['systemuserid']

After I received a few fails on this Flow, I added on error handling to my Flow Logging entity. All the security roles and team DataVerse actions are now placed in the Try Sec Roles scope, and the Catch Sec Roles captures any error in those steps. The most common error is that the user does not yet exist in DataVerse, as the sync is slow between Azure and DataVerse. This is then caught, and the error including the user’s principal name is written to the Flow Log, where another Flow fires to raise a ticket with IT Support.

The final step can be a notification to the user to inform them they are now enabled and can include training guides etc.

Notes: If there are no licences left there will be no notification. An HTTP/Graph request into Azure can validate the licences left beforehand. See this Jan Bakker article for the idea, which I will incorporate when time permits.

Issues found. In Azure AD, a primary email address can include a single quote. Using the above List rows then failed on the quote needing to be replaced.

List rows on domainname eq ‘Andrew.O’Brien@mydomain.co.uk’ will fail on

Syntax error at position 28 in 'domainname eq 'Andrew.O'Brien@mydomain.co.uk'.

This means that every email address used in a List Rows (or every time List rows is used anywhere with a text string?) needs to escape it out first using a replace like this:

replace(outputs(),””,’\”’)

Perhaps for efficiency, this could be handled in a Catch statement as it will be quite rare – and definitely one to write to the Flow Error logging !

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s