Using Power Automate for Project Lifecycle Workflows in Project Online – Part 2
Background to the requirement to move to Power Automate
One great question from part 1, that was raised on LinkedIn by Adem, was Shouldn’t we be using Project for the web? My answer – of course you should! But there will be customers where the features they need aren’t quite there yet, or even if they are, Project Online just works and they have other business priorities right now, and don’t feel the need to change. As I mentioned in part 1, going the Power Automate route does at least get you heading in the right direction.
This 2nd article describes ways customers can obtain similar functionality to their current Project Online workflows using Power Automate, and specifically how they can interact with custom fields from Project Online from a flow. In this case I’m using Adaptive Card technology and Teams to gather the information, as an alternative to the Project Detail Pages used with current workflows. This is a great example of how Power Automate has greater abilities to integrate across the M365 ecosystem – and it can go beyond too – with plenty of external connectors.
Starting a workflow
This 2nd document concentrates more on the capture and setting of project details into custom fields – and my example starts from creating a project and uses the same techniques as in the Part 1 article. In this case I’m not checking EPT values – just getting right on with the process.
Capturing Data in Adaptive Cards in Teams and persisting these to Project Online Custom Fields
The approach I present here is to trigger the flow on project create, set various variables, then check in and publish the plan, so that I have access to it from the flow. I send e-mail out at this stage too. The next step uses a Teams Post adaptive card and wait for a response action to place an adaptive card into a specific channel. Adaptive Cards are a platform-agnostic method for sharing and displaying information without the complexity of customizing CSS or HTML. They are authored in JSON format and automatically transform into native UI within Teams, making them ideal for consistent information exchange between Teams and other services. The JSON returned from this call can be parsed and then the results used to set variables as required, or used directly within a REST call to SharePoint to set the values in the Project Online custom fields (after first checking out the project). I’ve chosen here to add a cost value, a multiline text value and also to set a specific lookup table (LU) value – just to cover the different approaches required. The final steps then check-in and publish the project. I have a couple of delays too – as some of these jobs are queued and I’m not checking they complete – just waiting a bit.
Let’s get started!
The variables I’m setting here are for the internal names of the custom fields (CF) I’m going to be setting. It is easy enough to find the GUID that represents the CF, just open up the CF and look at the Url and grab the GUID. But the internal name isn’t just the GUID. It is the GUID stripped on the dashes, then prefixed with Custom_. I set variables for the Division Lookup CF (LU), the Cost and Goal CF, that contain their internal names and also set a variable for the Division LU value, which will be chosen from the adaptive card (or at least the readable version will be chosen, and that will also get me the GUID). I have manually hard-coded the internal names here, but later, when I get the GUID for the specific lookup table member, I do that using an expression.
The remainder of this flow looks like this:
The check-in and publish just needs the PWA Url (I could have set this as a variable, as I use it in a few places…) and the Project ID, which was obtained as output from the trigger that feeds of the new project creation:
So what is this Adaptive Card stuff all about?
If you aren’t familiar with Adaptive Cards, then this is a good place to start - https://learn.microsoft.com/en-us/adaptive-cards/ The next few steps in this workflow need the most explanation, and I’m also hoping some tooling could be built around this as it shouldn’t be too difficult to decide which custom fields you wish to work with, and then the JSON for the Adaptive Card, the schema for parsing, and the dictionary for setting the fields in the REST call could all be generated. A project (and possible blog post) for another day… The first action in this part of the process is the Post adaptive card and wait for a response. This has options on how to post and I use a Flow bot, and post in a Channel. For the Message the scrrenshot just contains the first part, so I’ll post the full text as you might want to copy/paste as a starting point for your own. There is an Update message which pops up once the card is submitted and then the Team and Channel the post is going to are in the final two entry fields. I’ve told a white lie with the Update message – the fields are not yet added to PJO, but they will be:
\ /
/ \
What does the user see in Teams?
This is what it looks like in Teams after I’ve added some data (the IT field was chosen from a drop down):
Once I hit submit, it then comes back with:
As promised, here is the json that went into the full message. Only the first and last few lines shows in the screenshot above.
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{
"type": "TextBlock",
"text": "Project Proposal: @{triggerOutputs()?['body/ProjectName']}",
"size": "large"
},
{
"type": "TextBlock",
"text": "Thank you for proposing @{triggerOutputs()?['body/ProjectName']}. Please enter the estimated cost, the goals of the project and the delivery division below",
"wrap": true
},
{
"type": "Input.Number",
"id": "Cost Value",
"placeholder": "Enter the proposed plan cost",
"label": "Enter cost",
"min": 1,
"max": 100000,
"value": 25000
},
{
"type": "TextBlock",
"text": "Please enter the goals of this plan"
},
{
"type": "Input.Text",
"id": "Goals Value",
"placeholder": "Enter goals of this plan",
"maxLength": 500,
"isMultiline": true
},
{
"type": "Input.ChoiceSet",
"id": "Division Value",
"style": "compact",
"label": "Which division would deliver this proposed project?",
"isMultiSelect": false,
"value": "1",
"choices": [
{
"title": "IT",
"value": "b0e5895f-d3d1-ee11-9ebd-00155dd8354d"
},
{
"title": "HR",
"value": "b1e5895f-d3d1-ee11-9ebd-00155dd8354d"
},
{
"title": "Sales",
"value": "b2e5895f-d3d1-ee11-9ebd-00155dd8354d"
}
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
}
Handling the response from the card
What I did to make the next steps easier was to set the id of the fields to Cost Value, Goals Value and Division Value, to make the parsing more readable, and by setting the actual GUID values of the lookup table choices to their actual GUIDs (picked from the view source on the lookup tables page, and yes, a tool would be nice).
By running a test of the flow at this point I can also see the returned json from the Adaptive Card – which can be plugged in to the Parse JSON action as a sample to generate the schema. Just drill into a flow that has run to see the responses – I’ve scrolled down in this one. so you see the parts that are most interesting, but copying the whole body is useful in the next step:
The Parse JSON takes the dynamic content Body of the previous step and puts it into objects that can be selected from the dynamic content for future steps. By clicking on Generate from sample and then entering the json copied from the body above, you will get the right schema:
In this example, the schema in full is:
{
"type": "object",
"properties": {
"responseTime": {
"type": "string"
},
"responder": {
"type": "object",
"properties": {
"objectId": {
"type": "string"
},
"tenantId": {
"type": "string"
},
"email": {
"type": "string"
},
"userPrincipalName": {
"type": "string"
},
"displayName": {
"type": "string"
}
}
},
"submitActionId": {
"type": "string"
},
"messageId": {
"type": "string"
},
"messageLink": {
"type": "string"
},
"data": {
"type": "object",
"properties": {
"Cost Value": {
"type": "string"
},
"Goals Value": {
"type": "string"
},
"Division Value": {
"type": "string"
}
}
}
}
}
I then check-out the project, and delay for a few seconds. The checkout isn’t queued, so should be quick, but just in case…:
The variables set up for the custom field internal names at the start of this workflow, have a similar counterpart in the lookup table entries. The next step takes the value for the field chosen above, and converts to the internal name by prefixing with Entry_ and stripping out the dashes in the GUID. The full expression in this case is replace(body('Parse_JSON')?['data']?['Division Value'],'-','')
I could have set the full internal name in the Adaptive Card, but I either didn’t think of it at the time, or though it would be a good learning exercise to do it with this action. You decide.
Pushing the captured information into Project Online
Now we have all the data we need for the REST call to SharePoint, so we use a Send an HTTP request to SharePoint action with the following settings:
You should be able to follow what’s happening here, but for clarity the customFieldDictionary JSON ends up looking something like this:
{"customFieldDictionary":[
{"Key":"Custom_1d3c4898d3d1ee11b50500155dd9564e","Value":"Entry_b0e5895fd3d1ee119ebd00155dd8354d","ValueType":"Edm.String"},
{"Key":"Custom_af05782fd3d1ee1194fe00155dd83733","Value":"To give a good demonstration of the use of Adaptive Cards in Teams as a way to capture data of all kinds and feed into Project Online's custom fields.
To make a good blog post with meaningful images","ValueType":"Edm.String"},
{"Key":"Custom_897f7774d2d1ee118b3500155dd84a5c","Value":"85000","ValueType":"Edm.Double"}
]}
The first key is the lookup table entry for IT. The second is my multiline field for Goals, and final key is the Cost.
All that is left here is to have a short delay, then check in and publish:
I haven’t expanded these as we’ve used these steps before.
If we pop over to Project Online, and the project that was created to kick off this workflow, we should see we now have some custom fields set in our Project Details PDP:
Hopefully that all made sense – any questions then please do ask in the comments section. As in part 1, we are eager to hear your experience if you have tried going to Power Automate so reach out to us at PJOQueries@microsoft.com and let us know.
For customers who believe the above approach using Power Automate is not addressing their requirements, and are seeking extension of workflows for genuine scenarios, also please reach out to us on PJOQueries@microsoft.com.
The next part won’t follow quite so quickly, and I may cover a variation of the Worflow Status approach, but using Dataverse, or look at some tooling for generating the JSON, Schema and Dictionary used in this workflow.