Azure Logic App (Consumption) is one of the most used integration services for orchestrating critical workflows, and an error in it would essentially affect your business continuity.
By default, Logic App allows handling errors using the Configure run after settings at a per action level. For more complex scenarios, it can be done by setting up Scope action and implementing try-catch or try-catch-finally statements.
This video aims to explain and discuss error handling within Logic Apps and explore the implementation of a try-catch-finally statement. Even though we are using Logic Apps Consumption for this proof of concept, the same principles will be applied to Logic Apps Standard.
Hope you find this helpful! So, if you liked the content or found it useful and want to help me write more content, you can buy (or help buy) my son a Star Wars Lego!
Big thanks to my team member Luís Rigueira for creating this video.
Author: Sandro Pereira
Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc.
He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.
View all posts by Sandro Pereira
I have often been writing about how to handle exceptions and get the correct and detailed error message describing in an easy and clean matter the failures in our processes:
And the reason why that happens is that we need to accept the fact that at a certain point in time, our process will fail. Or by the fact we did some updates that weren’t tested properly, an external system is down or in intervention, or by issues in Microsoft infrastructure, and so many other reasons. But when that happens, one thing is clear: we want to get the exact error message describing why it failed. But, of course, we can always go to the Logic Apps run history – except if it is a Stateless workflow on Standard – to check where and why it failed. And obviously, we will have all the information necessary to solve the problem. However, most of the time, if not always, we want to log the error in some place like Application Insights, SQL Database, Event Hubs, and so on in an automated way without human intervention. To accomplish that, we need to access this error message in runtime.
By default, Logic App allows handling errors using the Configure run after settings at a per action level. For more complex scenarios, it can be done by setting up Scope action and implementing try-catch/try-catch-finally statements. However, getting a detailed error message can be a bit challenging inside Logic App, and you will find different approaches to implement the same capabilities, each of them with advantages and disadvantages.
In this whitepaper, we will be addressing some of the possible ways to address these needs but also addressing some of the following questions:
How can I implement error handling?
This is an important question and topic because, without proper error handling, we will not be able to send in an automated way access the error message instead, our workflow will finish in that action with a failure state.
What out-of-the-box features do I have to capture and access the error details?
How can I improve these default features in a no-code, low-code way and in a code-first approach?
Where can I download it
You can download the whitepaper here:
Author: Sandro Pereira
Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc.
He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.
View all posts by Sandro Pereira
A few months ago, I wrote two blog posts about How to get the Error Message with Logic App Try-Catch, which you can find it here:
Of course, in this series of posts, we are addressing Logic App Consumption. We can actually implement the same strategy for Logic App Standard, but the APIs will be different – this is something that I will write about in the future.
Nevertheless, when I published the second part of this series, I mentioned that we could actually use a no code-low code approach using a Logic App to perform the same operation we were doing by code inside an Azure Function. And that got curiosity by some of my readers to question me if I was doing a thirty-part addressing that scenario. Well, it took some time, but here it is!
What we pretend to do here is to create a generic Logic App Consumption that can dynamically catch the actual error message and action inside a run from another Logic App. This means that we will discard generical errors like “An action failed. No dependent actions succeeded.” which don’t tell us anything about what really happened during the workflow, only that a subsequent child action failed, and dig deeper to find the real error behind that.
The Logic App will receive the same inputs of the Azure Function that we described in the previous post:
Subscription Id;
Resource name;
Logic App name;
Run id;
But in this case, in a JSON format using the Request > When a HTTP Request is received trigger.
Of course, to do this, after we add the trigger, we need to click on Use sample payload to generate schema.
And paste the above JSON sample for the editor to generate the JSON schema
The next step we are going to do is to invoke the Azure Logic App REST API in order to get the run history:
GET https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Logic/workflows/{workflowName}/runs/{runName}/actions?api-version=2016-06-01
We are going to do that by creating a new HTTP action.
Of course, we need to:
Specify the Method parameter to be GET.
On the URL parameter, copy the URL described above and replace:
{subscriptionId} by the token subscriptionId present in the trigger
{resourceGroupName} by the token resourceGroup present in the trigger
{workflowName} by the token workflowName present in the trigger
{runName} by the token runId present in the trigger
On the Headers parameter, add the following header:
Content-Type: application/json
On the Authentication type, we will use Managed identity to dynamically generate the OAuth token necessary to invoke the Azure Logic App REST API.
Managed identities in Azure Logic Apps are an authentication mechanism that allows the workflow to access other services without having the user define the credentials for those services inside the workflow actions.
Of course, to use it, we need to go to our Logic App resource and enable managed identity by:
On your LogicApp, from the left menu, go to the Identity option present in the Settings group, and once there, on the Status property, click on On, this will generate an Object (principal) ID.
Later on, we will be setting the correct permissions.
Getting back to our Logic App, now that we have an action to invoke the Azure Logic App REST API to get the run history, we are going to use a Filter Array action to filter the action with the status equal to Failed like we describe in the first blog on this series:
Select Add an action.
In the search box, enter Filter array, and from the result panel, select the Data Operations, Filter array action
And provide the following information:
on the From property, place the following expression:
Note: that ‘Call_Logic_App_Rest_API_To_Get_Run_History‘ is the name of the previous HTTP action, so you need to adjust this value to your scenario
on the condition property, on the left textbox, place the following expression:
item()?[‘properties’]?[‘status’]
Note: this is always equal
Leave the operator as is equal to, and on the right textbox, place the following value:
So, in this last action, we will create an array object for all actions that contain the property status equal to Failed. However, that can also have the generic error that we need to discard to archive that we are going to create a For each action to travel that array and find the correct error:
As I mentioned in the last post, depending on the scenario and actions you are using, the error information may be in different places and structures of the JSON in the run history:
Usually, we have inside the properties a structure call error that has the error message inside the message property: action[“properties”][“error”][“message”]
But sometimes, this error structure doesn’t exist – the HTTP action is a good example of this – and we need to get uri information on the outputsLink structure: action[“properties”][“outputsLink”][“uri”], to invoke that URL to get to correct error message.
These are different behaviors that we need to handle inside our workflow. Therefore, we will add another Action, this time a Condition with the following configuration:
Items(‘For_each_Filter_Array_Run_History’)[‘properties’]?[‘error’] is equal to null
As you see in the picture below.
What does this mean? It means that if inside the properties of the JSON, the object error is null (does not exist):
Then the Logic App goes through the true side of the condition, and there we need to implement the logic to get and invoke the URI to get the error details
Otherwise, If it is false and indeed the properties-error contains an object error, then it goes through the False side of the condition, meaning we can grab the error message from there.
True branch
Let’s focus on the True side of the condition. As we mentioned above, the details of the error information will not be present directly in the run history. Instead, we will have something like this:
The status is Failed, but there are no error message details to present in this situation, so the error must be somewhere else, and indeed it is. The error, in this case, is present as a URL, in the object uri, so if we follow this link and paste it into a browser, this is what we receive in our sample:
{"error":{"code":"AuthorizationFailed","message":"The authentication credentials are not valid."}}
That means for us to get the correct error message, we have to get that link and perform another HTTP call, and this is exactly what we are going to do with the next action:
So, on the true branch of the condition, add a new HTTP action with the following configuration:
Set the Method property as GET.
Set the URI property to be dynamic using the output of the filter array:
But unfortunately, even the response that comes from this URI with the error detail can appear in different ways. We have already found two scenarios:
The error can appear in this time of structure:
{
"statusCode": 404,
"headers": {
"Pragma": "no-cache",
"x-ms-failure-cause": "gateway",
"x-ms-request-id": "XXXXXXX",
"x-ms-correlation-request-id": "XXXXXX",
"x-ms-routing-request-id": "XXXXXX",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"X-Content-Type-Options": "nosniff",
"Cache-Control": "no-cache",
"Date": "Fri, 03 Feb 2023 12:19:12 GMT",
"Content-Length": "302",
"Content-Type": "application/json; charset=utf-8",
"Expires": "-1"
},
"body": {
"error": {
"code": "InvalidResourceType",
"message": "The resource type 'workflows' could not be found in the namespace 'Microsoft.Logic' for api version '2016-06-01''. The supported api-versions are '2015-02-01-preview,2015-08-01-preview,2016-06-01,2016-10-01,2017-07-01,2018-07-01-preview,2019-05-01'."
}
}
}
And sometimes like this:
[
{
"name": "Condition",
"startTime": "2023-02-06T10:21:38.4195084Z",
"endTime": "2023-02-06T10:21:38.4195084Z",
"trackingId": "fd8b62ec-4745-4e85-84b4-da57b8e8b8c2",
"clientTrackingId": "08585259279877955762218280603CU192",
"code": "BadRequest",
"status": "Failed",
"error": {
"code": "InvalidTemplate",
"message": "Unable to process template language expressions for action 'Condition' at line '0' and column '0': 'The template language function 'startsWith' expects its first parameter to be of type string. The provided value is of type 'Null'. Please see https://aka.ms/logicexpressions#startswith for usage details.'."
}
}
]
For that reason, and in order to try to provide the best detail possible, we decide to create a variable to set up the type of error we are dealing with:
First, we add a Parse JSON action to parse the response of the previous HTTP Call using the following schema:
Now, let’s focus on the False side of the condition. As we mentioned previously, in this scenario, we must carefully discard the following generic messages that can appear: An action failed. No dependent actions succeeded.
To do that, we will be adding a Condition with the following configuration:
items(‘For_each_Filter_Array_Run_History’)?[‘properties’]?[‘error’]?[‘message’] is not equal to An action failed. No dependent actions succeeded.
If the error is the generic one, it will ignore it, and it will go to the next error in the error history. Note: if we have this generic error message, we will always have two Failed actions – we will always want the other detail because it is there that we will have the real error detailed message.
If the error is not the generic one, then we are going to define the output message in our support variable using the following expression:
To finalize, we just need to add the Response action with the following configuration:
Set the Status Code as 200
Add the following header in the Headers properties:
Content-Type: application/json
Set the Body property with that value of our output message variable that we used previously
In the end, our Logic App will look like this:
To finalize and for this to work properly, we need to configure the Logic App managed identity and permissions.
Configure the Logic App managed identity and permissions
We already described in the beginning that we need to enable the managed identity, but in order for this Logic App to be able to extract the error information from other Logic App run histories, we need to give to that managed identity the Logic App Operator role in each resource group that contains the Logic App from which we want to access and read the run history.
For this last step:
On the Azure Portal, access the Resource Group that has our Logic App from where we want to grab the correct error message.
On the Resource Group page, select the option Access control (IAM).
On the Access control (IAM) panel, click Add > Add role assignment
On the Add role assignment page, on the Role tab, search for Logic App and then select the option Logic App Operator, and then click Next.
On the Members tab, select the Assign access to be Managed identity, and from the Members:
Select your subscription on the subscription property.
On the Managed identity list of options, select our Logic App Catch Error
and on the Select property, select the managed identity of our function and then click Close.
Click on Review + Assign and then Review + Assign again.
We can now use this Generic Logic App to read the error detail inside our other Logic Apps.
Where can I download it
You can download the complete Azure Function source code here:
Credits
Kudu to my team member Luis Rigueira for participating in this proof-of-concept!
Hope you find this useful! So, if you liked the content or found it useful and want to help me write more content, you can buy (or help buy) my son a Star Wars Lego!
Author: Sandro Pereira
Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc.
He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.
View all posts by Sandro Pereira
Error handling refers to anticipation, detection, and resolution of programming, application, and communication errors. Specialized programs, called error handlers, are available for some applications. The best programs of this type forestall errors if possible, recover from them when they occur without terminating the application, or (if all else fails) gracefully terminate an affected application and save the error information to a log file.
Programming error is the one that can be prevented mostly. Such an error can occur in syntax or logic. Syntax errors, which are a typographical mistake or improper use of special character, are handled by rigorous proofreading. Logic errors, also called bugs, occur when executed code does not produce the expected or desired result. Logic errors are best handled by meticulous program debugging. This can be an ongoing process that involves, in addition to the traditional debugging routine, beta testing prior to official release and customer feedback after release.
Error Handling in Web API
ASP.Net Web API is a lightweight framework from Microsoft that can be used for building stateless RESTful services that run on HTTP. Exceptions are errors that occur at runtime, and exception handling is the technique of handling runtime errors in your application code. You should have a good idea of how you can handle exception in Web API and send out appropriate error codes and error messages from your Web API controller methods.
Creating a Web API project:
Start Visual Studio ->File Menu -> Select New Project.
Select Installed Templates -> expand the Visual C# . Under Visual C# select Web. In the list of project templates, selectNET Web Application. Name the project “Errorhandlingpractise1” and click OK.
In the new ASP.NET Project -> select the Web API Click OK.
Add folder and code folder for to select Web API check box
click theView menu -> select SolutionExplorer. In Solution Explorer -> right-click the Models folder. From the context menu, select Add then select Class.
MODEL CLASS:
Product class:
Create a Product class and add the properties, then inherit the interface.
Check the status code and response.
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace Errorhandlingpractise1.Models
{
public class Product : IHttpActionResult
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public string ErrorMessage { get; set; }
public HttpStatusCode StatusCode { get; set; }
public Product Response { get; set; }
public Product()
{
}
public Product(HttpStatusCode statusCode, Product response)
{
this.StatusCode = statusCode;
this.Response = response;
}
public HttpResponseMessage CreateResponse(HttpStatusCode statusCode, object
response)
{
HttpRequestMessage request = new HttpRequestMessage();
request.Properties.Add(System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey,
new HttpConfiguration());
HttpResponseMessage httpResponse = request.CreateResponse(statusCode, response);
return httpResponse;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
if (!string.IsNullOrEmpty(this.ErrorMessage))
return Task.FromResult(CreateResponse(this.StatusCode, this.ErrorMessage));
return Task.FromResult(CreateResponse(this.StatusCode, this.Response));
}
}
}
ADD CONTROLLER:
In Solution Explorer, right-click the Controllers folder. Select Add and then select Controller.
Note: I have used ASP.NET MVC, ASP is already Familiar with controllers. Web API controllers are similar to MVC controllers, but inherit the API Controller class instead of the controller class.
Select Web API 2 Controller – Empty. Click Add.
Add the controller -> Name “Product Controller” and then click Add
Double click the Product controller and open the file.
DESCRIPTION:
Products are stored in a fixed array in the controller class. The controller defines two methods that return products:
The GetAllProductsmethod returns the entire list of products as an IEnumerable<Product>
The GetProductmethod looks up a single product by its Id.
The GetProduct method Check whether the Id is null or not. If the Id is null, throw the HttpResponseException and return the Id is not found the Httpstatuscode is 404.
Whether the condition is not null return the product and the Httpstatuscode is 200.
PRODUCT CONTROLLER:
using Errorhandlingpractise1.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace Errorhandlingpractise1.Controllers
{
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Toys", Category = "Teady bear", Price = 100 },
new Product { Id = 2, Name = "Watch", Category = "Fastrack", Price = 35.75M },
new Product { Id = 3, Name = "Laptop", Category = "Dell", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public Product GetProduct(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
try
{
if (product == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No product with ID = {0}", id)),
ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);
}
return new Product(HttpStatusCode.OK, product);
}
catch (Exception ex)
{
var response = new Product(HttpStatusCode.NotFound, product) { ErrorMessage = "Product ID Not Found" };
return response;
}
}
}
}
Running the application:
What is Postman and how do I use it with Azure?
Postman is a REST Client that runs as an application inside the Chrome browser. It is very useful for interfacing with REST APIs such as those found in Azure. In the Chrome browser navigate to https://chrome.google.com/webstore then search for Postman and it will show as offered by www.getpostman.com. Click the Add to Chrome button to install the application into your browser. Click Add in the dialog displayed to confirm installation.
The Chrome App Launcher will be added to your Desktop and Taskbar. Select the App Launcher and click the Postman icon which will launch the application. You can then type in the various REST calls you wish to make using all the various REST verbs such as GET, POST, PUT etc.
If you wish to use this with Azure you need two pieces of information. Your Subscription ID and also a JWT (JSON Web Token which is an authorization token). The Subscription ID can be found by viewing the portal and browsing Subscriptions or using Get-Azure Subscription. To get the JWT you will need to run the PowerShell code below, making sure to change the adTenant to your tenant. This code is part of code I previously covered in FAQ http://windowsitpro.com/azure/communicate-azure-rest-powershell.
Conclusion:
ASP.Net Web API is a lightweight framework from Microsoft that can be used for building stateless RESTful services that run on HTTP, ASP.NET Web API supports fully functional exception handling to handle runtime errors,
We can use HttpResponseException when the possibility of exception is known by us. In the above example, we have thrown exception using HttpResponseException class as we know there is a chance to employee not found in the database, in this case it makes sense to return a HttpNotFound 404 status instead of the regular 200 code. Exceptions are errors that occur at runtime, and exception handling is the technique of handling runtime errors in your application code. We can handle the exception in Web API and send out appropriate error codes and error messages from your Web API controller methods.