This is the first article in a series of posts aimed at showing the main differences between Dataverse Custom APIs and the more traditional Workflow Custom Actions. This article will focus on the AllowedCustomProcessingStep attribute of Custom APIs.
Custom API vs Custom Action
Dataverse Custom APIs and Workflow Custom Action share common ground. The 2 models will expose custom Messages that can be called by the Dataverse WebApi like other Out-of-the-Box Messages of the Platform (ex. Create, Update). The differences are found in the underlying implementation of each model.
You can find Microsoft official documentation that compares the 2 models here
One of the most notable differences in my opinion is that with a Custom API you have the ability to restrict how 3rd Party developers can mingle with your custom Message by using the AllowedCustomProcessingStepType attribute.
But first, let’s go under the hood and see how a Custom API works.
Coding on the 30
If you are familiar with the Dataverse Message execution pipeline you know that there are 4 StagesPreValidation(10)
, PreOperation(20)
,MainOperation(30)
and PostOperation(40)
. The Main Operation (30) is reserved for the platform core operation and has always been (until now) obfuscated to developers.
Then, a bit like a suckerfish that attaches itself to a shark, developers can extend the Messages by attaching plugin steps to stages before or after the Main operation to alter the message behavior.
That is exactly the strategy that can be used to execute custom code using classic Workflow Custom Actions. You would start by defining your Action, Inputs, and Outputs in the classic workflow designer and leave the (low-code) execution part blank. This would create a new Message on which you can attach custom code on Pre or Post Operation.
Things are a bit different with Custom APIs, where you are actually coding the Message core operation itself, and the custom code triggered by the API executes on the Main Operation (30) (see Plugin trace below).
As a Power Platform developer, I personally felt a great deal of power⚡ when I first realized that I was coding on the 30 … but maybe I spend too much time in front of my computer 🤓.
In fact, with the Custom API model, you won’t even need to register any plugin steps with the Plugin Registration Tool for the code to be executed. You only need to configure your Custom API record to point on a Plugin exposed by a plugin assembly. Once this link between the Custom API record and a Plugin Type is made, the API is up and running.
To Allow or Not To Allow. That is the Question.
That being said, there is an attribute on the Custom API Table called AllowedCustomProcessingStepType that enables the author to configure how others will be able to attach or not to the Message exposed by the API. This attribute contains the following values.
- None
- This blocks completely any extension of the message
- AsyncOnly
- Only asynchronous plugins can be registered on the message
- SyncAndAsync
- Allows any kind of extensions on the message
This can be very important especially if the message being exposed by the Custom API carries sensitive information. For example, a message that would expose the Licensing status of an ISV product.
Let’s illustrate this with an example
Say we have an Environment Variable called ‘SensitiveUrl‘ that contains an URL that is critical to our business logic. A Custom API is responsible to retrieve the Environment variable value at run-time.
We have 2 Custom APIs that will execute the exact same code, but one has the AllowedCustomProcessingStepType set to None and the other set to SyncAndAsync.
Name | AllowedCustomProcessingStepType |
---|---|
GetEnvironementVariable | None |
GetEnvironmentVariable_OPEN | SyncAndAsync |
If you are interested, the GetEnvironmentVariable API implementation can be found in my collection of generic Dataverse Custom API.
Now let’s register a new Plugin Assembly on the Dataverse environment called HackTheApi. Since the GetEnvironmentVariable_OPEN is extendable because of its AllowedCustomProcessingStepType status (SyncAndAsync), it is possible to attach to the message synchronously and execute a diabolic 😈 hack. (Overdramatization )
Note that because of its AllowProcessingStep value set to None, the message GetEnvironmentVariable will not even appear in the list of available messages to extend.
The code of this nifty hack will substitute the value of the environment variable contained in the OutputParameters collection of the PluginExecutionContext with a different value.
When we test the APIs, we get completely different behavior.
GetEnvironmentVariable will give the expected result.
While GetEnvironmentVariable_OPEN will return the ‘hacked value‘.
I’m being dramatic on purpose here but you can see that leaving an API opened could have a significant impact on your business logic. This doesn’t mean that you should always block your APIs, it might be totally worth it and legit to allow Message extension in certain scenarios.
Async Only
The 3rd option for the AllowedCustomProcessingStepType is to allow only Asynchronous plugin steps to be registered on the Custom API message.
Using this option will assure you that the Outputs of your API call cannot be tampered with while offering to 3rd party developers a way to trigger async custom code after a specific API has been called. For example, every time an API is called, I want to increment a counter.
Summary
There are differences in the way custom code is executed between the Custom API and the Custom Action extension model. A Custom API custom code executes on the Main Operation (30) of the message execution pipeline, something that is not possible in other extension models.
We demonstrated how the AllowedCustomProcessingStepType attribute can have a significant impact on the behavior of Custom API. This level of control is only available on the new Custom API functionality and doesn’t exist for classic Workflow Custom Actions.
There is no Good or Bad way, every option has its pros and cons, and you need to understand the intent and the desired usage of your APIs before releasing them to the wild.
Happy API‘ing!
Thank you !