Microsoft Power Automate is a fantastic platform when it comes to low-code automation, with connectors to literally hundreds of data sources.
But what if you need to do something too complex for the existing flow actions?
Take the Pro Code Escape with the Common Data Service.
In the true spirit of the #ProCodeNoCodeUnite movement, the Power Automate and Common Data Service platforms allow for extensibility to embed pro-code C# methods in no-code automation.
Let me take you through the tech of it.
With Microsoft Dynamics CRM 2015 Update 1 the concept of Custom Actions was introduced, where you created a specific type of workflow that was exposed through the API endpoint. This made it a lot easier to do something on the server as these actions could now be called natively from any kind of code, regardless if this code was executing backend, frontend, or on any other type of client. See full documentation at Microsoft Docs.
As of December 2019, it is also possible to call these endpoint methods from within a Microsoft Power Automate flow using the CDS (current environment) connector.
Almost as a side effect of the custom actions being exposed through API messages, it is possible to trigger plugins registered in CDS on calls to these actions.
Plugins triggering on custom action messages will receive the input parameters defined for the custom action and can post results to the output parameters defined. Our custom actions don’t have to “do” anything at all, they are just gateways between the custom action consumer and the plugins where we can write our complex business logic.
We now have a complete end-to-end method for taking Pro Code Escapes from Power Automate.
The Custom Action 🔗
I have a flow and I want it to perform some magic.
This magic is too complex to compose using available flow actions, so I will create a plugin performing the actual magic, create a Custom Action where input and output parameters to the plugin are defined, and call that action from my flow.
Creating a Custom Action
At the time of writing thing this article, it is still not possible to create custom actions from make.powerapps.com, you have to switch to the classic solution explorer. In this you can select New – Process.
Enter a name, select Category: Action, and unless you want it to be available for a specific entity, select Entity: None (global).
After creating the custom action, there is no need to change any of the general properties.
Defining Custom Action Arguments
The interesting bits come when we define the input and output arguments to the action. The input arguments will be passed to the plugin we register, and the output arguments can be set by the plugin.
Notice that the custom action does not need any steps, we will be performing all the magic within the plugin. Of course it is possible to also add steps, but in most cases that pre- or post-processing might as well be done in the flow calling the action. This also keeps the overall complexity down, with less different types of logic implemented.
Now just activate the Custom Action, and it is ready to be consumed!
The Flow 🔗
Let’s assume I have a Power Automate flow that reads info from a data source, and I need to perform something really complex – some true magic – on that information before continuing with my flow. The magic can only be performed using C#, or perhaps that magic already exists for other parts of your system architecture, and you don’t want to duplicate the implementation.
Calling the Custom Action
When it’s time to call the custom action, add Power Automate action Perform an unbound action (or the bound action equivalent if the custom action is defined for a specific entity). This task type has been available since late 2019, see announcement.
Now find the Custom Action name, which will be your solution prefix and then the name of your action without spaces. The input arguments specified will automagically show up as properties of the action. In this case I will just use the body of the previous call to get Rockets from CDS.
Fail fast or process results
Since I added the
ErrorMessage output arguments to the custom action, I start with checking the ErrorCode to fail fast using a guard clause. Read more about using guard clauses in Power Automate.
If the ErrorCode is not 0, the flow will send an email with the ErrorMessage and terminate with ErrorCode and ErrorMessage.
If the ErrorCode is 0, the flow can instead continue and process the results of the custom action.
The Plugin 🔗
Now let’s look at how we can create a stub for the plugin that shall handle the magic for my custom action.
The complete plugin project can be found on GitHub – enjoy!
A simple plugin
The plugin where we add the pro code magic is quite simple in its anatomy.
First the input arguments are extracted from the plugin context, then some initial validation, followed by a call to the actual magic, and finally placing the result in the output argument.
In this plugin class I inherit the
JonasPluginBase class from the repository with the same name on GitHub. This is a simple base class for plugins and the project also has some extension methods, like the
GetInputParametersForgiving method to retrieve input parameters without blowing up if it doesn’t find what is expected.
Of course the method
DoTheFancyMagicStuff is where the true magic happens, in this case I bet it will deserialize the JSON blob passed from the flow, investigate and do all kinds of crazy stuff with the objects, and then serialize it back to the JSON blob to return from the plugin.
Note the final line of the code above, where the result of the plugin is placed into OutputParameter
ResultJSON, the name that we specified when creating the custom action.
Generic error handling
If you look at the
GetInputParametersForgiving method you will see that it fails gently by setting output parameters for
ErrorMessage if an argument should be missing or of the wrong type.
OutputParameters on the plugin context is a better option than just throwing exceptions, since by this you move the control of how to handle possible errors to the flow that is calling the custom action.
Registering the plugin 🔗
When the plugin assembly is compiled, it is time to register it with the PluginRegistration tool (yes, I still use the one in XrmToolBox).
Now it is time for the major benefit of using Custom Actions – registering a plugin step for the custom message that has been added when creating the custom action.
Note that the Message is not one of the ordinary Create, Update, Delete etc. but my own custom message from the custom action:
In this case I select to register it Post-operation, to execute after all steps of the process have been completed – which really doesn’t matter since I never added any steps.
The registrations of assembly, class and step now look like this:
The Test 🔗
When the flow has been run, I check the run details to see how it behaved.
The call to the unbound action passed the Action Name, the flag to perform ExtraMagic, and the JSON input.
The results of the call returned ErrorCode = 0 and the resulting JSON. All good!
Provoking an error
Since my code had a check to return an error if the input JSON was empty, I will update my flow to send just that.
Running the flow again, and I get an email with the very simple error message I created in the guard clause of the flow.
Checking the flow run, I see the details of the error.
Note that since I decided to only return error codes and messages from the plugin, the call to the custom action did not fail, but it is instead up to the flow to handle possible errors.
And that, ladies and gentlemen, is how I make a Pro Code Escape in Power Automate!
Cover Photo by Matthias Zomer from Pexels
5 thoughts on “A Pro Code Escape in Power Automate”
Excellent post Jonas!! Nice way of tying various parts of Power Platform together
Very neat, thanks for posting. If I understand correctly, this is surfaced via a premium action and therefore any Flow that consumes it would need to run under a premium license?
Hey Martin, yes as Daniel says below this would require the premium connector, afaik (I really don’t keep up well with licensing issues)
@Martin: it looks like Jonas is using CDS actions, which means premium licenses are indeed required in this case!