Announcement: Introducing .NET C# Inline Action for Azure Logic Apps (Standard) - Preview
Published Jun 07 2024 04:21 PM 7,607 Views
Microsoft

We are excited to introduce a new capability that allows developers to write .NET C# script right within the Logic Apps designer in Azure Portal. This complements the custom code feature in VS Code that we introduced previously for invoking .NET FX and NET8 (planned) functions written and deployed to a Logic App.   

 

Usage guidelines

As you try this feature please keep the following in mind:

  • Script execution and assemblies: Runs within the Azure Function host process; custom assemblies not supported (custom code capability available). Access to all framework assemblies, including Newtonsoft.Json. Custom assembly support coming before general availability.
  • Portal-only support for C# Script Files (.csx This feature is not yet available in VS Code 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. 

Adding C# script in your workflow 

 

You will see a new action called “Execute CSharp Script Code” under “Inline Code” in the list of actions available for you. 

 

 

harimehta_0-1717592475468.png

 

Upon selecting this action, a code editor will pop-up allowing you to write your code. The code editor will start with “boilerplate” code to help guide you in writing your first CSharp script in Logic App. 

 

 

harimehta_1-1717592475469.png

 

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. 

 

harimehta_2-1717592475471.png

How does the scripting work? 

 

The .csx format allows you to write less "boilerplate" and focus on writing just a C# function. Instead of wrapping everything in a namespace and class, just define a Run method. Include any assembly references and namespaces at the beginning of the file as usual. The name of this method is predefined, and your workflow can run only invoke this Run method at runtime. 

 

Data from your workflow flows into your Run method through parameter of WorkflowContext type. In addition to the workflow context, you can also have this method take function logger as a parameter and a cancellation tokens (needed if your script is long running and needs to be gracefully terminate in case of Function Host is shutting down). 

 

 

 

 

 

 

 

 

 

 

 

// Add the required libraries 
#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<Results> Run(WorkflowContext context, ILogger log) 
{ 
  var triggerOutputs = (await context.GetTriggerResults().ConfigureAwait(false)).Outputs; 
  var name = triggerOutputs?["body"]?["name"]?.ToString(); 
  return new Results 
  { 
    Message = !string.IsNullOrEmpty(name) ? $"Hello {name} from CSharp action" : "Hello from CSharp action." 
  }; 
} 
public class Results 
{ 
  public string Message {get; set;} 
} 

 

 

 

 

 

 

 

 

 

 

 

The #r statement is explained here. And class definition for WorkflowContext provided here. Learn more on C# scripting for portal at aka.ms/csharp-scripting.

 

Flowing data into the script from the workflow

 

The WorkflowContext has two method that you can use to access the data from your workflow. 

 

For accessing data from your 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, the below code retrieves the data from trigger outputs body property 

 

 

 

 

 

 

 

 

 

 

 

public static async Task<Results> Run(WorkflowContext context, ILogger log) 
{ 
  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. This method will take action name as parameter as shown below. 

 

 

 

 

 

 

 

 

 

 

 

public static async Task<Results> Run(WorkflowContext context, ILogger log) 
{ 
  var actionOutputs = (await context.GetActionResults("actionName").ConfigureAwait(false)).Outputs; 
  var body = actionOutputs["body"]; 
} 

 

 

 

 

 

 

 

 

 

 

 

Returning data back to your workflow

 

Your run method can have a return type and it can also be a Task<> if you want the method to be async, the return value then will be set as the outputs body of the script action that any subsequent actions can reference. 

 

harimehta_3-1717592475471.png

 

Limits 

 

Duration 

Your script can run for up to 10mins. Let us know if you have scenarios that require longer durations  

Outputs 

Output size is subjected to the outputs size limit of the actions (100MB). 

 

Logging 

 

To log output to your streaming logs in C#, include an argument of type ILogger. We recommend that you name it log. Avoid using Console. Write in in your script. 

 

 

 

 

 

 

 

 

 

 

public static void Run(WorkflowContext context, ILogger log) 
{ 
    log.LogInformation($"C# script has executed successfully"); 
} 

 

 

 

 

 

 

 

 

 

 

 

Custom metrics logging 

 

You can use the LogMetric extension method on ILogger to create custom metrics in Application Insights. Here's a sample method call: 

 

 

 

 

 

 

 

 

 

 

logger.LogMetric("TestMetric", 1234); 

 

 

 

 

 

 

 

 

 

 

 

Importing namespaces 

 

If you need to import namespaces, you can do so as usual, with the using clause. 

 

The following namespaces are automatically imported and are therefore optional: 

  • System 
  • System.Collections.Generic 
  • System.IO 
  • System.Linq 
  • System.Net.Http 
  • System.Threading.Tasks 
  • Microsoft.Azure.WebJobs 
  • Microsoft.Azure.WebJobs.Host 

 

Referencing external assemblies 

 

For framework assemblies, add references by using the #r "AssemblyName" directive. 

 

 

 

 

 

 

 

 

 

 

// Add the required libraries 
#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<Results> Run(WorkflowContext context, ILogger log) 

 

 

 

 

 

 

 

 

 

 

 

The following assemblies are automatically added by the Azure Functions hosting environment: 

  • mscorlib 
  • System 
  • System.Core 
  • System.Xml 
  • System.Net.Http 
  • Microsoft.Azure.WebJobs 
  • Microsoft.Azure.WebJobs.Host 
  • Microsoft.Azure.WebJobs.Extensions 
  • System.Web.Http 
  • System.Net.Http.Formatting 
  • Newtonsoft.Json 

 

Environment variables 

 

To get an environment variable or an app setting value, use System.Environment.GetEnvironmentVariable, as shown in the following code example: 

 

 

 

 

 

 

 

 

 

 

 

public static void Run(WorkflowContext context, ILogger log) 
{ 
    log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); 
    log.LogInformation(GetEnvironmentVariable("AzureWebJobsStorage")); 
    log.LogInformation(GetEnvironmentVariable("WEBSITE_SITE_NAME")); 
} 

public static string GetEnvironmentVariable(string name) 
{ 
    return name + ": " + System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process); 
} 

 

 

 

 

 

 

 

 

 

 

 

 

Compilation Errors 

 

The web-based editor has limited IntelliSense support at this time, and we are working on improving as we make this capability generally available. Any compilation error will hence be detected at save time when the logic app runtime compiles the script. These errors will appear in the error-logs of your logic app.

 

harimehta_4-1717592475472.png

 

Runtime Errors 

 

Any error that happens at execution time in the script will propagate back to the workflow and the script action will be marked as failed with the error object representing the exception that was thrown from your script. 

 

harimehta_5-1717592475473.png

 

Example Scripts 

 
Uncompressing a ZIP file containing multiple text files retrieved from an HTTP action into an array of strings 

 

 

 

 

 

 

 

 

 

 

// Add the required libraries 
#r "Newtonsoft.Json" 
#r "Microsoft.Azure.Workflows.Scripting" 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.Extensions.Primitives; 
using Microsoft.Azure.Workflows.Scripting; 
using System; 
using System.IO; 
using System.IO.Compression; 
using System.Text; 
using System.Collections.Generic; 

/// <summary> 
/// Executes the inline csharp code. 
/// </summary> 
/// <param name="context">The workflow context.</param> 
public static async Task<List<string>> Run(WorkflowContext context) 
{ 
  var outputs = (await context.GetActionResults("HTTP_1").ConfigureAwait(false)).Outputs; 
  var base64zipFileContent = outputs["body"]["$content"].ToString(); 

  // Decode base64 to bytes 
  byte[] zipBytes = Convert.FromBase64String(base64zipFileContent); 
  List<string> fileContents = 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(); 
                  fileContents.Add(fileContent); 
              } 
          } 
      } 
  } 
  return fileContents; 
} 

 

 

 

 

 

 

 

 

 

 

 

Encrypt Data using a key from App-Settings 

 

 

 

 

 

 

 

 

 

 

// Add the required libraries 

#r "Newtonsoft.Json" 
#r "Microsoft.Azure.Workflows.Scripting" 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.Extensions.Primitives; 
using Microsoft.Azure.Workflows.Scripting; 
using Newtonsoft.Json.Linq; 
using System; 
using System.IO; 
using System.Security.Cryptography; 
using System.Text; 

/// <summary> 
/// Executes the inline csharp code. 
/// </summary> 
/// <param name="context">The workflow context.</param> 
public static async Task<string> Run(WorkflowContext context) 
{ 
  var compose = (await context.GetActionResults("compose").ConfigureAwait(false)).Outputs; 
  var text = compose["sampleData"].ToString(); 
  return EncryptString(text); 
} 

public static string EncryptString(string plainText) 
{ 
      var key = Environment.GetEnvironmentVariable("app-setting-key"); 
      var iv = Environment.GetEnvironmentVariable("app-setting-iv");  
      using (Aes aesAlg = Aes.Create()) 
      { 
          aesAlg.Key = Encoding.UTF8.GetBytes(key); 
          aesAlg.IV = Encoding.UTF8.GetBytes(iv); 
          ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 
          using (MemoryStream msEncrypt = new MemoryStream()) 
          { 
              using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
              { 
                  using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
                  { 
                      swEncrypt.Write(plainText); 
                  } 
              } 
              return Convert.ToBase64String(msEncrypt.ToArray()); 
          } 
      } 
} 

 

 

 

This feature is only in preview for Logic App (Standard) SKU and not Logic App (Consumption). We appreciate your feedback, please provide any comments and suggestions about this action using this form: https://aka.ms/csharplogicapps.

 

Appendix

 

WorkflowContext Class 

Represents the context of a workflow. 

 

Methods 

  • Task<WorkflowOperationResult> GetActionResult(string actionName) 
    Gets the result of a specific action within the workflow. 

Parameters 

  • actionName 
    The name of the action. 

Returns 

A Task representing the asynchronous operation. The task result contains a WorkflowOperationResult object. 

  • Task<WorkflowOperationResult> RunTriggerResult() 
    Gets the result of the workflow trigger. 

Returns 

A Task representing the asynchronous operation. The task result contains a WorkflowOperationResult object with the following properties: 

 

WorkflowOperationResult Class 

Represents the result of a workflow operation. 

 

Properties 

  • string Name 
    Gets or sets the operation name. 
  • JToken Inputs 
    Gets or sets the operation execution inputs. 
  • JToken Outputs 
    Gets or sets the operation execution outputs. 
  • DateTime? StartTime 
    Gets or sets the operation start time. 
  • DateTime? EndTime 
    Gets or sets the operation end time. 
  • string OperationTrackingId 
    Gets or sets the operation tracking id. 
  • string Code 
    Gets or sets the status code of the action. 
  • string Status 
    Gets or sets the status of the action. 
  • JToken Error  
    Gets or sets the error of the action 
  • JToken TrackedProperties  
    Gets or sets the tracked properties of the action 
11 Comments
Co-Authors
Version history
Last update:
‎Jun 10 2024 12:54 PM
Updated by: