The biggest strength of Power Automate is it's user interface, based on the Logic App UI, but even more user friendly, it's why anyone can create amazing automations.
So why would anyone want to create a flow in code instead of with the UI, I have no idea, but I did anyway.
How it Started
I first got interested in the code of a flow when I had to update 500 flows (they didn't have connection references so I didn't want to have to manually update every connection when deploying to a dedicated environment).
Looking at the definition.json I was able to identify the connections of the flow. So I made a flow that read the definition, did a replace with connection reference, then updated the json. And now I I had 500 flows that now only need 3 connections on import, not 1500!
A few other occasions I find editing the definition beneficial, generally for find and replaces or validating complex expressions. But exporting, copying out of zip, updating, copying back and then importing, was far to cumbersome. And then I found this chrome extension Power Automate Tools
Edit Flow live in Code
When editing a flow, click the extensions button and it opens the definition to edit and save right in the browser.
Best of all, it has intellisense and even the same Validation check as the UI does:
So now I had the tool to properly edit/create in code. I would start a new flow in the UI and then flip to code, after looking at examples, and reading the schema Microsoft.Logic/schemas I was able to create working flows pretty quickly.
Basics
The definition object is quite simple, with all actions/steps in a actions
node (this is the same for container steps -steps that have actions/steps inside like Scopes and Conditionds).
The basic action structure is:
"Name_of_Step": {
"runAfter": {
"name_of_previous_stepAction": [
"when"
]
},
"metadata": {
"operationMetadataId": "idNumber"
},
"type": "type",
"inputs": "inputs from action/step"
}
An example would be a compose, named Get_id, which runs after an excel list all rows action, and has an input which is the utcNow()
expression:
"Get_Date": {
"runAfter": {
"List_rows_present_in_a_table": [
"Succeeded"
]
},
"metadata": {
"operationMetadataId": "df2acb42-3954-4b98-b8d9-g571120509"
},
"type": "Compose",
"inputs": "@utcNow()"
}
Actions will have additional nodes in inputs, like host
- connection info, parameters
- inputs from user and authentication
(generally always the same):
"authentication": {
"value": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']",
"type": "Raw"
}
Containers are very similar, but just have the actions
node which contains all internal actions/steps.
Definition Tips
A top tip I found was a useful website called www.objgen.com, which converted simple text into json, helped speed up my workflow no end. It meant I had to have 2 windows open, and some copy and pasting, but definitely was quicker.
You can use the 'Peek Code' in the UI to see parts of the action/steps code.
Other interesting things I found out about the definition.json was
Order
The order of the steps/actions isn't related to order in the Json (the order is when the step was created). The runAfter
key is what dictates the flow order, except within containers (steps that contain other steps like Scopes and Conditions). With these the first step within them is the flow order, as these steps don't have a runAfter
(because in the flow they don't, they use the containers)
Object Structure
Within the steps the order of the keys doesn't matter, but with the UI generated the order isn't consistent
e.g runAfter
- Standard Action: at beginning of object
- Condition: before else
Or type
- Standard Action: near beginning
- Scope: at the end
Optional
Not all keys are required, biggest example is operationMetadataId
"metadata": {
"operationMetadataId": "e76eee77-177d-47ef-b5c2-973ba7c29615"
},
This can be removed and has no impact (the operationMetadataId
key was only added recently, some legacy exports won't have them).
Results
So would I recommend creating flows in code, the answer is hell no. When I said I could make working flows, I neglected to say only basic ones. Why... well:
Speed of the UI (especially with the complexity and inconsistencies of all the steps/actions) is faster then code.
Populated inputs (like listing SharePoint Sites), are auto populated by built in he api calls.
Selection of GUID's through dropdowns (e.g selecting mailbox folder by name in Outlook actually selects folder GUID). Trying to track down these GUID's can be almost impossible, let alone in anyway quick.
Expressions/Variable list, having them to hand is so much easier than trying to remember them all.
Order having to key order in the runAfter
is a lot harder then the natural order in a UI, it's so easy to get wrong but not see.
But, and this is a big but, editing in code is incredibly useful. Where starting from scratch it is just to painful, but doing updates is really useful:
Updates with find & replace are so much easier.
Validating/Code Reviews can be so much easier buy just finding keys, like checking all the runAfter
keys are right or Get action have Pagination turned on.
Editing huge/nested flows is not fun in the UI. I've had flows so big or nested that the browser struggles when every step is opened up. Doing edits in the code will never hang your browser, and using the find allows you to jump straight to the step instead of searching.
I don't think I really expected to replace the UI with code, but I was surprised how useful I found it as a tool. Flipping between UI and the code review more often than I thought. Shout out to Kamil Rithaler, the developer of Power Automate Tools, it's a great tool which without it editing in code isn't practical, and it's free.
Further Reading
Top comments (2)
Hi David! Is this extension still supposed to work? I tried it both in Edge and Chrome, and in both browsers it merely pops up the extension management menu when you click the extension in the browser toolbar. It sounds extremely useful to me right now and I would love to get it working.
It can be temperamental for me, but if I refresh the page still works