Running inline C# scripts in Azure Logic Apps workflows

Last week I wrote a post explaining the new Inline Powershell action available in Azure Logic Apps standard. With this action you can now execute Powershell scripts in your low-code workflow (receiving inputs and outputs from other actions).

But the Azure Logic Apps “Inline code” action’s family has another child: the Inline C# Action:

With this action, you can  write .NET C# script right within the Logic Apps designer in Azure Portal and then execute it inside your Azure Logic Apps workflow.

How to use the Inline C# Script Code action

Before describing this new action (currently listed as preview) I need to add a preface. In some conferences (Directions EMEA 2023, DynamicsMinds 2024, Global Azure and others) I’ve presented how to write high-performance workflows with Azure Logic Apps Standard by using a new feature that permits you (from Visual Studio Code embedded designer) to add .NET code-based actions inside a low-code workflow. This new feature is handled by the action Call a local function in this logic app. This is an extremely powerful feature tailored to a more advanced developer experience, out of scope of this post:

The new Inline C# execution action (called Execute CSharp Script Code) is instead tailored to the Azure Portal experience. It gives you a portal-only support for C# Script Files (.csx file) and is aimed at helping users solve management tasks, automate workflows, and handle other C# scenarios directly in Azure Portal without having to configure a local development environment. 

When you add this action to an Azure Logic Apps workflow, a coee editor appears and here is where you can write your C# code:

Intellisense support is limited, so I suggest to first prepare a script offline and then paste it into the editor.

The script code is saved as a .csx file in the same folder as your workflow.json file and deployed to your application along with the workflow definition. As part of Logic App initialization, this code file will be compiled and be ready for execution:

In a .csx file you need to just define a Run method, adding any assembly references and namespaces at the beginning of the file. The name of this method is predefined, and your workflow can run only invoke this Run method at runtime. You can reference external assemblies by using the #r “AssemblyName” directive.

The C# code runs within the Azure Function host process. You have access to all the standard .NET assemblies. Custom assemblies are not supported at the time of writing this post but they will come soon (before general availability of the action is the official plan).

For accessing data from your workflow trigger, you can use GetTriggerResults method. This will return an object representing the trigger and its outputs is available in the Outputs property. It is an object of type JObject and you can use [] indexer to lookup for various properties in the trigger outputs. For example, to read the body of the trigger output you can use the following C# code:

var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs; 
var body = triggerOutputs["body"]; 

For accessing data from an action, you can use the GetActionResults method. Like triggers, this will return an object representing the action and its outputs is available in the Outputs property:

var actionOutputs = (await context.GetActionResults("actionName").ConfigureAwait(false)).Outputs; 
var body = actionOutputs["body"]; 

To give you an example, here is a workflow where I’m receiving as input an HTTP POST request with a JSON body containing a base64 representation of a ZIP file. In the ZIP file, there are N data files containing customer’s data (customer code in my sample). The trigger’s JSON body is the following:

{
    "zipContent": "test"
}

In my Azure Logic Apps workflow, I need to:

  • Retrieve the base64 representation of the ZIP file
  • Uncompress the ZIP file
  • For each text file in the zip file, retrieve its string content (Customer code)
  • The ZIP data files processing needs to return a list of string (list of Customer codes)
  • For each Customer code, I need to execute an action in Dynamics 365 Business Central

My workflow is defined as follows:

The ZIP file processing is handled by the Execute CSharp Script Code action with the following C# code:

#r "Newtonsoft.Json"
#r "Microsoft.Azure.Workflows.Scripting"
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Workflows.Scripting;
using Newtonsoft.Json.Linq;

public static async Task<List<string>> Run(WorkflowContext context, ILogger log)
{
  var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs;

  ////Retrieves zipContent parameter from trigger payload.
  var base64ZipContent = triggerOutputs?["body"]?["zipContent"]?.ToString();

  //Unzipping a ZIP file multiple files, each file containing a customer number
  // Decode base64 to bytes 
  byte[] zipBytes = Convert.FromBase64String(base64ZipContent); 
  List<string> customerList = new List<string>(); 
  
  // Create an in-memory stream from the zip bytes 
  using (MemoryStream zipStream = new MemoryStream(zipBytes)) 
  { 
      // Extract files from the zip archive 
      using (ZipArchive zipArchive = new ZipArchive(zipStream)) 
      { 
          foreach (ZipArchiveEntry entry in zipArchive.Entries) 
          { 
              // Read each file's content 
              using (StreamReader reader = new StreamReader(entry.Open())) 
              { 
                  string fileContent = reader.ReadToEnd(); 
                  customerList.Add(fileContent); 
              } 
          } 
      } 
  } 
  return customerList; 
}

The action reads the zipContent parameter from the trigger’s body, converts the base64 stream to bytes, creates an in-memory stream for the zip file bytes and extract each file in the ZIP archive. All these steps are handled by the C# script itself (performance!). Then the action returns an array of strings (Customer codes).

The for each output of the Execute CSharp Script Code action (array of strings), I need to create Customer records in Dynamics 365 Business Central. For that scope, you can retrieve the Execute CSharp Script Code actions’s ouput in the For Each block by using the following formula: @outputs(‘NAME_OF_YOUR_ACTION’)[‘body’]

Now you can iterate through the strings array elements and process the records in Dynamics 365 Business Central accordingly:

Please remember that at the moment:

  • the C# script can run for up to 10 minutes.
  • the output size of the Execute CSharp Script Code action can be maximum 100MB

Why is this action important?

If you’re a professional serverless workflows developer and you’ve created complex workflows in real production environments, you probably already have experienced that often the term “low-code” and the term “performance” are not so much in love (and I also wrote about a real-world case here, live-demoed during recent conferences too).

Quite often, when you have a complex low-code workflow and you need to do complex operations in a very quick way, using code is the best way to do. Injecting code in some workflow actions gives you the possibility to gain in performance and also to perform things that maybe a low-code workflo alone cannot do.

If you want to obtain results like what I’ve demoed in the past (see the following chart), mixing code and low-code is the way to go…

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.