Tested on:
Dynamics 365 CE with Field Service version 8.8
I’ve previously written about syncing Dynamics 365 for Project Service Automation’s Resource Assignments to exchange with Flow. For the most part, that Flow can also be used to sync Bookable Resource Bookings to Exchange. The reason we want to do this is because the OOTB feature is deprecated. The Flow covered on this blog post can also delete a Bookable Resource Booking (later BRB) related calendar event from Exchange, if the BRB has a Field Service Status of Canceled.
The objective and the Flow
The objective is to sync Bookable Resource Bookings to a user’s Exchange calendar. When a new BRB is created, a new calendar event is created in Exchange. When a BRB’s Start/End Time, Duration or Booking Status changes, the new values are reflected in the related calendar event in Exchange. If a BRB’s Booking Status equals Canceled, the related calendar event in Exchange is deleted.
This Flow can be downloaded from the TDG Power Platform Bank and the download link can be found at the end of the blog post. Let’s dissect the Flow and see what it contains!
Step 1
The trigger in the Flow is the new CDS current environment trigger, which has a Create or Update trigger condition. This trigger can be selected when a Flow is built in a solution. The new trigger not only simplifies Flows in general as creating, updating and even deleting is possible by using a single Flow, but it also drives makers to using Flows in solutions. This way customization and ALM best practices can be followed. For this Flow, the trigger either creates a new BRB record or updates an existing one when either Start Time, End Time, Duration or Booking Status fields change.
As the Flow reads, creates and updates calendar events in Exchange, the Microsoft Graph API will be used. This means that an Azure AD Application is needed. My blog post about Creating a Planner plan from a D365 PSA Project with Flow will get you forward with AAD Apps, if you’re not familiar with them.
I have created Compose actions for all different Booking Status values used in Field Service, in case you have a use case where these values are needed. For this example, the Canceled status is the only one that is needed. If you don’t need the other values, you can remove the related Compose actions from the Flow.
Step 2
The condition in step 2 checks that Work Order on a BRB record contains a value and that Project doesn’t. As PSA also uses Bookable Resource Bookings, we want the Flow to terminate as canceled if it was fired off in PSA’s context. After the condition, Get records actions are used to access the related Work Order, Booking Status and Bookable Resource records. The User record gives us access to a user’s O365 user profile, so that we can get a user’s User Principal Name (UPN).
Step 3
In step 3, the first action will be to define what the subject of a new event in a calendar will be. For this example, I’ve used dynamic content from Work Order Number. The subject should remain unchanged so that the Flow works as intended so make sure you don’t include an action that changes the subject. Next, we’ll get a user’s calendar with the HTTP – Get Calendars action. We can then parse the JSON and filter the results with a Filter array action. We want to see if Name equals the value set in the Compose – Default Calendar name action. In this example the value is Calendar. The JSON schema is included in the zip file that contains this Flow.
Step 4
After the Filter array action, we have our hands on the specific calendar we’re interested in. Calendar ID is needed so that we can get, post, patch and delete events so a Compose action is used after the Filter array. This automatically creates an Apply to each loop. Next we’ll use an HTTP action to get all events with a subject that matches what we composed for subject earlier. We should only have a single event in Exchange that matches, however if there are duplicates for some reason, the Flow will still update all events where the subject matches. Next we’ll use parse JSON for the previous HTTP action’s content. The schema is included in the zip file that contains this Flow.
The Compose actions for Start Time and End Time are legacy from the Flow that updates calendar events in Exchange based on PSA’s Resource Assignments. I’ve left them in this Flow as you may have use cases where manipulating Start and End Time is needed. The outputs of the Compose actions are referenced in the Compose – Event body for updated event on true side action. If you remove the Start/End Time Compose actions from the Flow, remember to also edit the Compose action for the event body. The body of the calendar event is a convenient place to indicate the status of the BRB.
Step 5
In the final 5th step, a calendar event is either created or an existing calendar event is updated or deleted. The first condition checks if Parse JSON – Get events returns a subject that equals the subject we defined in the Compose subject for Exchange calendar event action. If a match is found, an existing calendar event is updated or deleted. Before this is possible, an event ID needs to be composed. We will get our hands on the ID from the previous step’s Parse JSON – Get Events action. The second conditions checks the Field Service Status of the BRB. If the status is Canceled, a calendar event in Exchange is deleted. If the status is anything else, a calendar event is updated.
If a match for the subject is not found, a new event will be created in a user’s calendar. For both the true and false side, the body of the HTTP PATCH/POST is based on the Compose – Event body action.
I hope this blog post helps you in creating calendar events in Exchange based on Bookable Resource Bookings. If you want to try this Flow out, you can download both a managed and an unmanaged solution from the TDG Power Platform Bank here.
Edit: As the TDG website is being relaunched, the Flow can be downloaded from my Share Point Online here.
Thank you so much for this Antti. This works very well for my application.
Hi.
Do you know if this flow would work with an on-premise exchange server? If not, do you think the flow could be customized to work that way?
Thank you!
Hi,
I tried this on my own environments, but unfortunately, whatever permission I give to the registered app in AAD, the result is always the same – error in the “HTTP – Get Calendars” action. Right now I have given: Calendars.ReadWriteShared, Directory.ReadWrite.All, Group.ReadWrite.All, User.ReadWrite.All. Any idea what could be wrong? The same request when calling from Graph Explorer is just fine.
{
“error”: {
“code”: “NoPermissionsInAccessToken”,
“message”: “The token contains no permissions, or permissions can not be understood.”,
“innerError”: {
“requestId”: “ce966c07-2240-4d13-908e-4e0ed57dea7f”,
“date”: “2020-06-25T12:45:58”,
“request-id”: “ce966c07-2240-4d13-908e-4e0ed57dea7f”
}
}
}
Hey Robert,
Did you find a solution to your problem?
I have exactly the same error when i try this flow..
Rgds
Benny Giebens
Hi,
Is it possible to share this flow?
Or can I download it somewhere?
Thanks!
Hi Thomas. Unfortunately, the TDG site is down as it is being re-built and re-launched. I’m still going over different options of publishing all my Flows.
Hi Antti,
Did you find another place to publish the flow? I would greatly appreciate it.
Looks awesome! Do you by any chance have a download link available while the website is relaunching? Would very much like to try this out! 🙂
Thanks 🙂 The TDG site will be relaunched very soon. The link should be back up early 2021.
Hey Antti, could you please post here the full ‘Parse JSON – Get Calendars’ schema please?
Hi Antti, could you please post the JSON code for the Parse JSON – Get Calendars function. The rest of the code is cut off in the screenshot. Would be greatly apprieciated!
Of course. I’ve originally used Graph API samples from docs.microsoft.com.
{
"type": "object",
"properties": {
"@@odata.context": {
"type": "string"
},
"value": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"color": {
"type": "string"
},
"changeKey": {
"type": "string"
},
"canShare": {
"type": "boolean"
},
"canViewPrivateItems": {
"type": "boolean"
},
"canEdit": {
"type": "boolean"
},
"owner": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
}
}
}
},
"required": [
"id",
"name",
"color",
"changeKey",
"canShare",
"canViewPrivateItems",
"canEdit",
"owner"
]
}
}
}
}
Hi Antti,
Could you please post the JSON code for the Parse JSON – Get Events function.
Thanks in advance!
Thank you for your post and detailed clarification,
Can I have a downloadable link for your flow to try in our CRM? the above links are not working.
Mohammad the flow can be found here:
https://powerplatform.app/powerplatformbanklist/syncing-field-services-bookable-resource-bookings-to-exchange-with-flow/
However I’d recommend checking out my newest approach to syncing bookings first:
https://anttipajunen.com/two-way-sync-of-bookings-between-dynamics-365-and-outlook-part-ii-sync-from-d365-to-outlook/
I followed this article and works great. However it creates duplicate entries in outlook when the booking is created. I think it happens because when the booking is created, Microsoft calculates the travel time and updates the starttime with the update request instantaneously. So the flow is executed twice (Create and Update) at the same time.
Neha please see my newest post on syncing bookings here:
https://anttipajunen.com/two-way-sync-of-bookings-between-dynamics-365-and-outlook-part-ii-sync-from-d365-to-outlook/
It’s possible to control the trigger by checking the SdkMessage value from the trigger to determine whether the flow is running on create or update.